From 1334ebd3cc978db933f5ba6672f07c41844eef85 Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Sun, 16 Mar 2025 17:18:46 +0900 Subject: [PATCH 001/393] add: Add issue templates --- ...4\354\212\210-\354\235\264\353\246\204.md" | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 ".github/ISSUE_TEMPLATE/\354\235\264\354\212\210-\354\235\264\353\246\204.md" diff --git "a/.github/ISSUE_TEMPLATE/\354\235\264\354\212\210-\354\235\264\353\246\204.md" "b/.github/ISSUE_TEMPLATE/\354\235\264\354\212\210-\354\235\264\353\246\204.md" new file mode 100644 index 00000000..b685dd47 --- /dev/null +++ "b/.github/ISSUE_TEMPLATE/\354\235\264\354\212\210-\354\235\264\353\246\204.md" @@ -0,0 +1,20 @@ +--- +name: 이슈 이름 +about: 팝풀 기본 템플릿 +title: '' +labels: '' +assignees: '' + +--- + +## 🤔 작업 배경 + +작업 배경을 적어주세요 + +## 📝 작업 내용 + +- 작업 내용을 적어주세요 + +## 👀 ETC (추후 개발해야 할 것, 참고자료 등) + + From 84e71dbca296f95f6cb419410afd8a402fbe4b24 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 16 Mar 2025 21:33:56 +0900 Subject: [PATCH 002/393] =?UTF-8?q?[ADD]:=20PR=20=ED=85=9C=ED=94=8C?= =?UTF-8?q?=EB=A6=BF=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/pull_request_template.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..321522df --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,15 @@ +## 📌 이슈 + +- #이슈번호 + +## ✅ 작업 사항 + +- [ ] 작업 사항을 정리해주세요 + +## 🚀 테스트 방식 + + + +## 👀 ETC (추후 개발해야 할 것, 참고자료 등) -> + + \ No newline at end of file From 3fea2e1d42e4be645df9f1864c2df7d3a00d61a7 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 17 Mar 2025 21:48:47 +0900 Subject: [PATCH 003/393] =?UTF-8?q?[REFACTOR]:=20RxKakao=20SDK=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C,=20Kakao=20User=20SDK=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 | 52 ++--- .../xcshareddata/swiftpm/Package.resolved | 33 +--- .../xcshareddata/xcschemes/Poppool.xcscheme | 2 +- Poppool/Poppool/Application/AppDelegate.swift | 4 +- .../Poppool/Application/SceneDelegate.swift | 92 +-------- .../Infrastructure/KakaoLoginService.swift | 177 +++++++++--------- 6 files changed, 116 insertions(+), 244 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index a38282f7..d05337a5 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -343,9 +343,6 @@ 08B191B42CF609260057BC04 /* KakaoLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B32CF609260057BC04 /* KakaoLoginService.swift */; }; 08B191B62CF6092B0057BC04 /* AppleLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */; }; 08B191B82CF6092F0057BC04 /* AuthServiceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */; }; - 08B191BA2CF609AE0057BC04 /* RxKakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 08B191B92CF609AE0057BC04 /* RxKakaoSDK */; }; - 08B191BC2CF609AE0057BC04 /* RxKakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 08B191BB2CF609AE0057BC04 /* RxKakaoSDKAuth */; }; - 08B191BE2CF609AE0057BC04 /* RxKakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 08B191BD2CF609AE0057BC04 /* RxKakaoSDKUser */; }; 08B191C22CF615CA0057BC04 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191C12CF615CA0057BC04 /* Secrets.swift */; }; 08CBEA032D38989E00248007 /* PostTokenReissueResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */; }; 08CBEA062D38991600248007 /* PostTokenReissueResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */; }; @@ -374,6 +371,7 @@ 08DE8A1D2D5261E70049BCAC /* MyPageTermsReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A1C2D5261E70049BCAC /* MyPageTermsReactor.swift */; }; 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A3E2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift */; }; 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */; }; + 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 08F403322D884F4D00BFA61A /* KakaoSDKUser */; }; 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */; }; 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */; }; @@ -975,15 +973,13 @@ BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, - 08B191BA2CF609AE0057BC04 /* RxKakaoSDK in Frameworks */, - 08B191BC2CF609AE0057BC04 /* RxKakaoSDKAuth in Frameworks */, 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, 088DE2472D12DB5C0030FA9E /* GoogleMaps in Frameworks */, + 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */, BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, - 08B191BE2CF609AE0057BC04 /* RxKakaoSDKUser in Frameworks */, 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, @@ -3085,14 +3081,12 @@ BDCA420C2CF35FD2005EECF6 /* RxGesture */, BDCA420F2CF35FF5005EECF6 /* Lottie */, 083A25CF2CF364B70099B58E /* Alamofire */, - 08B191B92CF609AE0057BC04 /* RxKakaoSDK */, - 08B191BB2CF609AE0057BC04 /* RxKakaoSDKAuth */, - 08B191BD2CF609AE0057BC04 /* RxKakaoSDKUser */, 088DE2432D104EE70030FA9E /* SwiftSoup */, 088DE2462D12DB5C0030FA9E /* GoogleMaps */, 4E5825662D1951DF00EE83EF /* FloatingPanel */, 4EA9989C2D21C404009DC30B /* RxDataSources */, 082197A02D426DCB0054094A /* Then */, + 08F403322D884F4D00BFA61A /* KakaoSDKUser */, ); productName = Poppool; productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; @@ -3142,7 +3136,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1540; - LastUpgradeCheck = 1540; + LastUpgradeCheck = 1620; TargetAttributes = { BDCA41BC2CF35AC0005EECF6 = { CreatedOnToolsVersion = 15.4; @@ -3170,7 +3164,6 @@ BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */, BDCA41F32CF35D33005EECF6 /* XCRemoteSwiftPackageReference "Kingfisher" */, BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */, - BDCA41F92CF35EC0005EECF6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk-rx" */, BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */, BDCA41FF2CF35EFE005EECF6 /* XCRemoteSwiftPackageReference "RxKeyboard" */, BDCA42022CF35F76005EECF6 /* XCRemoteSwiftPackageReference "PanModal" */, @@ -3184,6 +3177,7 @@ 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */, 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */, 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */, + 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, ); productRefGroup = BDCA41BE2CF35AC0005EECF6 /* Products */; projectDirPath = ""; @@ -3934,7 +3928,6 @@ BDCA41EB2CF35AC2005EECF6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -3954,7 +3947,6 @@ BDCA41EC2CF35AC2005EECF6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -3974,7 +3966,6 @@ BDCA41EE2CF35AC2005EECF6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 2U86LHQK8Q; @@ -3992,7 +3983,6 @@ BDCA41EF2CF35AC2005EECF6 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = 2U86LHQK8Q; @@ -4081,6 +4071,14 @@ minimumVersion = 9.2.0; }; }; + 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.24.0; + }; + }; 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/scenee/FloatingPanel.git"; @@ -4121,14 +4119,6 @@ minimumVersion = 6.8.0; }; }; - BDCA41F92CF35EC0005EECF6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk-rx" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kakao/kakao-ios-sdk-rx"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.23.0; - }; - }; BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactorKit/ReactorKit.git"; @@ -4208,20 +4198,10 @@ package = 088DE2452D12DB5C0030FA9E /* XCRemoteSwiftPackageReference "ios-maps-sdk" */; productName = GoogleMaps; }; - 08B191B92CF609AE0057BC04 /* RxKakaoSDK */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F92CF35EC0005EECF6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk-rx" */; - productName = RxKakaoSDK; - }; - 08B191BB2CF609AE0057BC04 /* RxKakaoSDKAuth */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F92CF35EC0005EECF6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk-rx" */; - productName = RxKakaoSDKAuth; - }; - 08B191BD2CF609AE0057BC04 /* RxKakaoSDKUser */ = { + 08F403322D884F4D00BFA61A /* KakaoSDKUser */ = { isa = XCSwiftPackageProductDependency; - package = BDCA41F92CF35EC0005EECF6 /* XCRemoteSwiftPackageReference "kakao-ios-sdk-rx" */; - productName = RxKakaoSDKUser; + package = 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; }; 4E5825662D1951DF00EE83EF /* FloatingPanel */ = { isa = XCSwiftPackageProductDependency; diff --git a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 717ac91b..06d33cce 100644 --- a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "9287fcb2d41c7fcf69aee03103ff9eac9ec5b01fe72ca36a57109537b58b6a8d", + "originHash" : "460c30523d69559c359ade610d8fe09aab6a0d403cc71804de63aa23b3649fe0", "pins" : [ { "identity" : "alamofire", @@ -33,17 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kakao/kakao-ios-sdk.git", "state" : { - "revision" : "ab4309c1950550add307046ad1e08024c7514603", - "version" : "2.23.0" - } - }, - { - "identity" : "kakao-ios-sdk-rx", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kakao/kakao-ios-sdk-rx", - "state" : { - "revision" : "fa5ce05d610c4b026df8d42e891a32f31a239d58", - "version" : "2.23.0" + "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", + "version" : "2.24.0" } }, { @@ -64,15 +55,6 @@ "version" : "4.5.1" } }, - { - "identity" : "ohhttpstubs", - "kind" : "remoteSourceControl", - "location" : "https://github.com/AliSoftware/OHHTTPStubs.git", - "state" : { - "revision" : "12f19662426d0434d6c330c6974d53e2eb10ecd9", - "version" : "9.1.0" - } - }, { "identity" : "pageboy", "kind" : "remoteSourceControl", @@ -100,15 +82,6 @@ "version" : "3.2.0" } }, - { - "identity" : "rxalamofire", - "kind" : "remoteSourceControl", - "location" : "https://github.com/RxSwiftCommunity/RxAlamofire.git", - "state" : { - "revision" : "9535b58695b91fb67f56d58d6fd0c76462d7743a", - "version" : "6.1.2" - } - }, { "identity" : "rxdatasources", "kind" : "remoteSourceControl", diff --git a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme index 8971a232..858b58f7 100644 --- a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme +++ b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme @@ -1,6 +1,6 @@ Bool { - RxKakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue, loggingEnable: false) +// RxKakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue, loggingEnable: false) GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 diff --git a/Poppool/Poppool/Application/SceneDelegate.swift b/Poppool/Poppool/Application/SceneDelegate.swift index 8f18f6ac..df6fe39f 100644 --- a/Poppool/Poppool/Application/SceneDelegate.swift +++ b/Poppool/Poppool/Application/SceneDelegate.swift @@ -1,86 +1,6 @@ -//// -//// SceneDelegate.swift -//// Poppool -//// -//// Created by Porori on 11/24/24. -//// -// -//import UIKit -//import RxKakaoSDKAuth -//import KakaoSDKAuth -//import RxSwift -// -//class SceneDelegate: UIResponder, UIWindowSceneDelegate { -// -// var window: UIWindow? -// static let appDidBecomeActive = PublishSubject() -// -// func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { -// guard let windowScene = (scene as? UIWindowScene) else { return } -// window = UIWindow(windowScene: windowScene) -// -// // Debug: Admin Page Test -// let provider = ProviderImpl() -// let repository = DefaultAdminRepository(provider: provider) -// let useCase = DefaultAdminUseCase(repository: repository) -// let reactor = AdminReactor(useCase: useCase) -// let adminVC = AdminViewController() -// adminVC.reactor = reactor -// -// let navigationController = UINavigationController(rootViewController: adminVC) -// -// let rootViewController = LoginController() -// rootViewController.reactor = LoginReactor() -// -// let rootVC = WaveTabBarController() -// -// let rootViewController = DetailController() -// rootViewController.reactor = DetailReactor(popUpID: 8) -// -// let rootViewController = SearchMainController() -// rootViewController.reactor = SearchMainReactor() -// -// let navigationController = UINavigationController(rootViewController: rootVC) -// let navigationController = WaveTabBarController() -// -// window?.rootViewController = navigationController -// window?.makeKeyAndVisible() -// } -// -// func sceneDidDisconnect(_ scene: UIScene) { -// } -// -// func sceneDidBecomeActive(_ scene: UIScene) { -// SceneDelegate.appDidBecomeActive.onNext(()) -// } -// -// func sceneWillResignActive(_ scene: UIScene) { -// } -// -// func sceneWillEnterForeground(_ scene: UIScene) { -// } -// -// func sceneDidEnterBackground(_ scene: UIScene) { -// } -// -// func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { -// if let url = URLContexts.first?.url { -// if AuthApi.isKakaoTalkLoginUrl(url) { -// _ = AuthController.rx.handleOpenUrl(url: url) -// } -// } -// } -//} -// -// SceneDelegate.swift -// Poppool -// -// Created by Porori on 11/24/24. -// - import UIKit -import RxKakaoSDKAuth +//import RxKakaoSDKAuth import KakaoSDKAuth import RxSwift @@ -117,11 +37,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { - if let url = URLContexts.first?.url { - if AuthApi.isKakaoTalkLoginUrl(url) { - _ = AuthController.rx.handleOpenUrl(url: url) - } - } +// if let url = URLContexts.first?.url { +// if AuthApi.isKakaoTalkLoginUrl(url) { +// _ = AuthController.rx.handleOpenUrl(url: url) +// } +// } } } diff --git a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift index 951c56b5..57b5a362 100644 --- a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift +++ b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift @@ -7,7 +7,7 @@ import RxSwift import KakaoSDKUser -import RxKakaoSDKUser +//import RxKakaoSDKUser import KakaoSDKAuth final class KakaoLoginService: AuthServiceable { @@ -21,58 +21,58 @@ final class KakaoLoginService: AuthServiceable { func unlink() -> Observable { return Observable.create { observer in - UserApi.shared.unlink { error in - if let error = error { - observer.onNext(()) - Logger.log(message: error.localizedDescription, category: .error) - } else { - observer.onNext(()) - observer.onCompleted() - } - } +// UserApi.shared.unlink { error in +// if let error = error { +// observer.onNext(()) +// Logger.log(message: error.localizedDescription, category: .error) +// } else { +// observer.onNext(()) +// observer.onCompleted() +// } +// } return Disposables.create() } } func fetchUserCredential() -> Observable { return Observable.create { [weak self] observer in - guard let self = self else { - Logger.log( - message: "KakaoTalk login Error", - category: .error, - fileName: #file, - line: #line - ) - return Disposables.create() - } - // 카카오톡 설치 유무 - guard UserApi.isKakaoTalkLoginAvailable() else { - Logger.log( - message: "KakaoTalk is not install", - category: .error, - fileName: #file, - line: #line - ) - UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in - if let error = error { - observer.onError(error) - } else { - if let self = self, let accessToken = oauthToken?.accessToken { - self.fetchUserId(observer: observer, accessToken: accessToken) - } - } - } - return Disposables.create() - } - // token을 획득하기 위한 로그인 - loginWithKakaoTalk() - .withUnretained(self) - .subscribe { (owner, loginResponse) in - owner.fetchUserId(observer: observer, accessToken: loginResponse.accessToken) - } onError: { _ in - observer.onError(AuthError.unknownError) - } - .disposed(by: disposeBag) +// guard let self = self else { +// Logger.log( +// message: "KakaoTalk login Error", +// category: .error, +// fileName: #file, +// line: #line +// ) +// return Disposables.create() +// } +// // 카카오톡 설치 유무 +// guard UserApi.isKakaoTalkLoginAvailable() else { +// Logger.log( +// message: "KakaoTalk is not install", +// category: .error, +// fileName: #file, +// line: #line +// ) +// UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in +// if let error = error { +// observer.onError(error) +// } else { +// if let self = self, let accessToken = oauthToken?.accessToken { +// self.fetchUserId(observer: observer, accessToken: accessToken) +// } +// } +// } +// return Disposables.create() +// } +// // token을 획득하기 위한 로그인 +// loginWithKakaoTalk() +// .withUnretained(self) +// .subscribe { (owner, loginResponse) in +// owner.fetchUserId(observer: observer, accessToken: loginResponse.accessToken) +// } onError: { _ in +// observer.onError(AuthError.unknownError) +// } +// .disposed(by: disposeBag) return Disposables.create() } @@ -82,50 +82,51 @@ final class KakaoLoginService: AuthServiceable { private extension KakaoLoginService { func fetchUserId(observer: AnyObserver, accessToken: String) { - UserApi.shared.rx.me() - .subscribe(onSuccess: { user in - observer.onNext(.init(kakaoUserId: user.id,kakaoAccessToken: accessToken)) - }, onFailure: { _ in - observer.onError(AuthError.unknownError) - }) - .disposed(by: self.disposeBag) +// UserApi.shared.rx.me() +// .subscribe(onSuccess: { user in +// observer.onNext(.init(kakaoUserId: user.id,kakaoAccessToken: accessToken)) +// }, onFailure: { _ in +// observer.onError(AuthError.unknownError) +// }) +// .disposed(by: self.disposeBag) } func loginWithKakaoTalk() -> Observable { - return UserApi.shared.rx.loginWithKakaoTalk() - .do { token in - Logger.log( - message: "KakaoTalk Login Response - \(token)", - category: .info, - fileName: #file, - line: #line - ) - } onError: { _ in - Logger.log( - message: "KakaoTalk Login Fail", - category: .error, - fileName: #file, - line: #line - ) - } + return Observable.just(.init(accessToken: "", tokenType: "", refreshToken: "", scope: "", scopes: [])) +// return UserApi.shared.rx.loginWithKakaoTalk() +// .do { token in +// Logger.log( +// message: "KakaoTalk Login Response - \(token)", +// category: .info, +// fileName: #file, +// line: #line +// ) +// } onError: { _ in +// Logger.log( +// message: "KakaoTalk Login Fail", +// category: .error, +// fileName: #file, +// line: #line +// ) +// } } - func fetchUserProfile() -> Single { - return UserApi.shared.rx.me() - .do { user in - Logger.log( - message: "KakaoTalk Profile Response - \(user)", - category: .info, - fileName: #file, - line: #line - ) - } onError: { _ in - Logger.log( - message: "KakaoTalk Profile Fetch Fail", - category: .error, - fileName: #file, - line: #line - ) - } - } +// func fetchUserProfile() -> Single { +// return UserApi.shared.rx.me() +// .do { user in +// Logger.log( +// message: "KakaoTalk Profile Response - \(user)", +// category: .info, +// fileName: #file, +// line: #line +// ) +// } onError: { _ in +// Logger.log( +// message: "KakaoTalk Profile Fetch Fail", +// category: .error, +// fileName: #file, +// line: #line +// ) +// } +// } } From 5499aca3c3b32aa254e537a070504fee0212a9da Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 17 Mar 2025 22:14:53 +0900 Subject: [PATCH 004/393] =?UTF-8?q?[REFACTOR]:=20KakaoSDK=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 --- Poppool/Poppool/Application/AppDelegate.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 909fce62..aebe135a 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -6,7 +6,8 @@ // import UIKit -import KakaoSDKAuth + +import KakaoSDKCommon import GoogleMaps import CoreLocation @@ -14,8 +15,8 @@ import CoreLocation class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { -// RxKakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue, loggingEnable: false) - GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) + KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue) + GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 return true From 726ecaa0b9ad023de57228183021cd89353f144c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 17 Mar 2025 22:16:37 +0900 Subject: [PATCH 005/393] =?UTF-8?q?[STYLE]:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EB=B0=8F=20=EC=A3=BC=EC=84=9D?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 21 ++----------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index aebe135a..40284a66 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,10 +1,3 @@ -// -// AppDelegate.swift -// Poppool -// -// Created by Porori on 11/24/24. -// - import UIKit import KakaoSDKCommon @@ -17,26 +10,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue) GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) + let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 + return true - } // MARK: UISceneSession Lifecycle - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { - // Called when a new scene session is being created. - // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { - // Called when the user discards a scene session. - // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. - // Use this method to release any resources that were specific to the discarded scenes, as they will not return. - } - - } From 4616ac5d0ce7325950e62640a2a508a127d58417 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 17 Mar 2025 22:20:18 +0900 Subject: [PATCH 006/393] =?UTF-8?q?[REFACTOR]:=20SceneDelegate=EC=97=90?= =?UTF-8?q?=EC=84=9C=20KakaoSDK=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=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 --- Poppool/Poppool/Application/SceneDelegate.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Poppool/Poppool/Application/SceneDelegate.swift b/Poppool/Poppool/Application/SceneDelegate.swift index df6fe39f..a23213ca 100644 --- a/Poppool/Poppool/Application/SceneDelegate.swift +++ b/Poppool/Poppool/Application/SceneDelegate.swift @@ -1,6 +1,5 @@ import UIKit -//import RxKakaoSDKAuth import KakaoSDKAuth import RxSwift @@ -37,11 +36,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { -// if let url = URLContexts.first?.url { -// if AuthApi.isKakaoTalkLoginUrl(url) { -// _ = AuthController.rx.handleOpenUrl(url: url) -// } -// } + if let url = URLContexts.first?.url { + if AuthApi.isKakaoTalkLoginUrl(url) { + _ = AuthController.handleOpenUrl(url: url) + } + } } } From 510535f36179427a6b39caf72c50c59f51af8343 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 19 Mar 2025 00:07:11 +0900 Subject: [PATCH 007/393] =?UTF-8?q?[REFACTOR]:=20rx=20=EA=B1=B7=EC=96=B4?= =?UTF-8?q?=EB=82=B4=EB=8A=94=20=EC=9E=91=EC=97=85=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=EC=A7=84=ED=96=89=20=EB=B0=8F=20=EC=9E=91=EC=97=85=20=EC=96=91?= =?UTF-8?q?=EB=8F=84=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/KakaoLoginService.swift | 197 +++++++++--------- 1 file changed, 100 insertions(+), 97 deletions(-) diff --git a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift index 57b5a362..cff5897d 100644 --- a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift +++ b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift @@ -1,13 +1,5 @@ -// -// KakaoLoginService.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/13/24. -// - import RxSwift import KakaoSDKUser -//import RxKakaoSDKUser import KakaoSDKAuth final class KakaoLoginService: AuthServiceable { @@ -21,58 +13,57 @@ final class KakaoLoginService: AuthServiceable { func unlink() -> Observable { return Observable.create { observer in -// UserApi.shared.unlink { error in -// if let error = error { -// observer.onNext(()) -// Logger.log(message: error.localizedDescription, category: .error) -// } else { -// observer.onNext(()) -// observer.onCompleted() -// } -// } + UserApi.shared.unlink { error in + if let error = error { + observer.onNext(()) + Logger.log(message: error.localizedDescription, category: .error) + } else { + observer.onNext(()) + observer.onCompleted() + } + } + return Disposables.create() } } func fetchUserCredential() -> Observable { return Observable.create { [weak self] observer in -// guard let self = self else { -// Logger.log( -// message: "KakaoTalk login Error", -// category: .error, -// fileName: #file, -// line: #line -// ) -// return Disposables.create() -// } -// // 카카오톡 설치 유무 -// guard UserApi.isKakaoTalkLoginAvailable() else { -// Logger.log( -// message: "KakaoTalk is not install", -// category: .error, -// fileName: #file, -// line: #line -// ) -// UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in -// if let error = error { -// observer.onError(error) -// } else { -// if let self = self, let accessToken = oauthToken?.accessToken { -// self.fetchUserId(observer: observer, accessToken: accessToken) -// } -// } -// } -// return Disposables.create() -// } -// // token을 획득하기 위한 로그인 -// loginWithKakaoTalk() -// .withUnretained(self) -// .subscribe { (owner, loginResponse) in -// owner.fetchUserId(observer: observer, accessToken: loginResponse.accessToken) -// } onError: { _ in -// observer.onError(AuthError.unknownError) -// } -// .disposed(by: disposeBag) + guard let self else { + Logger.log( + message: "KakaoTalk login Error", + category: .error, + fileName: #file, + line: #line + ) + return Disposables.create() + } + + // 카카오톡 설치 유무 확인 + guard UserApi.isKakaoTalkLoginAvailable() else { + Logger.log( + message: "KakaoTalk is not install", + category: .error, + fileName: #file, + line: #line + ) + + // 카카오톡 미설치시 계정으로 접속 시도 + UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in + if let error = error { + observer.onError(error) + } else { + if let self = self, let accessToken = oauthToken?.accessToken { + self.fetchUserId(observer: observer, accessToken: accessToken) + } + } + } + + return Disposables.create() + } + + // token을 획득하기 위한 로그인 + loginWithKakaoTalk(observer: observer) return Disposables.create() } @@ -82,51 +73,63 @@ final class KakaoLoginService: AuthServiceable { private extension KakaoLoginService { func fetchUserId(observer: AnyObserver, accessToken: String) { -// UserApi.shared.rx.me() -// .subscribe(onSuccess: { user in -// observer.onNext(.init(kakaoUserId: user.id,kakaoAccessToken: accessToken)) -// }, onFailure: { _ in -// observer.onError(AuthError.unknownError) -// }) -// .disposed(by: self.disposeBag) + UserApi.shared.me() { user, error in + if let error = error { + observer.onError(AuthError.unknownError) + } else { + // ???: 여기 onComplete로 observer를 종료하지 않아도 되는지? + // ???: disposeBag으로 수거를 하지 않게 되는데 observer 수거에 대한 문제 발생 가능 여부 확인이 필요해보임 + observer.onNext(.init(kakaoUserId: user?.id, kakaoAccessToken: accessToken)) + } + } } - func loginWithKakaoTalk() -> Observable { - return Observable.just(.init(accessToken: "", tokenType: "", refreshToken: "", scope: "", scopes: [])) -// return UserApi.shared.rx.loginWithKakaoTalk() -// .do { token in -// Logger.log( -// message: "KakaoTalk Login Response - \(token)", -// category: .info, -// fileName: #file, -// line: #line -// ) -// } onError: { _ in -// Logger.log( -// message: "KakaoTalk Login Fail", -// category: .error, -// fileName: #file, -// line: #line -// ) -// } + func loginWithKakaoTalk(observer: AnyObserver) { + UserApi.shared.loginWithKakaoTalk { oauthToken, error in + if let error = error { + Logger.log( + message: "KakaoTalk Login Fail", + category: .error, + fileName: #file, + line: #line + ) + observer.onError(AuthError.unknownError) + } else { + if let oauthToken = oauthToken { + Logger.log( + message: "KakaoTalk Login Response - \(oauthToken)", + category: .info, + fileName: #file, + line: #line + ) + self.fetchUserId(observer: observer, accessToken: oauthToken.accessToken) + } + + + } + } } -// func fetchUserProfile() -> Single { -// return UserApi.shared.rx.me() -// .do { user in -// Logger.log( -// message: "KakaoTalk Profile Response - \(user)", -// category: .info, -// fileName: #file, -// line: #line -// ) -// } onError: { _ in -// Logger.log( -// message: "KakaoTalk Profile Fetch Fail", -// category: .error, -// fileName: #file, -// line: #line -// ) -// } -// } + func fetchUserProfile() -> Single { + // MARK: 이런 식으로 구현하면 될거 같다 생각만 하고 여기서 멈췄습니다... ㅠㅠ +// return Observable +// .just(<#T##element: _##_#>) +// .asSingle() + return UserApi.shared.rx.me() + .do { user in + Logger.log( + message: "KakaoTalk Profile Response - \(user)", + category: .info, + fileName: #file, + line: #line + ) + } onError: { _ in + Logger.log( + message: "KakaoTalk Profile Fetch Fail", + category: .error, + fileName: #file, + line: #line + ) + } + } } From 8de2ea12f9df2c87f8bf95ac2e12a40ad29b203b Mon Sep 17 00:00:00 2001 From: JunYoung Date: Fri, 21 Mar 2025 15:16:01 +0900 Subject: [PATCH 008/393] =?UTF-8?q?[REFACTOR]:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B6=84=EB=A6=AC,=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20error=EC=88=98=EC=A0=95=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=BC=EB=B6=80=20=EB=82=B4=EC=9A=A9=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/AppleLoginService.swift | 17 +--- .../Infrastructure/AuthServiceable.swift | 2 +- .../Infrastructure/KakaoLoginService.swift | 92 ++++++------------- 3 files changed, 34 insertions(+), 77 deletions(-) diff --git a/Poppool/Poppool/Infrastructure/AppleLoginService.swift b/Poppool/Poppool/Infrastructure/AppleLoginService.swift index e58e20b5..a26f2e0c 100644 --- a/Poppool/Poppool/Infrastructure/AppleLoginService.swift +++ b/Poppool/Poppool/Infrastructure/AppleLoginService.swift @@ -58,23 +58,12 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi case let appleIDCredential as ASAuthorizationAppleIDCredential: guard let idToken = appleIDCredential.identityToken else { // 토큰이 없는 경우 오류 방출 - Logger.log( - message: "AppleLogin Token is Not Found", - category: .error, - fileName: #file, - line: #line - ) - authServiceResponse.onError(AuthError.unknownError) + authServiceResponse.onError(AuthError.unknownError(description: "AppleLogin Token is Not Found")) return } guard let idToken = String(data: idToken, encoding: .utf8) else { - Logger.log( - message: "AppleLogin Token Convert Fail", - category: .error, - fileName: #file, - line: #line - ) - authServiceResponse.onError(AuthError.unknownError) + // 토큰 convert가 실패할 경우 오류 방출 + authServiceResponse.onError(AuthError.unknownError(description: "AppleLogin Token Convert Fail")) return } guard let authorizationCode = appleIDCredential.authorizationCode else { diff --git a/Poppool/Poppool/Infrastructure/AuthServiceable.swift b/Poppool/Poppool/Infrastructure/AuthServiceable.swift index 79c2330e..cd20f3ee 100644 --- a/Poppool/Poppool/Infrastructure/AuthServiceable.swift +++ b/Poppool/Poppool/Infrastructure/AuthServiceable.swift @@ -24,5 +24,5 @@ struct AuthServiceResponse: Encodable { enum AuthError: Error { case notInstalled - case unknownError + case unknownError(description: String?) } diff --git a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift index cff5897d..4d234418 100644 --- a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift +++ b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift @@ -4,11 +4,6 @@ import KakaoSDKAuth final class KakaoLoginService: AuthServiceable { - struct Credential: Encodable { - var id: String - var token: String - } - var disposeBag = DisposeBag() func unlink() -> Observable { @@ -48,22 +43,13 @@ final class KakaoLoginService: AuthServiceable { line: #line ) - // 카카오톡 미설치시 계정으로 접속 시도 - UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in - if let error = error { - observer.onError(error) - } else { - if let self = self, let accessToken = oauthToken?.accessToken { - self.fetchUserId(observer: observer, accessToken: accessToken) - } - } - } - + // 카카오톡 미설치시 웹으로 인증 시도 + loginWithKakaoTalkWeb(observer: observer) return Disposables.create() } - - // token을 획득하기 위한 로그인 - loginWithKakaoTalk(observer: observer) + + // 카카오톡 설치시 앱으로 인증 시도 + loginWithKakaoTalkApp(observer: observer) return Disposables.create() } @@ -72,64 +58,46 @@ final class KakaoLoginService: AuthServiceable { private extension KakaoLoginService { + /// 제공된 액세스 토큰을 사용하여 사용자의 카카오 ID를 가져옵니다. + /// - Parameters: + /// - observer: 인증 응답을 처리할 옵저버. + /// - accessToken: 카카오 로그인 과정에서 얻은 액세스 토큰. func fetchUserId(observer: AnyObserver, accessToken: String) { UserApi.shared.me() { user, error in if let error = error { - observer.onError(AuthError.unknownError) + observer.onError(AuthError.unknownError(description: error.localizedDescription)) } else { - // ???: 여기 onComplete로 observer를 종료하지 않아도 되는지? - // ???: disposeBag으로 수거를 하지 않게 되는데 observer 수거에 대한 문제 발생 가능 여부 확인이 필요해보임 observer.onNext(.init(kakaoUserId: user?.id, kakaoAccessToken: accessToken)) + observer.onCompleted() } } } - - func loginWithKakaoTalk(observer: AnyObserver) { - UserApi.shared.loginWithKakaoTalk { oauthToken, error in + + /// 카카오톡 앱을 사용하여 로그인하고 액세스 토큰을 가져옵니다. + /// - Parameter observer: 인증 응답을 처리할 옵저버. + func loginWithKakaoTalkApp(observer: AnyObserver) { + UserApi.shared.loginWithKakaoTalk { [weak self] oauthToken, error in if let error = error { - Logger.log( - message: "KakaoTalk Login Fail", - category: .error, - fileName: #file, - line: #line - ) - observer.onError(AuthError.unknownError) + observer.onError(AuthError.unknownError(description: error.localizedDescription)) } else { - if let oauthToken = oauthToken { - Logger.log( - message: "KakaoTalk Login Response - \(oauthToken)", - category: .info, - fileName: #file, - line: #line - ) - self.fetchUserId(observer: observer, accessToken: oauthToken.accessToken) + if let accessToken = oauthToken?.accessToken { + self?.fetchUserId(observer: observer, accessToken: accessToken) } - - } } } - func fetchUserProfile() -> Single { - // MARK: 이런 식으로 구현하면 될거 같다 생각만 하고 여기서 멈췄습니다... ㅠㅠ -// return Observable -// .just(<#T##element: _##_#>) -// .asSingle() - return UserApi.shared.rx.me() - .do { user in - Logger.log( - message: "KakaoTalk Profile Response - \(user)", - category: .info, - fileName: #file, - line: #line - ) - } onError: { _ in - Logger.log( - message: "KakaoTalk Profile Fetch Fail", - category: .error, - fileName: #file, - line: #line - ) + /// 카카오톡 웹을 사용하여 로그인하고 액세스 토큰을 가져옵니다. + /// - Parameter observer: 인증 응답을 처리할 옵저버. + func loginWithKakaoTalkWeb(observer: AnyObserver) { + UserApi.shared.loginWithKakaoAccount { [weak self] (oauthToken, error) in + if let error = error { + observer.onError(AuthError.unknownError(description: error.localizedDescription)) + } else { + if let accessToken = oauthToken?.accessToken { + self?.fetchUserId(observer: observer, accessToken: accessToken) + } } + } } } From 1a3197d2e1f4130520372f8571dc3b86d115144d Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 23 Mar 2025 23:12:48 +0900 Subject: [PATCH 009/393] =?UTF-8?q?[FIX]:=20kakao=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=8B=9C=20=EC=95=B1=20=EC=82=AC=EC=9A=A9=EC=9D=84?= =?UTF-8?q?=20=EC=9C=84=ED=95=9C=20Schemes=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Resource/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 9b1010ac..9b8b88b1 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -22,6 +22,8 @@ kakaomap tmap maps + kakaokompassauth + kakaotalk UIAppFonts From a941d63c1f2c97b94a301f22429a645913457215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Thu, 20 Mar 2025 01:38:25 +0900 Subject: [PATCH 010/393] =?UTF-8?q?[REFACTOR]=20:=20NMaps=20SPM=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A7=80=EB=8F=84=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20NaverMap=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 23 +- .../xcshareddata/swiftpm/Package.resolved | 168 -- Poppool/Poppool/Application/AppDelegate.swift | 11 + .../Admin/Data/MapDomain/MapPopUpStore.swift | 36 +- .../Data/MapDomain/MapPopUpStoreDTO.swift | 1 - .../MapDomain/Repository/MapRepository.swift | 4 +- .../Map/Common/ClusteringManager.swift | 187 +- .../Map/Common/ClusteringModels.swift | 27 +- .../Map/Common/GMSMapViewDelegateProxy.swift | 39 - .../Map/Common/MapUtilities.swift | 14 +- .../Map/Common/NMFMapViewDelegateProxy.swift | 56 + .../Map/Common/RegionDefinitions.swift | 500 ++-- .../FullScreenMapViewController.swift | 261 +- .../MapGuideView/MapGuideAppService.swift | 71 + .../MapGuideView/MapGuideViewController.swift | 103 +- .../Presentation/Map/MapView/MapMarker.swift | 19 +- .../Presentation/Map/MapView/MapReactor.swift | 38 + .../Presentation/Map/MapView/MapView.swift | 33 +- .../Map/MapView/MapViewController.swift | 2204 ++++++++--------- .../Map/StoreListView/StoreListReactor.swift | 3 +- 20 files changed, 1780 insertions(+), 2018 deletions(-) delete mode 100644 Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved delete mode 100644 Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift create mode 100644 Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift create mode 100644 Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index d05337a5..026dc060 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -373,6 +373,7 @@ 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */; }; 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 08F403322D884F4D00BFA61A /* KakaoSDKUser */; }; 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; + 4E60C2262D8AABBF003A5A37 /* NMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E60C2252D8AABBF003A5A37 /* NMap */; }; 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */; }; 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */; }; 4E685ECE2D12CEB6001EF91C /* BalloonBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAA2D12CEB6001EF91C /* BalloonBackgroundView.swift */; }; @@ -427,7 +428,7 @@ 4EA2C9432D424DF900F4D97C /* FindDirectionEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA2C9422D424DF900F4D97C /* FindDirectionEndPoint.swift */; }; 4EA9989A2D21C2FC009DC30B /* StoreListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA998992D21C2FC009DC30B /* StoreListSection.swift */; }; 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA9989C2D21C404009DC30B /* RxDataSources */; }; - 4EAB809D2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809C2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift */; }; + 4EAB809D2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */; }; 4EAB809F2D3F8EF50041AF30 /* ViewportBounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */; }; 4EDDEFB42D2D285900CFAFA5 /* DateTimePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */; }; 4EDE57012D5E6A5F0014D924 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */; }; @@ -914,7 +915,7 @@ 4EA2C9402D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpDirectionResponseDTO.swift; sourceTree = ""; }; 4EA2C9422D424DF900F4D97C /* FindDirectionEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindDirectionEndPoint.swift; sourceTree = ""; }; 4EA998992D21C2FC009DC30B /* StoreListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListSection.swift; sourceTree = ""; }; - 4EAB809C2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GMSMapViewDelegateProxy.swift; sourceTree = ""; }; + 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NMFMapViewDelegateProxy.swift; sourceTree = ""; }; 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportBounds.swift; sourceTree = ""; }; 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimePickerManager.swift; sourceTree = ""; }; 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPermissionBottomSheet.swift; sourceTree = ""; }; @@ -981,6 +982,7 @@ BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, + 4E60C2262D8AABBF003A5A37 /* NMap in Frameworks */, 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, @@ -2880,7 +2882,7 @@ 4EED9BAA2D2272F500B288E7 /* Common */ = { isa = PBXGroup; children = ( - 4EAB809C2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift */, + 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */, 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */, 4EED9BAB2D22730400B288E7 /* FilterType.swift */, 4E6C07052D4B6E56008A962A /* RegionDefinitions.swift */, @@ -3647,7 +3649,7 @@ 086F89CA2D1E42A700CA4FC9 /* CommentUserBlockView.swift in Sources */, 086DD8D02CFDFEB900B97D3B /* HomeListReactor.swift in Sources */, 081899202D34DF880067BF01 /* MyPageNoticeDetailController.swift in Sources */, - 4EAB809D2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift in Sources */, + 4EAB809D2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift in Sources */, 083C86472D0DCDFB003F441C /* AddCommentTitleSection.swift in Sources */, 0841BAB62CFABEDC00049E31 /* HomePopularCardSectionCell.swift in Sources */, 082197A92D4E3EE90054094A /* CommentMyMenuController.swift in Sources */, @@ -4087,6 +4089,14 @@ minimumVersion = 2.8.6; }; }; + 4E60C2242D8AABBF003A5A37 /* XCRemoteSwiftPackageReference "NaverMap" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/zzangzzangguy/NaverMap.git"; + requirement = { + branch = main; + kind = branch; + }; + }; 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources.git"; @@ -4208,6 +4218,11 @@ package = 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */; productName = FloatingPanel; }; + 4E60C2252D8AABBF003A5A37 /* NMap */ = { + isa = XCSwiftPackageProductDependency; + package = 4E60C2242D8AABBF003A5A37 /* XCRemoteSwiftPackageReference "NaverMap" */; + productName = NMap; + }; 4EA9989C2D21C404009DC30B /* RxDataSources */ = { isa = XCSwiftPackageProductDependency; package = 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */; diff --git a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 06d33cce..00000000 --- a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,168 +0,0 @@ -{ - "originHash" : "460c30523d69559c359ade610d8fe09aab6a0d403cc71804de63aa23b3649fe0", - "pins" : [ - { - "identity" : "alamofire", - "kind" : "remoteSourceControl", - "location" : "https://github.com/Alamofire/Alamofire.git", - "state" : { - "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", - "version" : "5.10.2" - } - }, - { - "identity" : "floatingpanel", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scenee/FloatingPanel.git", - "state" : { - "revision" : "b6e8928b1a3ad909e6db6a0278d286c33cfd0dc3", - "version" : "2.8.6" - } - }, - { - "identity" : "ios-maps-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/googlemaps/ios-maps-sdk", - "state" : { - "revision" : "9fa352d6eca4a731949efcdb27ed851f1fcd4447", - "version" : "9.3.0" - } - }, - { - "identity" : "kakao-ios-sdk", - "kind" : "remoteSourceControl", - "location" : "https://github.com/kakao/kakao-ios-sdk.git", - "state" : { - "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", - "version" : "2.24.0" - } - }, - { - "identity" : "kingfisher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/onevcat/Kingfisher.git", - "state" : { - "revision" : "3db26ab625d194c38e68c1a40e43d1bc12743fe0", - "version" : "8.2.0" - } - }, - { - "identity" : "lottie-spm", - "kind" : "remoteSourceControl", - "location" : "https://github.com/airbnb/lottie-spm.git", - "state" : { - "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", - "version" : "4.5.1" - } - }, - { - "identity" : "pageboy", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Pageboy.git", - "state" : { - "revision" : "be0c1f6f1964cfb07f9d819b0863f2c3f255f612", - "version" : "4.2.0" - } - }, - { - "identity" : "panmodal", - "kind" : "remoteSourceControl", - "location" : "https://github.com/slackhq/PanModal.git", - "state" : { - "revision" : "b012aecb6b67a8e46369227f893c12544846613f", - "version" : "1.2.7" - } - }, - { - "identity" : "reactorkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ReactorKit/ReactorKit.git", - "state" : { - "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", - "version" : "3.2.0" - } - }, - { - "identity" : "rxdatasources", - "kind" : "remoteSourceControl", - "location" : "https://github.com/RxSwiftCommunity/RxDataSources.git", - "state" : { - "revision" : "90c29b48b628479097fe775ed1966d75ac374518", - "version" : "5.0.2" - } - }, - { - "identity" : "rxgesture", - "kind" : "remoteSourceControl", - "location" : "https://github.com/RxSwiftCommunity/RxGesture.git", - "state" : { - "revision" : "1b137c576b4aaaab949235752278956697c9e4a0", - "version" : "4.0.4" - } - }, - { - "identity" : "rxkeyboard", - "kind" : "remoteSourceControl", - "location" : "https://github.com/RxSwiftCommunity/RxKeyboard.git", - "state" : { - "revision" : "63f6377975c962a1d89f012a6f1e5bebb2c502b7", - "version" : "2.0.1" - } - }, - { - "identity" : "rxswift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ReactiveX/RxSwift.git", - "state" : { - "revision" : "5dd1907d64f0d36f158f61a466bab75067224893", - "version" : "6.9.0" - } - }, - { - "identity" : "snapkit", - "kind" : "remoteSourceControl", - "location" : "https://github.com/SnapKit/SnapKit.git", - "state" : { - "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", - "version" : "5.7.1" - } - }, - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup", - "state" : { - "revision" : "18ad8b8ff0f03f3c0a5544ffccfa2ea1c051ae6e", - "version" : "2.8.0" - } - }, - { - "identity" : "tabman", - "kind" : "remoteSourceControl", - "location" : "https://github.com/uias/Tabman.git", - "state" : { - "revision" : "3b2213290eb93e55bb50b49d1a179033005c11ab", - "version" : "3.2.0" - } - }, - { - "identity" : "then", - "kind" : "remoteSourceControl", - "location" : "https://github.com/devxoul/Then", - "state" : { - "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", - "version" : "3.0.0" - } - }, - { - "identity" : "weakmaptable", - "kind" : "remoteSourceControl", - "location" : "https://github.com/ReactorKit/WeakMapTable.git", - "state" : { - "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", - "version" : "1.2.1" - } - } - ], - "version" : 3 -} diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 40284a66..8d2ed331 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,3 +1,14 @@ +// +// AppDelegate.swift +// Poppoolasdasdasdasda +// +// Created by Porori on 11/24/24. +// +import UIKit +import RxKakaoSDKAuth +import KakaoSDKAuth +import RxKakaoSDKCommon +import NMapsMap import UIKit import KakaoSDKCommon diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift index bcc448f4..ad5875c8 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift @@ -1,11 +1,6 @@ -// -// MapPopUpStore.swift -// Poppool -// -// Created by 김기현 on 12/3/24. -// import Foundation import CoreLocation +import NMapsMap struct MapPopUpStore: Equatable { let id: Int64 @@ -19,35 +14,34 @@ struct MapPopUpStore: Equatable { let markerId: Int64 let markerTitle: String let markerSnippet: String - let mainImageUrl: String? // 이미지 URL 추가 - + let mainImageUrl: String? var coordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: latitude, longitude: longitude) } -} - extension MapPopUpStore { - func toMarkerInput() -> MapMarker.Input { - return MapMarker.Input( - isSelected: false, - isCluster: false, - regionName: self.markerTitle, // 또는 name이나 다른 적절한 필드 - count: 0 - ) - } - + var nmgCoordinate: NMGLatLng { + NMGLatLng(lat: latitude, lng: longitude) + } + func toMarkerInput() -> MapMarker.Input { + return MapMarker.Input( + isSelected: false, + isCluster: false, + regionName: self.markerTitle, + count: 0 + ) + } func toStoreItem() -> StoreItem { return StoreItem( id: id, - thumbnailURL: mainImageUrl ?? "", // 이미지 URL 매핑 + thumbnailURL: mainImageUrl ?? "", category: category, title: name, location: address, dateRange: "\(startDate) ~ \(endDate)", - isBookmarked: false // 기본값 + isBookmarked: false ) } } diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStoreDTO.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStoreDTO.swift index 1db431dd..cd7bb5de 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStoreDTO.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStoreDTO.swift @@ -15,7 +15,6 @@ struct MapPopUpStoreDTO: Codable { let mainImageUrl: String? let bookmarkYn: Bool? - // toDomain() 메서드 추가 func toDomain() -> MapPopUpStore { return MapPopUpStore( id: id, diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift index 53164882..2c44294a 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift @@ -49,7 +49,7 @@ class DefaultMapRepository: MapRepository { southWestLon: southWestLon, categories: categories ), - interceptor: TokenInterceptor() // ← 토큰 누락 해결 + interceptor: TokenInterceptor() ) .map { $0.popUpStoreList } } @@ -63,7 +63,7 @@ class DefaultMapRepository: MapRepository { query: query, categories: categories ), - interceptor: TokenInterceptor() // ← 토큰 누락 해결 + interceptor: TokenInterceptor() ) .map { $0.popUpStoreList } } diff --git a/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift b/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift index 306392bb..8b8600f6 100644 --- a/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift +++ b/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift @@ -1,23 +1,22 @@ -import CoreLocation -import UIKit +import NMapsMap class ClusteringManager { - private let regions = RegionDefinitions.allClusters + private let regions = RegionType.RegionDefinitions.allClusters private class MutableCluster { let base: RegionCluster var stores: [MapPopUpStore] var storeCount: Int - var fixedCenter: CLLocationCoordinate2D? + var fixedCenter: NMGLatLng? - init(base: RegionCluster, fixedCenter: CLLocationCoordinate2D? = nil) { + init(base: RegionCluster, fixedCenter: NMGLatLng? = nil) { self.base = base self.stores = [] self.storeCount = 0 self.fixedCenter = fixedCenter } - func centerCoordinate() -> CLLocationCoordinate2D { + func centerCoordinate() -> NMGLatLng { return fixedCenter ?? base.coordinate } @@ -35,87 +34,65 @@ class ClusteringManager { } } - // 수정: 항상 구 단위 클러스터링만 사용하도록 변경 func clusterStores(_ stores: [MapPopUpStore], at zoomLevel: Float) -> [ClusterMarkerData] { - // 줌 레벨 무시하고 항상 구 단위 클러스터링 실행 - return clusterByDistrictOnly(stores) - } - - // 새로운 함수: 모든 스토어를 구 단위로만 클러스터링 - private func clusterByDistrictOnly(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { - var clusters: [String: MutableCluster] = [:] + let level = MapZoomLevel.getLevel(from: zoomLevel) - // 서울 구 클러스터 초기화 - for cluster in RegionDefinitions.seoulClusters { - clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) + // partition() 호출 결과를 별도의 변수에 할당 + let partitionedStores = stores.partition { store in + let city = extractCity(from: store.address) + return city == "서울" || city == "경기" } - - // 경기 시/군 클러스터 초기화 - for cluster in RegionDefinitions.gyeonggiClusters { - clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) + let seoulGyeonggiStores = partitionedStores.0 + let otherStores = partitionedStores.1 + + switch level { + case .country: + return clusterByProvince(stores) + case .city: + let seoulGyeonggiClusters = clusterByCityLevel(seoulGyeonggiStores) + let otherClusters = clusterByMetropolitan(otherStores) + return seoulGyeonggiClusters + otherClusters + case .district: + let seoulGyeonggiClusters = clusterByDistrict(seoulGyeonggiStores) + let otherClusters = clusterByMetropolitan(otherStores) + return seoulGyeonggiClusters + otherClusters + case .detailed: + return [] } + } - // 기타 광역시/도 초기화 - for cluster in RegionDefinitions.metropolitanClusters { - clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) - } + private func clusterByMetropolitan(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { + var clusters: [String: MutableCluster] = [:] - // 도 단위 초기화 - for cluster in RegionDefinitions.provinceClusters { + // 광역시/도 클러스터 초기화 + let allClusters = RegionType.RegionDefinitions.metropolitanClusters + RegionType.RegionDefinitions.provinceClusters + for cluster in allClusters { clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - // 각 스토어를 적절한 클러스터에 할당 + // 스토어 할당 for store in stores { let city = extractCity(from: store.address) - - switch city { - case "서울": - if let clusterName = findMatchingSeoulDistrictName(in: store.address), - let cluster = clusters[clusterName] { - cluster.stores.append(store) - cluster.storeCount += 1 - } - case "경기": - if let clusterName = findMatchingGyeonggiCityName(in: store.address), - let cluster = clusters[clusterName] { - cluster.stores.append(store) - cluster.storeCount += 1 - } - default: - // 서울/경기 외 지역은 광역시/도 단위로 클러스터링 - if let cluster = clusters[city] { - cluster.stores.append(store) - cluster.storeCount += 1 - } + if let cluster = clusters[city] { + cluster.stores.append(store) + cluster.storeCount += 1 } } - // 스토어가 있는 클러스터만 필터링 let validClusters = clusters.values.filter { $0.storeCount > 0 } return validClusters.map { $0.toMarkerData() } } - // 기존 함수들 유지 (다만 더 이상 사용되지 않음) - private func clusterByCityName(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { + private func clusterByCityLevel(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { var clusters: [String: MutableCluster] = [:] + // 서울/경기 클러스터 초기화 + initializeSeoulGyeonggiClusters(&clusters) + for store in stores { let city = extractCity(from: store.address) - - // 아직 해당 city 이름으로 된 MutableCluster가 없다면 생성 - if clusters[city] == nil { - let baseRegion = RegionCluster( - name: city, - subRegions: [city], - coordinate: getFixedCenterForCity(city) ?? CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780), // 기본값(서울 좌표) - type: .metropolitan - ) - clusters[city] = MutableCluster(base: baseRegion, fixedCenter: baseRegion.coordinate) - } - - // 스토어 할당 - if let cluster = clusters[city] { + let clusterKey = determineClusterKey(for: store, city: city, clusters: &clusters) + if let cluster = clusters[clusterKey] { cluster.stores.append(store) cluster.storeCount += 1 } @@ -125,26 +102,44 @@ class ClusteringManager { return validClusters.map { $0.toMarkerData() } } - private func clusterByMetropolitan(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { - var clusters: [String: MutableCluster] = [:] - - // 광역시/도 클러스터 초기화 - let allClusters = RegionDefinitions.metropolitanClusters + RegionDefinitions.provinceClusters - for cluster in allClusters { - clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) + private func initializeSeoulGyeonggiClusters(_ clusters: inout [String: MutableCluster]) { + let predefinedClusters = [ + ("서울 북부", RepresentativeScope.seoulNorth.center), + ("서울 남부", RepresentativeScope.seoulSouth.center), + ("경기 북부", RepresentativeScope.gyeonggiNorth.center), + ("경기 남부", RepresentativeScope.gyeonggiSouth.center) + ] + + for (name, coordinate) in predefinedClusters { + let baseRegion = RegionCluster( + name: name, + subRegions: [name], + coordinate: coordinate, + type: .metropolitan + ) + clusters[name] = MutableCluster(base: baseRegion, fixedCenter: coordinate) } + } - // 스토어 할당 - for store in stores { - let city = extractCity(from: store.address) - if let cluster = clusters[city] { - cluster.stores.append(store) - cluster.storeCount += 1 + private func determineClusterKey(for store: MapPopUpStore, city: String, clusters: inout [String: MutableCluster]) -> String { + if city == "서울" { + return seoulNorthRegions.contains(where: { store.address.contains($0) }) ? "서울 북부" : "서울 남부" + } else if city == "경기" { + return gyeonggiNorthRegions.contains(where: { store.address.contains($0) }) ? "경기 북부" : "경기 남부" + } else { + if clusters[city] == nil { + if let coordinate = getFixedCenterForCity(city) { + let baseRegion = RegionCluster( + name: city, + subRegions: [city], + coordinate: coordinate, + type: .metropolitan + ) + clusters[city] = MutableCluster(base: baseRegion, fixedCenter: coordinate) + } } + return city } - - let validClusters = clusters.values.filter { $0.storeCount > 0 } - return validClusters.map { $0.toMarkerData() } } private func clusterByDistrict(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { @@ -152,19 +147,18 @@ class ClusteringManager { var gyeonggiClusters: [String: MutableCluster] = [:] var otherClusters: [String: MutableCluster] = [:] - // 서울/경기 각 구/시 초기화 - for cluster in RegionDefinitions.seoulClusters { + // 서울/경기 클러스터 초기화 + for cluster in RegionType.RegionDefinitions.seoulClusters { seoulClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - for cluster in RegionDefinitions.gyeonggiClusters { + for cluster in RegionType.RegionDefinitions.gyeonggiClusters { gyeonggiClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - - // 그 외 지역 - for cluster in RegionDefinitions.metropolitanClusters { + // 다른 지역 클러스터 초기화 + for cluster in RegionType.RegionDefinitions.metropolitanClusters { otherClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - for cluster in RegionDefinitions.provinceClusters { + for cluster in RegionType.RegionDefinitions.provinceClusters { otherClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } @@ -193,12 +187,18 @@ class ClusteringManager { let combined = Array(seoulClusters.values) + Array(gyeonggiClusters.values) + Array(otherClusters.values) let filtered = combined.filter { $0.storeCount > 0 } + + Logger.log(message: "구 단위 클러스터 생성 결과: \(filtered.count)개", category: .debug) + for cluster in filtered { + Logger.log(message: "- \(cluster.base.name): \(cluster.storeCount)개 매장", category: .debug) + } + return filtered.map { $0.toMarkerData() } } private func clusterByProvince(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { var clusters: [String: MutableCluster] = [:] - for cluster in RegionDefinitions.provinceClusters { + for cluster in RegionType.RegionDefinitions.provinceClusters { clusters[cluster.name] = MutableCluster(base: cluster) } for store in stores { @@ -213,7 +213,7 @@ class ClusteringManager { } private func findMatchingSeoulDistrictName(in address: String) -> String? { - return RegionDefinitions.seoulClusters.first { cluster in + return RegionType.RegionDefinitions.seoulClusters.first { cluster in cluster.subRegions.contains { district in address.contains(district) } @@ -221,7 +221,7 @@ class ClusteringManager { } private func findMatchingGyeonggiCityName(in address: String) -> String? { - return RegionDefinitions.gyeonggiClusters.first { cluster in + return RegionType.RegionDefinitions.gyeonggiClusters.first { cluster in cluster.subRegions.contains { cityName in address.contains(cityName) } @@ -229,14 +229,14 @@ class ClusteringManager { } private func findMatchingProvinceName(in address: String) -> String? { - return RegionDefinitions.provinceClusters.first { cluster in + return RegionType.RegionDefinitions.provinceClusters.first { cluster in cluster.subRegions.contains { province in address.contains(province) } }?.name } - private func getFixedCenterForCity(_ city: String) -> CLLocationCoordinate2D? { + private func getFixedCenterForCity(_ city: String) -> NMGLatLng? { switch city { case "대구": return RegionCoordinate.daegu case "부산": return RegionCoordinate.busan @@ -250,7 +250,6 @@ class ClusteringManager { } } -// partition() 확장: 서울·경기 vs 그 외 지역 분류 용 extension Array { func partition(by predicate: (Element) -> Bool) -> ([Element], [Element]) { var matching: [Element] = [] diff --git a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift index 3d5a1c94..0f218e3c 100644 --- a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift +++ b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift @@ -1,4 +1,4 @@ -import CoreLocation +import NMapsMap enum MapZoomLevel { case country @@ -7,24 +7,27 @@ enum MapZoomLevel { case detailed static func getLevel(from zoom: Float) -> MapZoomLevel { - switch zoom { - case ..<7: - return .country - case 7..<10: - return .city - case 10..<12: - return .district - default: - return .detailed + let level: MapZoomLevel + switch zoom { + case ..<7: + level = .country + case 7..<10: + level = .city + case 10..<11: + level = .district + default: + level = .detailed + } + Logger.log(message: "줌 레벨 계산: \(zoom) -> \(level)", category: .debug) + return level } } -} struct RegionCluster { let name: String let subRegions: [String] - let coordinate: CLLocationCoordinate2D + let coordinate: NMGLatLng let type: RegionType var storeCount: Int = 0 diff --git a/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift b/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift deleted file mode 100644 index a3e7064b..00000000 --- a/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift +++ /dev/null @@ -1,39 +0,0 @@ -import RxSwift -import RxCocoa -import GoogleMaps - -class GMSMapViewDelegateProxy: DelegateProxy, DelegateProxyType, GMSMapViewDelegate { - - public private(set) weak var mapView: GMSMapView? - let didChangePositionSubject = PublishSubject() - let idleAtPositionSubject = PublishSubject() - - init(mapView: GMSMapView) { - self.mapView = mapView - super.init(parentObject: mapView, delegateProxy: GMSMapViewDelegateProxy.self) - } - - static func registerKnownImplementations() { - self.register { mapView in - GMSMapViewDelegateProxy(mapView: mapView) - } - } - - static func currentDelegate(for object: GMSMapView) -> GMSMapViewDelegate? { - return object.delegate - } - - static func setCurrentDelegate(_ delegate: GMSMapViewDelegate?, to object: GMSMapView) { - object.delegate = delegate - } - - func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { - didChangePositionSubject.onNext(()) - self.forwardToDelegate()?.mapView?(mapView, didChange: position) - } - - func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) { - idleAtPositionSubject.onNext(()) - self.forwardToDelegate()?.mapView?(mapView, idleAt: position) - } -} diff --git a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift index ff6f6557..1b20d677 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift @@ -1,6 +1,5 @@ - - -import CoreLocation +import NMapsMap +import UIKit public func extractCity(from address: String) -> String { let components = address.components(separatedBy: " ") @@ -33,22 +32,21 @@ public let gyeonggiSouthRegions: [String] = [ // RepresentativeScope 수정 public struct RepresentativeScope { public static let seoulNorth = ( - center: CLLocationCoordinate2D(latitude: 37.6020, longitude: 127.0350), + center: NMGLatLng(lat: 37.6020, lng: 127.0350), radius: 3000.0 ) public static let seoulSouth = ( - center: CLLocationCoordinate2D(latitude: 37.4959, longitude: 127.0664), // 강남/서초 중심 + center: NMGLatLng(lat: 37.4959, lng: 127.0664), // 강남/서초 중심 radius: 3000.0 ) // 경기 북부/남부 좌표 조정 public static let gyeonggiNorth = ( - center: CLLocationCoordinate2D(latitude: 37.7358, longitude: 127.0346), // 의정부 중심 + center: NMGLatLng(lat: 37.7358, lng: 127.0346), // 의정부 중심 radius: 4000.0 ) public static let gyeonggiSouth = ( - center: CLLocationCoordinate2D(latitude: 37.2911, longitude: 127.0876), // 용인/분당 중심 + center: NMGLatLng(lat: 37.2911, lng: 127.0876), // 용인/분당 중심 radius: 4000.0 ) } - diff --git a/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift new file mode 100644 index 00000000..d5ef360c --- /dev/null +++ b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift @@ -0,0 +1,56 @@ +import NMapsMap +import RxSwift +import RxCocoa + +class NMFMapViewDelegateProxy: DelegateProxy, DelegateProxyType, NMFMapViewDelegate { + + public weak private(set) var mapView: NMFMapView? + + // Rx 이벤트를 위한 subject 추가 + let didChangePositionSubject = PublishSubject() + let idleAtPositionSubject = PublishSubject() + + init(mapView: NMFMapView) { + self.mapView = mapView + super.init(parentObject: mapView, delegateProxy: NMFMapViewDelegateProxy.self) + } + + static func registerKnownImplementations() { + self.register { NMFMapViewDelegateProxy(mapView: $0) } + } + + static func currentDelegate(for object: NMFMapView) -> NMFMapViewDelegate? { + return object.delegate + } + + static func setCurrentDelegate(_ delegate: NMFMapViewDelegate?, to object: NMFMapView) { + object.delegate = delegate + } + + // 네이버맵의 Delegate 메서드를 Rx로 전달 + func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) { + didChangePositionSubject.onNext(()) + _forwardToDelegate?.mapView?(mapView, cameraDidChangeByReason: reason, animated: animated) + } + + func mapViewIdle(_ mapView: NMFMapView) { + idleAtPositionSubject.onNext(()) + forwardToDelegate()?.mapViewIdle?(mapView) + } +} + +extension Reactive where Base: NMFMapView { + var delegate: DelegateProxy { + return NMFMapViewDelegateProxy.proxy(for: base) + } + + var didChangePosition: Observable { + let proxy = NMFMapViewDelegateProxy.proxy(for: base) + return proxy.didChangePositionSubject.asObservable() + } + + var idleAtPosition: Observable { + let proxy = NMFMapViewDelegateProxy.proxy(for: base) + return proxy.idleAtPositionSubject.asObservable() + } +} diff --git a/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift b/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift index 4f46b43e..8de0e178 100644 --- a/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift +++ b/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift @@ -1,23 +1,23 @@ -import CoreLocation +import NMapsMap struct RegionCoordinate { - static let seoul = CLLocationCoordinate2D(latitude: 37.5665, longitude: 126.9780) - static let gyeonggi = CLLocationCoordinate2D(latitude: 37.4138, longitude: 127.5183) - static let incheon = CLLocationCoordinate2D(latitude: 37.4563, longitude: 126.7052) - static let daejeon = CLLocationCoordinate2D(latitude: 36.3504, longitude: 127.3845) - static let gwangju = CLLocationCoordinate2D(latitude: 35.1595, longitude: 126.8526) - static let daegu = CLLocationCoordinate2D(latitude: 35.8714, longitude: 128.6014) - static let busan = CLLocationCoordinate2D(latitude: 35.1796, longitude: 129.0756) - static let ulsan = CLLocationCoordinate2D(latitude: 35.5384, longitude: 129.3114) - static let chungbuk = CLLocationCoordinate2D(latitude: 36.6357, longitude: 127.4914) - static let chungnam = CLLocationCoordinate2D(latitude: 36.6588, longitude: 126.6728) - static let sejong = CLLocationCoordinate2D(latitude: 36.4801, longitude: 127.2892) - static let jeonbuk = CLLocationCoordinate2D(latitude: 35.7175, longitude: 127.1530) - static let jeonnam = CLLocationCoordinate2D(latitude: 34.8679, longitude: 126.9910) - static let gyeongbuk = CLLocationCoordinate2D(latitude: 36.4919, longitude: 128.8889) - static let gyeongnam = CLLocationCoordinate2D(latitude: 35.4606, longitude: 128.2132) - static let gangwon = CLLocationCoordinate2D(latitude: 37.8228, longitude: 128.1555) - static let jeju = CLLocationCoordinate2D(latitude: 33.4890, longitude: 126.4983) + static let seoul = NMGLatLng(lat: 37.5665, lng: 126.9780) + static let gyeonggi = NMGLatLng(lat: 37.4138, lng: 127.5183) + static let incheon = NMGLatLng(lat: 37.4563, lng: 126.7052) + static let daejeon = NMGLatLng(lat: 36.3504, lng: 127.3845) + static let gwangju = NMGLatLng(lat: 35.1595, lng: 126.8526) + static let daegu = NMGLatLng(lat: 35.8714, lng: 128.6014) + static let busan = NMGLatLng(lat: 35.1796, lng: 129.0756) + static let ulsan = NMGLatLng(lat: 35.5384, lng: 129.3114) + static let chungbuk = NMGLatLng(lat: 36.6357, lng: 127.4914) + static let chungnam = NMGLatLng(lat: 36.6588, lng: 126.6728) + static let sejong = NMGLatLng(lat: 36.4801, lng: 127.2892) + static let jeonbuk = NMGLatLng(lat: 35.7175, lng: 127.1530) + static let jeonnam = NMGLatLng(lat: 34.8679, lng: 126.9910) + static let gyeongbuk = NMGLatLng(lat: 36.4919, lng: 128.8889) + static let gyeongnam = NMGLatLng(lat: 35.4606, lng: 128.2132) + static let gangwon = NMGLatLng(lat: 37.8228, lng: 128.1555) + static let jeju = NMGLatLng(lat: 33.4890, lng: 126.4983) } enum RegionType { @@ -26,241 +26,241 @@ enum RegionType { case metropolitan case province -} - -struct RegionDefinitions { - // 서울 클러스터 - static let seoulClusters: [RegionCluster] = [ - RegionCluster( - name: "도봉/노원/강북/중랑", - subRegions: ["도봉구", "노원구", "강북구", "중랑구"], - coordinate: CLLocationCoordinate2D(latitude: 37.6494, longitude: 127.0510), - type: .seoul - ), - RegionCluster( - name: "동대문/성북", - subRegions: ["동대문구", "성북구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5894, longitude: 127.0435), - type: .seoul - ), - RegionCluster( - name: "중구/종로", - subRegions: ["중구", "종로구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5738, longitude: 126.9861), - type: .seoul - ), - RegionCluster( - name: "성동/광진", - subRegions: ["성동구", "광진구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5509, longitude: 127.0403), - type: .seoul - ), - RegionCluster( - name: "송파/강동", - subRegions: ["송파구", "강동구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5145, longitude: 127.1058), - type: .seoul - ), - RegionCluster( - name: "동작/관악", - subRegions: ["동작구", "관악구"], - coordinate: CLLocationCoordinate2D(latitude: 37.4959, longitude: 126.9410), - type: .seoul - ), - RegionCluster( - name: "서초/강남", - subRegions: ["서초구", "강남구"], - coordinate: CLLocationCoordinate2D(latitude: 37.4959, longitude: 127.0664), - type: .seoul - ), - RegionCluster( - name: "은평/서대문/마포", - subRegions: ["은평구", "서대문구", "마포구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5744, longitude: 126.9185), - type: .seoul - ), - RegionCluster( - name: "영등포/구로", - subRegions: ["영등포구", "구로구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5162, longitude: 126.8968), - type: .seoul - ), - RegionCluster( - name: "용산", - subRegions: ["용산구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5384, longitude: 126.9654), - type: .seoul - ), - RegionCluster( - name: "양천/강서/금천", - subRegions: ["양천구", "강서구", "금천구"], - coordinate: CLLocationCoordinate2D(latitude: 37.5509, longitude: 126.8553), - type: .seoul - ) - ] + struct RegionDefinitions { + // 서울 클러스터 + static let seoulClusters: [RegionCluster] = [ + RegionCluster( + name: "도봉/노원/강북/중랑", + subRegions: ["도봉구", "노원구", "강북구", "중랑구"], + coordinate: NMGLatLng(lat: 37.6494, lng: 127.0510), + type: .seoul + ), + RegionCluster( + name: "동대문/성북", + subRegions: ["동대문구", "성북구"], + coordinate: NMGLatLng(lat: 37.5894, lng: 127.0435), + type: .seoul + ), + RegionCluster( + name: "중구/종로", + subRegions: ["중구", "종로구"], + coordinate: NMGLatLng(lat: 37.5738, lng: 126.9861), + type: .seoul + ), + RegionCluster( + name: "성동/광진", + subRegions: ["성동구", "광진구"], + coordinate: NMGLatLng(lat: 37.5509, lng: 127.0403), + type: .seoul + ), + RegionCluster( + name: "송파/강동", + subRegions: ["송파구", "강동구"], + coordinate: NMGLatLng(lat: 37.5145, lng: 127.1058), + type: .seoul + ), + RegionCluster( + name: "동작/관악", + subRegions: ["동작구", "관악구"], + coordinate: NMGLatLng(lat: 37.4959, lng: 126.9410), + type: .seoul + ), + RegionCluster( + name: "서초/강남", + subRegions: ["서초구", "강남구"], + coordinate: NMGLatLng(lat: 37.4959, lng: 127.0664), + type: .seoul + ), + RegionCluster( + name: "은평/서대문/마포", + subRegions: ["은평구", "서대문구", "마포구"], + coordinate: NMGLatLng(lat: 37.5744, lng: 126.9185), + type: .seoul + ), + RegionCluster( + name: "영등포/구로", + subRegions: ["영등포구", "구로구"], + coordinate: NMGLatLng(lat: 37.5162, lng: 126.8968), + type: .seoul + ), + RegionCluster( + name: "용산", + subRegions: ["용산구"], + coordinate: NMGLatLng(lat: 37.5384, lng: 126.9654), + type: .seoul + ), + RegionCluster( + name: "양천/강서/금천", + subRegions: ["양천구", "강서구", "금천구"], + coordinate: NMGLatLng(lat: 37.5509, lng: 126.8553), + type: .seoul + ) + ] - // 경기도 클러스터 - static let gyeonggiClusters: [RegionCluster] = [ - RegionCluster( - name: "포천/연천/동두천/양주", - subRegions: ["포천시", "연천군", "동두천시", "양주시"], - coordinate: CLLocationCoordinate2D(latitude: 37.8859, longitude: 127.0543), - type: .gyeonggi - ), - RegionCluster( - name: "의정부/구리/남양주", - subRegions: ["의정부시", "구리시", "남양주시"], - coordinate: CLLocationCoordinate2D(latitude: 37.7358, longitude: 127.1422), - type: .gyeonggi - ), - RegionCluster( - name: "파주/고양/가평", - subRegions: ["파주시", "고양시", "가평군"], - coordinate: CLLocationCoordinate2D(latitude: 37.7599, longitude: 126.7762), - type: .gyeonggi - ), - RegionCluster( - name: "용인/화성/수원", - subRegions: ["용인시", "화성시", "수원시"], - coordinate: CLLocationCoordinate2D(latitude: 37.2911, longitude: 127.0876), - type: .gyeonggi - ), - RegionCluster( - name: "군포/의왕/과천/안양", - subRegions: ["군포시", "의왕시", "과천시", "안양시"], - coordinate: CLLocationCoordinate2D(latitude: 37.3956, longitude: 126.9477), - type: .gyeonggi - ), - RegionCluster( - name: "부천/광명/시흥/안산", - subRegions: ["부천시", "광명시", "시흥시", "안산시"], - coordinate: CLLocationCoordinate2D(latitude: 37.4563, longitude: 126.8040), - type: .gyeonggi - ), - RegionCluster( - name: "안성/평택/오산", - subRegions: ["안성시", "평택시", "오산시"], - coordinate: CLLocationCoordinate2D(latitude: 37.0042, longitude: 127.2003), - type: .gyeonggi - ), - RegionCluster( - name: "여주/양평/광주/이천", - subRegions: ["여주시", "양평군", "광주시", "이천시"], - coordinate: CLLocationCoordinate2D(latitude: 37.2958, longitude: 127.5986), - type: .gyeonggi - ), - RegionCluster( - name: "김포", - subRegions: ["김포시"], - coordinate: CLLocationCoordinate2D(latitude: 37.6153, longitude: 126.7164), - type: .gyeonggi - ), - RegionCluster( - name: "성남/하남", - subRegions: ["성남시", "하남시"], - coordinate: CLLocationCoordinate2D(latitude: 37.4517, longitude: 127.1486), - type: .gyeonggi - ) - ] + // 경기도 클러스터 + static let gyeonggiClusters: [RegionCluster] = [ + RegionCluster( + name: "포천/연천/동두천/양주", + subRegions: ["포천시", "연천군", "동두천시", "양주시"], + coordinate: NMGLatLng(lat: 37.8859, lng: 127.0543), + type: .gyeonggi + ), + RegionCluster( + name: "의정부/구리/남양주", + subRegions: ["의정부시", "구리시", "남양주시"], + coordinate: NMGLatLng(lat: 37.7358, lng: 127.1422), + type: .gyeonggi + ), + RegionCluster( + name: "파주/고양/가평", + subRegions: ["파주시", "고양시", "가평군"], + coordinate: NMGLatLng(lat: 37.7599, lng: 126.7762), + type: .gyeonggi + ), + RegionCluster( + name: "용인/화성/수원", + subRegions: ["용인시", "화성시", "수원시"], + coordinate: NMGLatLng(lat: 37.2911, lng: 127.0876), + type: .gyeonggi + ), + RegionCluster( + name: "군포/의왕/과천/안양", + subRegions: ["군포시", "의왕시", "과천시", "안양시"], + coordinate: NMGLatLng(lat: 37.3956, lng: 126.9477), + type: .gyeonggi + ), + RegionCluster( + name: "부천/광명/시흥/안산", + subRegions: ["부천시", "광명시", "시흥시", "안산시"], + coordinate: NMGLatLng(lat: 37.4563, lng: 126.8040), + type: .gyeonggi + ), + RegionCluster( + name: "안성/평택/오산", + subRegions: ["안성시", "평택시", "오산시"], + coordinate: NMGLatLng(lat: 37.0042, lng: 127.2003), + type: .gyeonggi + ), + RegionCluster( + name: "여주/양평/광주/이천", + subRegions: ["여주시", "양평군", "광주시", "이천시"], + coordinate: NMGLatLng(lat: 37.2958, lng: 127.5986), + type: .gyeonggi + ), + RegionCluster( + name: "김포", + subRegions: ["김포시"], + coordinate: NMGLatLng(lat: 37.6153, lng: 126.7164), + type: .gyeonggi + ), + RegionCluster( + name: "성남/하남", + subRegions: ["성남시", "하남시"], + coordinate: NMGLatLng(lat: 37.4517, lng: 127.1486), + type: .gyeonggi + ) + ] - // 광역시 및 기타 지역 - static let metropolitanClusters: [RegionCluster] = [ - RegionCluster( - name: "인천", - subRegions: ["인천광역시"], - coordinate: CLLocationCoordinate2D(latitude: 37.4563, longitude: 126.7052), - type: .metropolitan - ), - RegionCluster( - name: "대전", - subRegions: ["대전광역시"], - coordinate: CLLocationCoordinate2D(latitude: 36.3504, longitude: 127.3845), - type: .metropolitan - ), - RegionCluster( - name: "광주", - subRegions: ["광주광역시"], - coordinate: CLLocationCoordinate2D(latitude: 35.1595, longitude: 126.8526), - type: .metropolitan - ), - RegionCluster( - name: "대구", - subRegions: ["대구광역시"], - coordinate: CLLocationCoordinate2D(latitude: 35.8714, longitude: 128.6014), - type: .metropolitan - ), - RegionCluster( - name: "부산", - subRegions: ["부산광역시"], - coordinate: CLLocationCoordinate2D(latitude: 35.1796, longitude: 129.0756), - type: .metropolitan - ), - RegionCluster( - name: "울산", - subRegions: ["울산광역시"], - coordinate: CLLocationCoordinate2D(latitude: 35.5384, longitude: 129.3114), - type: .metropolitan - ) - ] + // 광역시 클러스터 + static let metropolitanClusters: [RegionCluster] = [ + RegionCluster( + name: "인천", + subRegions: ["인천광역시"], + coordinate: NMGLatLng(lat: 37.4563, lng: 126.7052), + type: .metropolitan + ), + RegionCluster( + name: "대전", + subRegions: ["대전광역시"], + coordinate: NMGLatLng(lat: 36.3504, lng: 127.3845), + type: .metropolitan + ), + RegionCluster( + name: "광주", + subRegions: ["광주광역시"], + coordinate: NMGLatLng(lat: 35.1595, lng: 126.8526), + type: .metropolitan + ), + RegionCluster( + name: "대구", + subRegions: ["대구광역시"], + coordinate: NMGLatLng(lat: 35.8714, lng: 128.6014), + type: .metropolitan + ), + RegionCluster( + name: "부산", + subRegions: ["부산광역시"], + coordinate: NMGLatLng(lat: 35.1796, lng: 129.0756), + type: .metropolitan + ), + RegionCluster( + name: "울산", + subRegions: ["울산광역시"], + coordinate: NMGLatLng(lat: 35.5384, lng: 129.3114), + type: .metropolitan + ) + ] - static let provinceClusters: [RegionCluster] = [ - RegionCluster( - name: "충북", - subRegions: ["충청북도"], - coordinate: CLLocationCoordinate2D(latitude: 36.6357, longitude: 127.4914), - type: .province - ), - RegionCluster( - name: "충남", - subRegions: ["충청남도"], - coordinate: CLLocationCoordinate2D(latitude: 36.6588, longitude: 126.6728), - type: .province - ), - RegionCluster( - name: "세종", - subRegions: ["세종특별자치시"], - coordinate: CLLocationCoordinate2D(latitude: 36.4801, longitude: 127.2892), - type: .province - ), - RegionCluster( - name: "전북", - subRegions: ["전라북도"], - coordinate: CLLocationCoordinate2D(latitude: 35.7175, longitude: 127.1530), - type: .province - ), - RegionCluster( - name: "전남", - subRegions: ["전라남도"], - coordinate: CLLocationCoordinate2D(latitude: 34.8679, longitude: 126.9910), - type: .province - ), - RegionCluster( - name: "경북", - subRegions: ["경상북도"], - coordinate: CLLocationCoordinate2D(latitude: 36.4919, longitude: 128.8889), - type: .province - ), - RegionCluster( - name: "경남", - subRegions: ["경상남도"], - coordinate: CLLocationCoordinate2D(latitude: 35.4606, longitude: 128.2132), - type: .province - ), - RegionCluster( - name: "강원", - subRegions: ["강원도"], - coordinate: CLLocationCoordinate2D(latitude: 37.8228, longitude: 128.1555), - type: .province - ), - RegionCluster( - name: "제주", - subRegions: ["제주특별자치도"], - coordinate: CLLocationCoordinate2D(latitude: 33.4890, longitude: 126.4983), - type: .province - ) - ] + // 도 클러스터 + static let provinceClusters: [RegionCluster] = [ + RegionCluster( + name: "충북", + subRegions: ["충청북도"], + coordinate: NMGLatLng(lat: 36.6357, lng: 127.4914), + type: .province + ), + RegionCluster( + name: "충남", + subRegions: ["충청남도"], + coordinate: NMGLatLng(lat: 36.6588, lng: 126.6728), + type: .province + ), + RegionCluster( + name: "세종", + subRegions: ["세종특별자치시"], + coordinate: NMGLatLng(lat: 36.4801, lng: 127.2892), + type: .province + ), + RegionCluster( + name: "전북", + subRegions: ["전라북도"], + coordinate: NMGLatLng(lat: 35.7175, lng: 127.1530), + type: .province + ), + RegionCluster( + name: "전남", + subRegions: ["전라남도"], + coordinate: NMGLatLng(lat: 34.8679, lng: 126.9910), + type: .province + ), + RegionCluster( + name: "경북", + subRegions: ["경상북도"], + coordinate: NMGLatLng(lat: 36.4919, lng: 128.8889), + type: .province + ), + RegionCluster( + name: "경남", + subRegions: ["경상남도"], + coordinate: NMGLatLng(lat: 35.4606, lng: 128.2132), + type: .province + ), + RegionCluster( + name: "강원", + subRegions: ["강원도"], + coordinate: NMGLatLng(lat: 37.8228, lng: 128.1555), + type: .province + ), + RegionCluster( + name: "제주", + subRegions: ["제주특별자치도"], + coordinate: NMGLatLng(lat: 33.4890, lng: 126.4983), + type: .province + ) + ] - static var allClusters: [RegionCluster] { - seoulClusters + gyeonggiClusters + metropolitanClusters + provinceClusters + static var allClusters: [RegionCluster] { + seoulClusters + gyeonggiClusters + metropolitanClusters + provinceClusters + } } } diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 690afbd7..1cf7084a 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,186 +1,153 @@ -import Foundation import UIKit +import SnapKit import RxSwift -import ReactorKit +import RxCocoa +import NMapsMap import CoreLocation -import GoogleMaps -final class FullScreenMapViewController: MapViewController { - var selectedStore: MapPopUpStore? - var shouldAutoSelectNearestStore = false // 일반 모드와 다르게 false로 설정 +class FullScreenMapViewController: MapViewController { + // MARK: - Properties + private var initialStore: MapPopUpStore? + // MARK: - Initialization + init(store: MapPopUpStore?) { + self.initialStore = store + super.init() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() + setupFullScreenUI() + configureInitialMapPosition() + + // 풀스크린 모드에서 별도 터치 델리게이트 설정 + mainView.mapView.touchDelegate = self + } - self.navigationController?.navigationBar.isHidden = false - setupNavigation() + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.setNavigationBarHidden(false, animated: false) + tabBarController?.tabBar.isHidden = true + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.setNavigationBarHidden(false, animated: false) + tabBarController?.tabBar.isHidden = false + navigationItem.title = "찾아가는 길" + } - mainView.searchFilterContainer.isHidden = true + // MARK: - Setup + private func setupFullScreenUI() { mainView.filterChips.isHidden = true mainView.listButton.isHidden = true + mainView.locationButton.isHidden = true + mainView.searchInput.isHidden = true carouselView.isHidden = false - // 지도 델리게이트 재설정 - mainView.mapView.delegate = self + // 닫기 버튼 추가 + let closeButton = UIButton(type: .system) + closeButton.setImage(UIImage(systemName: "xmark"), for: .normal) + closeButton.tintColor = .black + view.addSubview(closeButton) - // 선택된 스토어가 있다면 즉시 마커 탭 처리 (요구사항 1) - if let store = selectedStore { - updateUI(for: store) + closeButton.snp.makeConstraints { make in + make.top.equalTo(view.safeAreaLayoutGuide).offset(16) + make.trailing.equalToSuperview().offset(-16) + make.width.height.equalTo(44) } - } - - - // MARK: - Binding - override func bind(reactor: Reactor) { - super.bind(reactor: reactor) - - // [변경] 기존 viewportStores 관련 바인딩은 풀스크린에서 marker tap 처리와 충돌할 수 있으므로 주석처리하거나 제거 - /* - reactor.state - .map { $0.viewportStores } - .distinctUntilChanged() - .debounce(.milliseconds(300), scheduler: MainScheduler.instance) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] stores in - self?.currentStores = stores - self?.updateMapWithClustering() - }) - .disposed(by: disposeBag) - */ - - // searchResult나 selectedStore 변경시에만 UI 업데이트 (요구사항 1) - reactor.state - .map { $0.selectedStore ?? $0.searchResult } - .distinctUntilChanged { $0?.id == $1?.id } - .compactMap { $0 } - .filter { [weak self] store in - // 현재 선택된 스토어와 다른 경우에만 업데이트 - self?.selectedStore?.id != store.id - } - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] store in - self?.updateUI(for: store) + closeButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.dismiss(animated: true, completion: nil) }) - .disposed(by: disposeBag) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - self.navigationController?.navigationBar.isHidden = false - } + .disposed(by: disposeBag) // 상위 클래스의 disposeBag 사용 - // MARK: - Map Delegate Methods - override func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { - // (1) 구/시 단위 클러스터 - if let clusterData = marker.userData as? ClusterMarkerData { - return handleRegionalClusterTap(marker, clusterData: clusterData) + mainView.mapView.snp.remakeConstraints { make in + make.edges.equalToSuperview() } - // (2) 동일 좌표 마이크로 클러스터 - else if let storeArray = marker.userData as? [MapPopUpStore] { - if storeArray.count > 1 { - return handleMicroClusterTap(marker, storeArray: storeArray) - } else if let singleStore = storeArray.first { - return handleSingleStoreTap(marker, store: singleStore) - } - } - // (3) 단일 스토어 - else if let singleStore = marker.userData as? MapPopUpStore { - return handleSingleStoreTap(marker, store: singleStore) + + carouselView.snp.remakeConstraints { make in + make.leading.trailing.equalToSuperview() + make.height.equalTo(140) + make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) } - return false } - override func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { - // 카메라 이동 중 별도 처리 없음 - } + private func configureInitialMapPosition() { + guard let store = initialStore else { return } + + let position = NMGLatLng(lat: store.latitude, lng: store.longitude) + let cameraUpdate = NMFCameraUpdate(scrollTo: position, zoomTo: 15.0) + mainView.mapView.moveCamera(cameraUpdate) + + // 여기서 마커를 선택 상태로 추가 + let marker = NMFMarker() + marker.position = position + marker.userInfo = ["storeData": store] + updateMarkerStyle(marker: marker, selected: true, isCluster: false) // 선택된 스타일 적용 + marker.mapView = mainView.mapView + currentMarker = marker - override func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { - // 지도 빈 공간 탭은 무시 + // 캐러셀 설정 + currentCarouselStores = [store] + carouselView.updateCards([store]) + carouselView.isHidden = false } - private func findMarkerForStore(for store: MapPopUpStore) -> GMSMarker? { - if let marker = self.currentMarker, - let markerStore = marker.userData as? MapPopUpStore, - markerStore.id == store.id { - return marker - } - return nil + // MARK: - Override Methods + override func bind(reactor: MapReactor) { + super.bind(reactor: reactor) + // 풀스크린 모드에서 추가 바인딩 필요 시 여기에 작성 } - /// 선택된 스토어 정보를 기반으로 마커, 카메라, 캐러셀을 업데이트 (요구사항 1) - private func updateUI(for store: MapPopUpStore) { - // 기존 마커 제거 - mainView.mapView.clear() - - // 새 마커 생성 - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - // 마커 뷰 생성 및 선택 상태 주입 - let selectedInput = MapMarker.Input( - isSelected: true, - isCluster: false, - regionName: "", - count: 1, - isMultiMarker: false - ) - let markerView = MapMarker() - markerView.injection(with: selectedInput) - marker.iconView = markerView - - // 마커를 지도에 추가 - marker.map = mainView.mapView - currentMarker = marker + override func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { + isMovingToMarker = true - mainView.mapView.selectedMarker = marker + if let previousMarker = currentMarker, previousMarker != marker { + updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) + } - // 카메라 이동 - let camera = GMSCameraPosition.camera( - withLatitude: store.latitude, - longitude: store.longitude, - zoom: 16 - ) - mainView.mapView.animate(to: camera) + updateMarkerStyle(marker: marker, selected: true, isCluster: false) + currentMarker = marker - // 캐러셀 업데이트 - carouselView.updateCards([store]) currentCarouselStores = [store] + carouselView.updateCards([store]) carouselView.isHidden = false + mainView.setStoreCardHidden(false, animated: true) - // 약간의 딜레이 후 마커 뷰 재갱신 (필요 시) - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { - markerView.injection(with: selectedInput) - markerView.setNeedsLayout() - markerView.layoutIfNeeded() - } - } + let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: 15.0) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + mainView.mapView.moveCamera(cameraUpdate) + isMovingToMarker = false + return true + } - @objc private func backButtonTapped() { - dismiss(animated: true) + override func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { + return false // 풀스크린에서는 클러스터 비활성화 } - private func setupNavigation() { - navigationItem.title = "찾아가는 길" - let appearance = UINavigationBarAppearance() - appearance.configureWithOpaqueBackground() - appearance.shadowColor = .clear - appearance.backgroundColor = .white - appearance.titleTextAttributes = [ - .foregroundColor: UIColor.black, - .font: UIFont.systemFont(ofSize: 15, weight: .regular) - ] - navigationController?.navigationBar.standardAppearance = appearance - navigationController?.navigationBar.scrollEdgeAppearance = appearance - navigationItem.leftBarButtonItem = UIBarButtonItem( - image: UIImage(named: "bakcbutton")?.withRenderingMode(.alwaysOriginal), - style: .plain, - target: self, - action: #selector(backButtonTapped) - ) - navigationItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + override func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { + return false // 풀스크린에서는 마이크로 클러스터 비활성화 } } + +// MARK: - NMFMapViewTouchDelegate +//extension FullScreenMapViewController: NMFMapViewTouchDelegate { +// func mapView(_ mapView: NMFMapView, didTapOverlay overlay: NMFOverlay) -> Bool { +// guard let marker = overlay as? NMFMarker, +// let store = marker.userInfo["storeData"] as? MapPopUpStore else { return false } +// return handleSingleStoreTap(marker, store: store) +// } +// +// func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { +// // 풀스크린 모드에서는 지도 탭 시 동작 없음 +// } +//} diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift new file mode 100644 index 00000000..aab14ce8 --- /dev/null +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift @@ -0,0 +1,71 @@ +import UIKit +import CoreLocation + +enum MapAppType { + case naver + case kakao + case apple + + static func from(string: String) -> MapAppType? { + switch string.lowercased() { + case "naver": + return .naver + case "kakao": + return .kakao + case "apple", "applemap": + return .apple + default: + return nil + } + } + + func urlScheme(coordinate: CLLocationCoordinate2D, name: String, address: String) -> String { + let encodedName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" + let encodedAddress = address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" + + switch self { + case .naver: + return "nmap://place?lat=\(coordinate.latitude)&lng=\(coordinate.longitude)&name=\(encodedName)&addr=\(encodedAddress)&appname=com.poppool.app" + case .kakao: + return "kakaomap://look?p=\(coordinate.latitude),\(coordinate.longitude)" + case .apple: + return "maps://?q=\(encodedName)&ll=\(coordinate.latitude),\(coordinate.longitude)&z=16" + } + } + + var appStoreURL: String { + switch self { + case .naver: + return "https://apps.apple.com/kr/app/id311867728" + case .kakao: + return "https://apps.apple.com/kr/app/id304608425" + case .apple: + return "https://apps.apple.com/kr/app/id1108185179" + } + } +} + +class MapAppService { + static func openMapApp(_ appTypeString: String, coordinate: CLLocationCoordinate2D, name: String, address: String) -> Observable { + guard let appType = MapAppType.from(string: appTypeString) else { + return Observable.just("지원하지 않는 맵 앱입니다.") + } + + let urlScheme = appType.urlScheme(coordinate: coordinate, name: name, address: address) + + Logger.log(message: "🗺 맵 앱 열기 시도: \(urlScheme)", category: .debug) + + if let url = URL(string: urlScheme), UIApplication.shared.canOpenURL(url) { + Logger.log(message: "✅ \(appType) 앱 실행", category: .debug) + UIApplication.shared.open(url, options: [:], completionHandler: nil) + return Observable.empty() + } else { + Logger.log(message: "❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) + if let appStoreURL = URL(string: appType.appStoreURL) { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) + return Observable.just("\(appTypeString) 앱이 설치되어 있지 않아 앱스토어로 이동합니다.") + } + return Observable.just("앱을 열 수 없습니다.") + } + } +} diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index c07ed2b8..12c65b08 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -1,15 +1,15 @@ import UIKit import SnapKit -import GoogleMaps import ReactorKit import RxSwift import CoreLocation +import NMapsMap final class MapGuideViewController: UIViewController, View { // MARK: - Properties var disposeBag = DisposeBag() private let popUpStoreId: Int64 - private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 + private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 init(popUpStoreId: Int64) { self.popUpStoreId = popUpStoreId @@ -54,16 +54,14 @@ final class MapGuideViewController: UIViewController, View { return btn }() - private let mapView: GMSMapView = { - let map = GMSMapView() - map.isMyLocationEnabled = false + private let mapView: NMFMapView = { + let map = NMFMapView() map.layer.borderWidth = 1 map.layer.borderColor = UIColor.g100.cgColor map.layer.cornerRadius = 12 return map }() - /// 지도 우상단 "Expandable" 버튼 private let expandButton: UIButton = { let btn = UIButton() btn.setImage(UIImage(named: "Expandable"), for: .normal) @@ -102,9 +100,9 @@ final class MapGuideViewController: UIViewController, View { return btn }() - private let appleButton: UIButton = { + private let tmapButton: UIButton = { let btn = UIButton() - btn.setImage(UIImage(named: "AppleMap"), for: .normal) + btn.setImage(UIImage(named: "TMap"), for: .normal) btn.layer.cornerRadius = 24 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.g100.cgColor @@ -140,14 +138,16 @@ final class MapGuideViewController: UIViewController, View { .map { Reactor.Action.openMapApp("kakao") } .bind(to: reactor.action) .disposed(by: disposeBag) - appleButton.rx.tap - .map { Reactor.Action.openMapApp("apple") } + tmapButton.rx.tap + .map { Reactor.Action.openMapApp("tmap") } .bind(to: reactor.action) .disposed(by: disposeBag) expandButton.rx.tap + .throttle(.milliseconds(300), scheduler: MainScheduler.instance) .subscribe(onNext: { [weak self] in guard let self = self else { return } + let provider = ProviderImpl() let useCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) let directionRepository = DefaultMapDirectionRepository(provider: provider) @@ -155,10 +155,9 @@ final class MapGuideViewController: UIViewController, View { if let selectedStore = self.currentCarouselStores.first { reactor.action.onNext(.didSelectItem(selectedStore)) - reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) - let fullScreenMapVC = FullScreenMapViewController() - fullScreenMapVC.selectedStore = selectedStore // 직접 주입 + // store: 매개변수명 사용 + let fullScreenMapVC = FullScreenMapViewController(store: selectedStore) fullScreenMapVC.reactor = reactor let nav = UINavigationController(rootViewController: fullScreenMapVC) @@ -166,24 +165,28 @@ final class MapGuideViewController: UIViewController, View { self.present(nav, animated: true) } else { reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) + reactor.state .map { $0.searchResult } .distinctUntilChanged() .compactMap { $0 } .take(1) - .subscribe(onNext: { [weak self] store in - let fullScreenMapVC = FullScreenMapViewController() + .subscribe(onNext: { store in + // store: 매개변수명 사용 + let fullScreenMapVC = FullScreenMapViewController(store: store) fullScreenMapVC.reactor = reactor let nav = UINavigationController(rootViewController: fullScreenMapVC) nav.modalPresentationStyle = .fullScreen - self?.present(nav, animated: true) + self.present(nav, animated: true) }) .disposed(by: self.disposeBag) } }) .disposed(by: disposeBag) + + // 목적지 좌표로 마커 및 카메라 설정 reactor.state .map { $0.destinationCoordinate } .compactMap { $0 } @@ -192,6 +195,17 @@ final class MapGuideViewController: UIViewController, View { }) .disposed(by: disposeBag) + // searchResult로 currentCarouselStores 업데이트 + reactor.state + .map { $0.searchResult } + .distinctUntilChanged() + .compactMap { $0 } + .subscribe(onNext: { [weak self] store in + self?.currentCarouselStores = [store] + }) + .disposed(by: disposeBag) + + // Dismiss 처리 reactor.state .map { $0.shouldDismiss } .distinctUntilChanged() @@ -204,8 +218,7 @@ final class MapGuideViewController: UIViewController, View { // MARK: - UI Setup private func setupUI() { - view.backgroundColor = .clear - + view.backgroundColor = .white view.addSubview(dimmingView) dimmingView.snp.makeConstraints { $0.edges.equalToSuperview() } @@ -252,7 +265,7 @@ final class MapGuideViewController: UIViewController, View { } let bottomContainer = UIView() - modalCardView.addSubview(bottomContainer) + modalCardView.addSubview(bottomContainer) // 오타 수정 필요: bottomContainer로 변경 bottomContainer.snp.makeConstraints { make in make.top.equalTo(mapView.snp.bottom).offset(20) make.leading.trailing.equalToSuperview().inset(20) @@ -266,18 +279,17 @@ final class MapGuideViewController: UIViewController, View { make.centerY.equalToSuperview() } - // 티맵 버튼 제거, 네이버/카카오/애플맵만 포함 - let appStack = UIStackView(arrangedSubviews: [naverButton, kakaoButton, appleButton]) + let appStack = UIStackView(arrangedSubviews: [naverButton, kakaoButton, tmapButton]) appStack.axis = .horizontal appStack.alignment = .center - appStack.spacing = 16 // 버튼이 3개로 줄어 간격 다시 늘림 + appStack.spacing = 16 appStack.distribution = .fillEqually bottomContainer.addSubview(appStack) appStack.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() - [naverButton, kakaoButton, appleButton].forEach { button in + [naverButton, kakaoButton, tmapButton].forEach { button in button.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 48, height: 48)) } @@ -300,37 +312,38 @@ final class MapGuideViewController: UIViewController, View { } private func setupMarker(at coordinate: CLLocationCoordinate2D) { - // 새 마커 생성 및 설정 - let marker = GMSMarker() - marker.position = coordinate - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - marker.appearAnimation = .none + // 기존 마커 제거 + mapView.subviews.forEach { if $0 is NMFMarker { $0.removeFromSuperview() } } - let markerView = MapMarker() - markerView.injection(with: .init(isSelected: true)) - marker.iconView = markerView - - // 카메라 위치 설정 - let camera = GMSCameraPosition(target: coordinate, zoom: 16) - - // 애니메이션과 마커 변경을 하나의 트랜잭션으로 처리 - CATransaction.begin() - CATransaction.setDisableActions(true) - - // 카메라 이동과 마커 설정을 동시에 처리 - mapView.animate(to: camera) - marker.map = mapView + // 새 마커 생성 및 설정 + let marker = NMFMarker() + marker.position = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) + marker.iconImage = NMFOverlayImage(name: "TapMarker") // MapViewController에서 사용하는 기본 마커 + marker.width = 32 + marker.height = 32 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + + // 먼저 마커를 지도에 추가 + marker.mapView = mapView + + // 그 다음 카메라 위치 설정 + let cameraUpdate = NMFCameraUpdate( + scrollTo: NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude), + zoomTo: 15.0 + ) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + mapView.moveCamera(cameraUpdate) - CATransaction.commit() } private func dismissModalCard() { UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn) { -// self.dimmingView.alpha = 0 + self.dimmingView.alpha = 0 self.modalCardBottomConstraint?.update(offset: 408) self.view.layoutIfNeeded() } completion: { _ in - self.navigationController?.popViewController(animated: false) + self.dismiss(animated: false) } } } diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift index a90eb377..f15cc71d 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift @@ -1,13 +1,12 @@ import UIKit import SnapKit -import GoogleMaps +import NMapsMap final class MapMarker: UIView { // MARK: - Components private(set) var isSelected: Bool = false var currentInput: Input? - private let markerImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "Marker") @@ -181,8 +180,6 @@ extension MapMarker: Inputable { CATransaction.commit() } - - private func setupClusterMarker(_ input: Input) { markerImageView.isHidden = true clusterContainer.isHidden = false @@ -223,7 +220,21 @@ extension MapMarker { var imageView: UIImageView { return markerImageView } + + /// 네이버맵용으로 뷰를 UIImage로 렌더링하는 함수 + func asImage() -> UIImage? { + // 필요한 경우 프레임을 강제로 업데이트합니다. + self.layoutIfNeeded() + UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale) + defer { UIGraphicsEndImageContext() } + if let context = UIGraphicsGetCurrentContext() { + self.layer.render(in: context) + return UIGraphicsGetImageFromCurrentImageContext() + } + return nil + } } + extension MapMarker.Input: Equatable { static func == (lhs: MapMarker.Input, rhs: MapMarker.Input) -> Bool { return lhs.isSelected == rhs.isSelected && diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift index 8603c691..f2f6143e 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift @@ -15,6 +15,7 @@ final class MapReactor: Reactor { case fetchCategories case updateBothFilters(locations: [String], categories: [String]) // 새로 추가 case didSelectItem(MapPopUpStore) + case fetchAllStores case refreshMarkers(northEastLat: Double, northEastLon: Double, southWestLat: Double, southWestLon: Double) case viewportChanged( northEastLat: Double, @@ -298,6 +299,43 @@ final class MapReactor: Reactor { ) return .setSearchResult(store) } + case .fetchAllStores: + // 한국 전체 영역에 대한 바운드 설정 + let koreaRegion = ( + northEast: (lat: 38.0, lon: 132.0), + southWest: (lat: 33.0, lon: 124.0) + ) + + let categoryIDs = currentState.selectedCategoryFilters + .compactMap { currentState.categoryMapping[$0] } + + return .concat([ + .just(.setLoading(true)), + useCase.fetchStoresInBounds( + northEastLat: koreaRegion.northEast.lat, + northEastLon: koreaRegion.northEast.lon, + southWestLat: koreaRegion.southWest.lat, + southWestLon: koreaRegion.southWest.lon, + categories: categoryIDs + ) + .map { stores -> Mutation in + var filteredStores = stores + + let locationFilters = self.currentState.selectedLocationFilters + if !locationFilters.isEmpty { + filteredStores = stores.filter { store in + return locationFilters.contains { filter in + return self.store(store, matches: filter) + } + } + } + + return .setViewportStores(filteredStores) + } + .catch { error in .just(.setError(error)) }, + .just(.setLoading(false)) + ]) + case let .didSelectItem(store): return .concat([ diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift index 30d4f7c2..3df6ad08 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift @@ -1,19 +1,21 @@ import UIKit import SnapKit -import GoogleMaps +import NMapsMap final class MapView: UIView { // MARK: - Components - let mapView: GMSMapView = { - let camera = GMSCameraPosition(latitude: 37.5666, longitude: 126.9784, zoom: 14) - let view = GMSMapView(frame: .zero, camera: camera) - view.settings.myLocationButton = false - view.setMinZoom(7.5, maxZoom: 20) + let mapView: NMFMapView = { + let view = NMFMapView() + view.positionMode = .disabled + view.zoomLevel = 14 - let southWest = CLLocationCoordinate2D(latitude: 33.0, longitude: 124.0) - let northEast = CLLocationCoordinate2D(latitude: 39.0, longitude: 132.0) - let koreaBounds = GMSCoordinateBounds(coordinate: southWest, coordinate: northEast) - view.cameraTargetBounds = koreaBounds + view.extent = NMGLatLngBounds( + southWest: NMGLatLng(lat: 33.0, lng: 124.0), + northEast: NMGLatLng(lat: 39.0, lng: 132.0) + ) + + view.minZoomLevel = 7.5 + view.maxZoomLevel = 20 return view }() @@ -87,28 +89,24 @@ final class MapView: UIView { // MARK: - SetUp private extension MapView { func setUpConstraints() { - // 1. MapView 설정 addSubview(mapView) mapView.snp.makeConstraints { make in make.edges.equalToSuperview() } - // 2. Search Filter Container 설정 addSubview(searchFilterContainer) searchFilterContainer.snp.makeConstraints { make in make.top.equalToSuperview().offset(56) make.leading.trailing.equalToSuperview() } - // 3. Search Input 설정 searchFilterContainer.addSubview(searchInput) searchInput.snp.makeConstraints { make in make.top.equalToSuperview() - make.leading.trailing.equalToSuperview().inset(20) + make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(37) } - // 4. Filter Chips 설정 searchFilterContainer.addSubview(filterChips) filterChips.snp.makeConstraints { make in make.top.equalTo(searchInput.snp.bottom).offset(7) @@ -117,19 +115,16 @@ private extension MapView { make.bottom.equalToSuperview() } - // 5. Store Card 설정 addSubview(storeCard) storeCard.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(30) make.height.equalTo(137) - make.bottom.equalTo(safeAreaLayoutGuide).offset(-24) // 수정된 부분 + make.bottom.equalTo(safeAreaLayoutGuide).offset(-24) } - // 6. Buttons 설정 addSubview(locationButton) addSubview(listButton) - // 초기 버튼 레이아웃은 updateButtonLayout()에서 설정됨 } func configureUI() { diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index 52bf64b6..a2c36d29 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -4,15 +4,13 @@ import SnapKit import RxSwift import RxCocoa import ReactorKit -import GoogleMaps +import NMapsMap import CoreLocation import RxGesture - -class MapViewController: BaseViewController, View { +class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NMFMapViewTouchDelegate, NMFMapViewCameraDelegate, UIGestureRecognizerDelegate { typealias Reactor = MapReactor - fileprivate struct CoordinateKey: Hashable { let lat: Int let lng: Int @@ -22,20 +20,18 @@ class MapViewController: BaseViewController, View { self.lng = Int(longitude * 1_000_00) } } - // 전체 스토어 목록 저장 - var allStores: [MapPopUpStore] = [] + var currentTooltipView: UIView? var currentTooltipStores: [MapPopUpStore] = [] - var currentTooltipCoordinate: CLLocationCoordinate2D? - + var currentTooltipCoordinate: NMGLatLng? // MARK: - Properties private var storeDetailsCache: [Int64: StoreItem] = [:] - private var isMovingToMarker = false + var isMovingToMarker = false var currentCarouselStores: [MapPopUpStore] = [] - private var markerDictionary: [Int64: GMSMarker] = [:] - private var individualMarkerDictionary: [Int64: GMSMarker] = [:] - private var clusterMarkerDictionary: [String: GMSMarker] = [:] + private var markerDictionary: [Int64: NMFMarker] = [:] + private var individualMarkerDictionary: [Int64: NMFMarker] = [:] + private var clusterMarkerDictionary: [String: NMFMarker] = [:] private let popUpAPIUseCase = PopUpAPIUseCaseImpl( repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let clusteringManager = ClusteringManager() @@ -44,7 +40,7 @@ class MapViewController: BaseViewController, View { let mainView = MapView() let carouselView = MapPopupCarouselView() private let locationManager = CLLocationManager() - var currentMarker: GMSMarker? + var currentMarker: NMFMarker? private let storeListReactor = StoreListReactor() private let storeListViewController = StoreListViewController(reactor: StoreListReactor()) private var listViewTopConstraint: Constraint? @@ -62,6 +58,7 @@ class MapViewController: BaseViewController, View { } private var modalState: ModalState = .bottom + private let idleSubject = PublishSubject() // MARK: - Lifecycle override func viewDidAppear(_ animated: Bool) { @@ -72,6 +69,7 @@ class MapViewController: BaseViewController, View { self.filterChipsTopY = frameInView.minY } } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.tabBarController?.tabBar.isHidden = false @@ -80,60 +78,32 @@ class MapViewController: BaseViewController, View { override func viewDidLoad() { super.viewDidLoad() setUp() - mainView.mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0) - + mainView.mapView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0) locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.desiredAccuracy = kCLLocationAccuracyBest - mainView.mapView.isMyLocationEnabled = true + mainView.mapView.positionMode = .compass checkLocationAuthorization() + if let reactor = self.reactor { reactor.action.onNext(.fetchCategories) // 한국 전체 영역에 대한 경계값 설정 let koreaRegion = ( - northEast: CLLocationCoordinate2D(latitude: 38.0, longitude: 132.0), // 한국 북동쪽 끝 - southWest: CLLocationCoordinate2D(latitude: 33.0, longitude: 124.0) // 한국 남서쪽 끝 + northEast: NMGLatLng(lat: 38.0, lng: 132.0), + southWest: NMGLatLng(lat: 33.0, lng: 124.0) ) - // 전체 스토어 가져오기 reactor.action.onNext(.viewportChanged( - northEastLat: koreaRegion.northEast.latitude, - northEastLon: koreaRegion.northEast.longitude, - southWestLat: koreaRegion.southWest.latitude, - southWestLon: koreaRegion.southWest.longitude + northEastLat: koreaRegion.northEast.lat, + northEastLon: koreaRegion.northEast.lng, + southWestLat: koreaRegion.southWest.lat, + southWestLon: koreaRegion.southWest.lng )) - - // 데이터 로드 후 모든 마커 생성 및 추가 - reactor.state - .map { $0.viewportStores } - .distinctUntilChanged() - .filter { !$0.isEmpty } - .take(1) // 초기 1회만 - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] stores in - guard let self = self else { return } - - // 스토어 정보 저장 - self.allStores = stores - self.currentStores = stores - - // 모든 마커 생성 및 지도에 추가 (즉시 표시) - self.addAllMarkersToMap(stores: stores) - - // 현재 줌 레벨에 맞게 클러스터링 - self.updateMapWithClustering() - - // 가까운 스토어 표시 등 필요한 작업 - if let location = self.locationManager.location { - self.findAndShowNearestStore(from: location) - } - }) - .disposed(by: disposeBag) } - + setupMapViewRxObservables() carouselView.rx.observe(Bool.self, "hidden") .distinctUntilChanged() @@ -153,27 +123,6 @@ class MapViewController: BaseViewController, View { self?.navigationController?.pushViewController(detailController, animated: true) } - mainView.mapView.rx.idleAtPosition - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] in - guard let self = self else { return } - if let marker = self.currentMarker, - let storeArray = marker.userData as? [MapPopUpStore], - storeArray.count > 1 { - // 툴팁이 없으면 생성, 있으면 위치 업데이트 - if self.currentTooltipView == nil { - self.configureTooltip(for: marker, stores: storeArray) - } else { - self.updateTooltipPosition() - } - } - self.isMovingToMarker = false - }) - .disposed(by: disposeBag) - - - - carouselView.onCardScrolled = { [weak self] pageIndex in guard let self = self, pageIndex >= 0, @@ -182,13 +131,8 @@ class MapViewController: BaseViewController, View { let store = self.currentCarouselStores[pageIndex] // 이전 선택 마커 상태 초기화 - if let previousMarker = self.currentMarker, - let previousMarkerView = previousMarker.iconView as? MapMarker { - previousMarkerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: (previousMarker.userData as? [MapPopUpStore])?.count ?? 1 - )) + if let previousMarker = self.currentMarker { + self.updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false, count: 1) } // 스와이프한 스토어에 해당하는 마커 찾기 @@ -196,19 +140,15 @@ class MapViewController: BaseViewController, View { if let markerToFocus = markerToFocus { // 마커 선택 상태로 업데이트 - if let markerView = markerToFocus.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: (markerToFocus.userData as? [MapPopUpStore])?.count ?? 1 - )) - } - + self.updateMarkerStyle(marker: markerToFocus, selected: true, isCluster: false, count: 1) self.currentMarker = markerToFocus // 마이크로 클러스터인 경우 툴팁 처리 - if let storeArray = markerToFocus.userData as? [MapPopUpStore], storeArray.count > 1 { - if self.currentTooltipView == nil || self.currentTooltipCoordinate != markerToFocus.position { + let userData = markerToFocus.userInfo["storeData"] as? [MapPopUpStore] + if let storeArray = userData, storeArray.count > 1 { + if self.currentTooltipView == nil || + self.currentTooltipCoordinate?.lat != markerToFocus.position.lat || + self.currentTooltipCoordinate?.lng != markerToFocus.position.lng { self.configureTooltip(for: markerToFocus, stores: storeArray) } @@ -225,41 +165,37 @@ class MapViewController: BaseViewController, View { } if let reactor = self.reactor { - bindViewport(reactor: reactor) + bindViewport(reactor: reactor) reactor.action.onNext(.fetchCategories) - - } - + } } - private func addAllMarkersToMap(stores: [MapPopUpStore]) { - // 기존 마커 제거 - clearAllMarkers() - - // 모든 스토어에 대해 마커 생성하고 바로 지도에 추가 - for store in stores { - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: false - )) - marker.iconView = markerView - marker.map = mainView.mapView // 바로 지도에 추가 - individualMarkerDictionary[store.id] = marker - } + // NMF 이벤트 설정을 위한 새로운 메서드 + private func setupMapViewRxObservables() { + // 지도 이동 완료 감지 + mainView.mapView.addCameraDelegate(delegate: self) - Logger.log( - message: "🔍 전체 마커 생성 및 지도에 추가 완료: \(stores.count)개", - category: .debug - ) + // idleAtPosition 대체 - 지도 이동 완료 시 + idleSubject + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] in + guard let self = self else { return } + if let marker = self.currentMarker, + let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], + storeArray.count > 1 { + // 툴팁이 없으면 생성, 있으면 위치 업데이트 + if self.currentTooltipView == nil { + self.configureTooltip(for: marker, stores: storeArray) + } else { + self.updateTooltipPosition() + } + } + self.isMovingToMarker = false + }) + .disposed(by: disposeBag) } - private func configureTooltip(for marker: GMSMarker, stores: [MapPopUpStore]) { + private func configureTooltip(for marker: NMFMarker, stores: [MapPopUpStore]) { Logger.log(message: """ 툴팁 설정: - 현재 캐러셀 스토어: \(currentCarouselStores.map { $0.name }) @@ -283,14 +219,9 @@ class MapViewController: BaseViewController, View { self.carouselView.scrollToCard(index: index) // 선택된 상태로 업데이트 - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: stores.count - )) - } + self.updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: stores.count) tooltipView.selectStore(at: index) + Logger.log(message: """ 툴팁 선택: - 선택된 스토어: \(stores[index].name) @@ -298,11 +229,12 @@ class MapViewController: BaseViewController, View { """, category: .debug) } - // 툴팁 위치 설정 (예시: 마커 우측에 위치) - let markerPoint = self.mainView.mapView.projection.point(for: marker.position) - let markerHeight = (marker.iconView as? MapMarker)?.imageView.frame.height ?? 32 + // 툴팁 위치 설정 (마커 위치 기준으로 계산) + let markerPoint = self.mainView.mapView.projection.point(from: marker.position) + let markerHeight: CGFloat = 32 // 마커 이미지 높이 추정값 + tooltipView.frame = CGRect( - x: markerPoint.x , // 마커 오른쪽 10포인트 + x: markerPoint.x, y: markerPoint.y - markerHeight - tooltipView.frame.height - 14, width: tooltipView.frame.width, height: tooltipView.frame.height @@ -328,7 +260,7 @@ class MapViewController: BaseViewController, View { make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-24) } carouselView.isHidden = true - mainView.mapView.delegate = self + mainView.mapView.touchDelegate = self addChild(storeListViewController) view.addSubview(storeListViewController.view) @@ -346,36 +278,14 @@ class MapViewController: BaseViewController, View { setupPanAndSwipeGestures() let mapViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleMapViewTap(_:))) + mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 + mapViewTapGesture.delaysTouchesBegan = false // 터치 지연 없음 mainView.mapView.addGestureRecognizer(mapViewTapGesture) mapViewTapGesture.delegate = self - } - private let defaultZoomLevel: Float = 15.0 - private func addMarkersForAllStores(stores: [MapPopUpStore]) { - // 기존 마커 제거 - clearAllMarkers() - - for store in stores { - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: false - )) - marker.iconView = markerView - - // 여기서는 마커를 맵에 바로 추가하지 않고 딕셔너리에만 저장 - individualMarkerDictionary[store.id] = marker - } - // allStores 도 저장 - self.currentStores = stores - } + private let defaultZoomLevel: Double = 15.0 private func setupPanAndSwipeGestures() { storeListViewController.mainView.grabberHandle.rx.swipeGesture(.up) .skip(1) @@ -412,7 +322,6 @@ class MapViewController: BaseViewController, View { // MARK: - Bind func bind(reactor: Reactor) { - // 필터 관련 바인딩 mainView.filterChips.locationChip.rx.tap .map { Reactor.Action.filterTapped(.location) } @@ -438,17 +347,16 @@ class MapViewController: BaseViewController, View { guard let self = self, let location = self.locationManager.location else { return } - let camera = GMSCameraPosition.camera( - withLatitude: location.coordinate.latitude, - longitude: location.coordinate.longitude, - zoom: 15 - ) - self.mainView.mapView.animate(to: camera) - } - .disposed(by: disposeBag) - + // 현재 위치로 카메라 이동 + let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( + lat: location.coordinate.latitude, + lng: location.coordinate.longitude + ), zoomTo: 15.0) + self.mainView.mapView.moveCamera(cameraUpdate) + } + .disposed(by: disposeBag) mainView.filterChips.onRemoveLocation = { [weak self] in @@ -457,45 +365,46 @@ class MapViewController: BaseViewController, View { self.reactor?.action.onNext(.clearFilters(.location)) // 현재 뷰포트의 바운드로 마커 업데이트 요청 - let bounds = self.mainView.mapView.projection.visibleRegion() + let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( - northEastLat: bounds.farRight.latitude, - northEastLon: bounds.farRight.longitude, - southWestLat: bounds.nearLeft.latitude, - southWestLon: bounds.nearLeft.longitude + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng )) self.clearAllMarkers() - self.clusterMarkerDictionary.values.forEach { $0.map = nil } - self.clusterMarkerDictionary.removeAll() + self.clusterMarkerDictionary.values.forEach { $0.mapView = nil } + self.clusterMarkerDictionary.removeAll() + + // 캐러셀 숨기기 추가 + self.carouselView.isHidden = true + self.carouselView.updateCards([]) + self.currentCarouselStores = [] + self.mainView.setStoreCardHidden(true, animated: true) - // 캐러셀 숨기기 추가 - self.carouselView.isHidden = true - self.carouselView.updateCards([]) - self.currentCarouselStores = [] - self.mainView.setStoreCardHidden(true, animated: true) + self.updateMapWithClustering() + } - self.updateMapWithClustering() - } mainView.filterChips.onRemoveCategory = { [weak self] in guard let self = self else { return } // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.category)) // 현재 뷰포트의 바운드로 마커 업데이트 요청 - let bounds = self.mainView.mapView.projection.visibleRegion() + let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( - northEastLat: bounds.farRight.latitude, - northEastLon: bounds.farRight.longitude, - southWestLat: bounds.nearLeft.latitude, - southWestLon: bounds.nearLeft.longitude + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng )) self.resetSelectedMarker() // 만약 지도 위 마커를 전부 제거하고 싶다면 (상황에 따라) // self.clearAllMarkers() - // self.clusterMarkerDictionary.values.forEach { $0.map = nil } + // self.clusterMarkerDictionary.values.forEach { $0.mapView = nil } // self.clusterMarkerDictionary.removeAll() self.carouselView.isHidden = true self.carouselView.updateCards([]) @@ -558,34 +467,27 @@ class MapViewController: BaseViewController, View { } }) .disposed(by: disposeBag) + reactor.state.map { $0.searchResult } .distinctUntilChanged() .compactMap { $0 } .observe(on: MainScheduler.instance) .bind { [weak self] store in guard let self = self else { return } - let camera = GMSCameraPosition.camera( - withLatitude: store.latitude, - longitude: store.longitude, - zoom: 15 - ) - self.mainView.mapView.animate(to: camera) + + // 검색 결과 위치로 카메라 이동 + let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( + lat: store.latitude, + lng: store.longitude + ), zoomTo: 15.0) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + self.mainView.mapView.moveCamera(cameraUpdate) + self.addMarker(for: store) } .disposed(by: disposeBag) -// mainView.searchInput.onSearch = { [weak self] query in -// self?.reactor?.action.onNext(.searchTapped(query)) -// } -// -// reactor.state.map { $0.isLoading } -// .distinctUntilChanged() -// .observe(on: MainScheduler.instance) -// .bind { [weak self] isLoading in -// self?.mainView.searchInput.searchTextField.isEnabled = !isLoading -//// self?.mainView.searchInput.setLoading(isLoading) -// } -// .disposed(by: disposeBag) -// 보류 + mainView.searchInput.rx.tapGesture() .when(.recognized) .throttle(.milliseconds(500), scheduler: MainScheduler.instance) @@ -599,8 +501,6 @@ class MapViewController: BaseViewController, View { }) .disposed(by: disposeBag) - - reactor.state.map { $0.searchResults } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -608,7 +508,7 @@ class MapViewController: BaseViewController, View { guard let self = self else { return } // 이전 선택된 마커, 툴팁, 캐러셀 초기화 - self.mainView.mapView.clear() + self.clearAllMarkers() self.storeListViewController.reactor?.action.onNext(.setStores([])) self.carouselView.updateCards([]) self.carouselView.isHidden = true @@ -634,45 +534,20 @@ class MapViewController: BaseViewController, View { self.carouselView.isHidden = false self.currentCarouselStores = results - // 만약 현재 선택된 마커의 스토어가 새로운 결과에 없다면, 선택 상태 초기화 - if let currentMarker = self.currentMarker, - let selectedStore = currentMarker.userData as? MapPopUpStore, - !results.contains(where: { $0.id == selectedStore.id }) { - self.resetSelectedMarker() - } - // 첫 번째 검색 결과로 지도 이동 if let firstStore = results.first { - let camera = GMSCameraPosition.camera( - withLatitude: firstStore.latitude, - longitude: firstStore.longitude, - zoom: 15 - ) - self.mainView.mapView.animate(to: camera) + let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( + lat: firstStore.latitude, + lng: firstStore.longitude + ), zoomTo: 15.0) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + self.mainView.mapView.moveCamera(cameraUpdate) } } .disposed(by: disposeBag) - - - - -// reactor.state.map { $0.searchResults.isEmpty } -// .distinctUntilChanged() -// .skip(1) // 초기값 스킵 -// .observe(on: MainScheduler.instance) -// .bind { [weak self] isEmpty in -// guard let self = self else { return } -// if isEmpty { -// self.showAlert( -// title: "검색 결과 없음", -// message: "검색 결과가 없습니다. 다른 키워드로 검색해보세요." -// ) -// } -// } -// .disposed(by: disposeBag) } - // MARK: - List View Control private func toggleListView() { UIView.animate(withDuration: 0.3) { @@ -682,22 +557,55 @@ class MapViewController: BaseViewController, View { self.mainView.searchFilterContainer.backgroundColor = .clear self.view.layoutIfNeeded() } + } + + // 마커 추가 메서드 (NMFMarker로 변환) + func addMarker(for store: MapPopUpStore) { + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] + + // 마커 스타일 설정 + updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: 1) + + // 중요: 마커에 직접 터치 핸들러 추가 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self else { return false } + + Logger.log(message: "마커 터치됨! 위치: \(marker.position), 스토어: \(store.name)", category: .debug) + + // 단일 스토어 마커 처리 + return self.handleSingleStoreTap(marker, store: store) + } - } + marker.mapView = mainView.mapView + markerDictionary[store.id] = marker + } - func addMarker(for store: MapPopUpStore) { - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store + func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { + if selected { + marker.width = 44 + marker.height = 44 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + } else if isCluster { + marker.width = 36 + marker.height = 36 + marker.iconImage = NMFOverlayImage(name: "cluster_marker") + } else { + marker.width = 32 + marker.height = 32 + marker.iconImage = NMFOverlayImage(name: "Marker") + } - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) + if count > 1 { + marker.captionText = "\(count)" + } else { + marker.captionText = "" + } - let markerView = MapMarker() - markerView.injection(with: store.toMarkerInput()) - marker.iconView = markerView - marker.map = mainView.mapView - } + marker.anchor = CGPoint(x: 0.5, y: 1.0) + } @objc private func handleMapViewTap(_ gesture: UITapGestureRecognizer) { // 리스트뷰가 현재 보이는 상태(중간 또는 상단)일 때만 내림 @@ -772,11 +680,12 @@ class MapViewController: BaseViewController, View { } } - private func updateMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { let progress = (maxOffset - offset) / (maxOffset - minOffset) // 0(탑) ~ 1(바텀) mainView.mapView.alpha = max(0, min(progress, 1)) // 0(완전히 가림) ~ 1(완전히 보임) } + + private func animateToState(_ state: ModalState) { guard modalState != state else { return } self.view.layoutIfNeeded() @@ -793,8 +702,6 @@ class MapViewController: BaseViewController, View { self.listViewTopConstraint?.update(offset: filterChipsFrame.maxY) self.mainView.searchInput.setBackgroundColor(.g50) - - case .middle: self.storeListViewController.setGrabberHandleVisible(true) let offset = max(self.view.frame.height * 0.3, self.filterContainerBottomY) @@ -805,52 +712,46 @@ class MapViewController: BaseViewController, View { self.mainView.mapView.isHidden = false self.mainView.searchInput.setBackgroundColor(.white) - // 리스트뷰 표시 시, 이미 가져온 전체 스토어 데이터를 바로 사용 - if !self.allStores.isEmpty { - // 이미 데이터가 있으면 바로 표시 - self.fetchStoreDetails(for: self.allStores) - - Logger.log( - message: "✅ 기존 스토어 목록으로 리스트뷰 업데이트: \(self.allStores.count)개", - category: .debug + // 리스트뷰 표시 시, 전체 한국 영역의 스토어 가져오기 + if let reactor = self.reactor { + // 한국 전체 영역에 대한 경계값 설정 + let koreaRegion = ( + northEast: NMGLatLng(lat: 38.0, lng: 132.0), + southWest: NMGLatLng(lat: 33.0, lng: 124.0) ) - } else { - // 데이터가 없으면 전체 영역 요청 (이 부분도 필요하지만, 초기 로드 시 이미 불러왔을 가능성이 높음) - if let reactor = self.reactor { - let koreaRegion = ( - northEast: CLLocationCoordinate2D(latitude: 38.0, longitude: 132.0), - southWest: CLLocationCoordinate2D(latitude: 33.0, longitude: 124.0) - ) - - reactor.action.onNext(.viewportChanged( - northEastLat: koreaRegion.northEast.latitude, - northEastLon: koreaRegion.northEast.longitude, - southWestLat: koreaRegion.southWest.latitude, - southWestLon: koreaRegion.southWest.longitude - )) - - // 즉시 구독하지만 최대 1회만 실행 - reactor.state - .map { $0.viewportStores } - .distinctUntilChanged() - .filter { !$0.isEmpty } - .take(1) - .subscribe(onNext: { [weak self] allStores in - guard let self = self else { return } - - self.allStores = allStores - self.fetchStoreDetails(for: allStores) - }) - .disposed(by: self.disposeBag) - } + + // 전체 스토어를 가져오기 위해 한국 전체 영역 요청 + reactor.action.onNext(.viewportChanged( + northEastLat: koreaRegion.northEast.lat, + northEastLon: koreaRegion.northEast.lng, + southWestLat: koreaRegion.southWest.lat, + southWestLon: koreaRegion.southWest.lng + )) + + reactor.state + .map { $0.viewportStores } + .distinctUntilChanged() + .filter { !$0.isEmpty } + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] stores in + guard let self = self else { return } + self.fetchStoreDetails(for: stores) + + Logger.log( + message: "✅ 전체 스토어 목록으로 리스트뷰 업데이트: \(stores.count)개", + category: .debug + ) + }) + .disposed(by: self.disposeBag) } + case .bottom: self.storeListViewController.setGrabberHandleVisible(true) self.listViewTopConstraint?.update(offset: self.view.frame.height) self.mainView.mapView.alpha = 1 self.mainView.mapView.isHidden = false self.mainView.searchInput.setBackgroundColor(.white) - } self.view.layoutIfNeeded() @@ -860,1152 +761,1049 @@ class MapViewController: BaseViewController, View { } } + func imageFromView(_ view: UIView) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale) + defer { UIGraphicsEndImageContext() } + if let context = UIGraphicsGetCurrentContext() { + view.layer.render(in: context) + return UIGraphicsGetImageFromCurrentImageContext() + } + return nil + } - // updateMapWithClustering() 메서드 전체 구현 + // MARK: - Helper: 클러스터용 커스텀 마커 이미지 생성 (MapMarker를 사용) + func createClusterMarkerImage(regionName: String, count: Int) -> UIImage? { + // MapMarker의 입력값에 클러스터 상태를 전달합니다. + let markerView = MapMarker() // 기존 커스텀 뷰, 네이버맵용으로도 사용 가능하도록 구현됨. + let input = MapMarker.Input(isSelected: false, + isCluster: true, + regionName: regionName, + count: count, + isMultiMarker: false) + markerView.injection(with: input) + // 프레임이 설정되어 있지 않다면 적당한 크기로 지정 (예: 80x32) + if markerView.frame == .zero { + markerView.frame = CGRect(x: 0, y: 0, width: 80, height: 32) + } + return imageFromView(markerView) + } + // MARK: - Clustering private func updateMapWithClustering() { - let currentZoom = mainView.mapView.camera.zoom - let level = MapZoomLevel.getLevel(from: currentZoom) - let visibleRegion = mainView.mapView.projection.visibleRegion() - let visibleBoundsRect = GMSCoordinateBounds(region: visibleRegion) - - // 현재 뷰포트에 있는 스토어만 필터링 (allStores가 빈 경우 currentStores 사용) - let visibleStores = !allStores.isEmpty ? - allStores.filter { store in visibleBoundsRect.contains(store.coordinate) } : - currentStores - - // 현재 화면에 보이는 스토어 업데이트 - currentStores = visibleStores - + let currentZoom = mainView.mapView.zoomLevel + let level = MapZoomLevel.getLevel(from: Float(currentZoom)) + // 클러스터 처리 시 현재 스토어 목록(currentStores)을 사용 + Logger.log(message: "현재 줌 레벨: \(currentZoom), 모드: \(level), 스토어 수: \(currentStores.count)", category: .debug) CATransaction.begin() CATransaction.setDisableActions(true) switch level { case .detailed: - let newStoreIds = Set(visibleStores.map { $0.id }) - let groupedDict = groupStoresByExactLocation(visibleStores) + // 상세 레벨에서는 개별 마커를 사용합니다. + let newStoreIds = Set(currentStores.map { $0.id }) + let groupedDict = groupStoresByExactLocation(currentStores) - clusterMarkerDictionary.values.forEach { $0.map = nil } + // 클러스터 마커 제거 + clusterMarkerDictionary.values.forEach { $0.mapView = nil } clusterMarkerDictionary.removeAll() + // 그룹별로 개별 마커 생성/업데이트 for (coordinate, storeGroup) in groupedDict { if storeGroup.count == 1, let store = storeGroup.first { if let existingMarker = individualMarkerDictionary[store.id] { - if existingMarker.position != store.coordinate { - existingMarker.position = store.coordinate - } - - existingMarker.map = mainView.mapView - - if let markerView = existingMarker.iconView as? MapMarker, - markerView.currentInput?.isSelected != (existingMarker == currentMarker) { - markerView.injection(with: .init( - isSelected: (existingMarker == currentMarker), - isCluster: false - )) + if existingMarker.position.lat != store.latitude || + existingMarker.position.lng != store.longitude { + existingMarker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) } + let isSelected = (existingMarker == currentMarker) + updateMarkerStyle(marker: existingMarker, selected: isSelected, isCluster: false) } else { - let marker = GMSMarker(position: store.coordinate) - marker.userData = store - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: false - )) - marker.iconView = markerView - marker.map = mainView.mapView + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] + marker.anchor = CGPoint(x: 0.5, y: 1.0) + updateMarkerStyle(marker: marker, selected: false, isCluster: false) + + // 직접 터치 핸들러 추가 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self else { return false } + + print("개별 마커 터치됨! 스토어: \(store.name)") + return self.handleSingleStoreTap(marker, store: store) + } + marker.mapView = mainView.mapView individualMarkerDictionary[store.id] = marker } } else { + // 여러 스토어가 동일 위치에 있으면 단일 마커로 표시하면서 count 갱신 guard let firstStore = storeGroup.first else { continue } let markerKey = firstStore.id - if let existingMarker = individualMarkerDictionary[markerKey] { - existingMarker.userData = storeGroup - - existingMarker.map = mainView.mapView - - if let markerView = existingMarker.iconView as? MapMarker, - markerView.currentInput?.count != storeGroup.count || - markerView.currentInput?.isSelected != (existingMarker == currentMarker) { - markerView.injection(with: .init( - isSelected: (existingMarker == currentMarker), - isCluster: false, - count: storeGroup.count - )) - } + existingMarker.userInfo = ["storeData": storeGroup] + let isSelected = (existingMarker == currentMarker) + updateMarkerStyle(marker: existingMarker, selected: isSelected, isCluster: false, count: storeGroup.count) } else { - // 새 마커 생성 - let marker = GMSMarker(position: firstStore.coordinate) - marker.userData = storeGroup - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: storeGroup.count - )) - marker.iconView = markerView - marker.map = mainView.mapView + let marker = NMFMarker() + marker.position = NMGLatLng(lat: firstStore.latitude, lng: firstStore.longitude) + marker.userInfo = ["storeData": storeGroup] + marker.anchor = CGPoint(x: 0.5, y: 1.0) + updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeGroup.count) + + // 직접 터치 핸들러 추가 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self else { return false } + + print("마이크로 클러스터 마커 터치됨! 스토어 수: \(storeGroup.count)개") + return self.handleMicroClusterTap(marker, storeArray: storeGroup) + } + marker.mapView = mainView.mapView individualMarkerDictionary[markerKey] = marker } } } - for (id, marker) in individualMarkerDictionary { - if !newStoreIds.contains(id) { - marker.map = nil + // 기존에 보이지 않는 개별 마커 제거 + individualMarkerDictionary = individualMarkerDictionary.filter { id, marker in + if newStoreIds.contains(id) { + return true + } else { + marker.mapView = nil + return false } } case .district, .city, .country: // 개별 마커 숨기기 - individualMarkerDictionary.values.forEach { $0.map = nil } + individualMarkerDictionary.values.forEach { $0.mapView = nil } + individualMarkerDictionary.removeAll() - // 클러스터 생성 및 업데이트 - let clusters = clusteringManager.clusterStores(visibleStores, at: currentZoom) + // 클러스터 생성 + let clusters = clusteringManager.clusterStores(currentStores, at: Float(currentZoom)) let activeClusterKeys = Set(clusters.map { $0.cluster.name }) - // 클러스터 마커 업데이트 for cluster in clusters { let clusterKey = cluster.cluster.name - + var marker: NMFMarker if let existingMarker = clusterMarkerDictionary[clusterKey] { - // 기존 마커 재사용 - if existingMarker.position != cluster.cluster.coordinate { - existingMarker.position = cluster.cluster.coordinate - } - existingMarker.userData = cluster - existingMarker.map = mainView.mapView - - if let markerView = existingMarker.iconView as? MapMarker, - markerView.currentInput?.count != cluster.storeCount { - markerView.injection(with: .init( - isSelected: false, - isCluster: true, - regionName: cluster.cluster.name, - count: cluster.storeCount - )) + marker = existingMarker + // 위치 업데이트가 필요하면 수정 + if marker.position.lat != cluster.cluster.coordinate.lat || + marker.position.lng != cluster.cluster.coordinate.lng { + marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) } } else { - // 새 클러스터 마커 생성 - let marker = GMSMarker(position: cluster.cluster.coordinate) - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - marker.userData = cluster - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: true, - regionName: cluster.cluster.name, - count: cluster.storeCount - )) - marker.iconView = markerView - marker.map = mainView.mapView - + marker = NMFMarker() clusterMarkerDictionary[clusterKey] = marker } + + marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) + marker.userInfo = ["clusterData": cluster] // 중요: userInfo에 cluster 객체를 직접 저장 + + // 여기서 커스텀 클러스터 마커 뷰를 이미지로 변환하여 적용합니다. + if let clusterImage = createClusterMarkerImage(regionName: cluster.cluster.name, count: cluster.storeCount) { + marker.iconImage = NMFOverlayImage(image: clusterImage) + } else { + // 기본 에셋 fallback (원하는 경우) + marker.iconImage = NMFOverlayImage(name: "cluster_marker") + } + + // 터치 핸들러 추가 - userInfo에서 클러스터 데이터를 직접 가져오기 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self, + let tappedMarker = overlay as? NMFMarker, + let clusterData = tappedMarker.userInfo["clusterData"] as? ClusterMarkerData else { + return false + } + + return self.handleRegionalClusterTap(tappedMarker, clusterData: clusterData) + } + + marker.captionText = "" + marker.anchor = CGPoint(x: 0.5, y: 0.5) + marker.mapView = mainView.mapView } + // 활성 클러스터가 아닌 마커 제거 for (key, marker) in clusterMarkerDictionary { if !activeClusterKeys.contains(key) { - marker.map = nil + marker.mapView = nil + clusterMarkerDictionary.removeValue(forKey: key) } } } CATransaction.commit() } - private func clearAllMarkers() { - individualMarkerDictionary.values.forEach { $0.map = nil } - individualMarkerDictionary.removeAll() - clusterMarkerDictionary.values.forEach { $0.map = nil } - clusterMarkerDictionary.removeAll() - markerDictionary.values.forEach { $0.map = nil } - markerDictionary.removeAll() - } + private func clearAllMarkers() { + individualMarkerDictionary.values.forEach { $0.mapView = nil } + individualMarkerDictionary.removeAll() - private func groupStoresByExactLocation(_ stores: [MapPopUpStore]) -> [CoordinateKey: [MapPopUpStore]] { - var dict = [CoordinateKey: [MapPopUpStore]]() - for store in stores { - let key = CoordinateKey(latitude: store.latitude, longitude: store.longitude) - dict[key, default: []].append(store) + clusterMarkerDictionary.values.forEach { $0.mapView = nil } + clusterMarkerDictionary.removeAll() + + markerDictionary.values.forEach { $0.mapView = nil } + markerDictionary.removeAll() } - return dict - } + private func groupStoresByExactLocation(_ stores: [MapPopUpStore]) -> [CoordinateKey: [MapPopUpStore]] { + var dict = [CoordinateKey: [MapPopUpStore]]() + for store in stores { + let key = CoordinateKey(latitude: store.latitude, longitude: store.longitude) + dict[key, default: []].append(store) + } + return dict + } - private func updateIndividualMarkers(_ stores: [MapPopUpStore]) { - var newMarkerIDs = Set() + private func updateIndividualMarkers(_ stores: [MapPopUpStore]) { + var newMarkerIDs = Set() - for store in stores { - newMarkerIDs.insert(store.id) - if let marker = individualMarkerDictionary[store.id] { - if marker.position.latitude != store.latitude || marker.position.longitude != store.longitude { - marker.position = store.coordinate - } - } else { - // 새 마커 생성 및 추가 - let marker = GMSMarker(position: store.coordinate) - marker.userData = store + for store in stores { + newMarkerIDs.insert(store.id) + if let marker = individualMarkerDictionary[store.id] { + if marker.position.lat != store.latitude || marker.position.lng != store.longitude { + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + } + } else { + // 새 마커 생성 및 추가 + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] - let markerView = MapMarker() - markerView.injection(with: store.toMarkerInput()) - marker.iconView = markerView - marker.map = mainView.mapView + updateMarkerStyle(marker: marker, selected: false, isCluster: false) + marker.mapView = mainView.mapView - individualMarkerDictionary[store.id] = marker + individualMarkerDictionary[store.id] = marker + } } - } - for (id, marker) in individualMarkerDictionary { - if !newMarkerIDs.contains(id) { - marker.map = nil - individualMarkerDictionary.removeValue(forKey: id) + for (id, marker) in individualMarkerDictionary { + if !newMarkerIDs.contains(id) { + marker.mapView = nil + individualMarkerDictionary.removeValue(forKey: id) + } } } - } + private func updateClusterMarkers(_ clusters: [ClusterMarkerData]) { for clusterData in clusters { let clusterKey = clusterData.cluster.name let fixedCoordinate = clusterData.cluster.coordinate if let marker = clusterMarkerDictionary[clusterKey] { - if marker.position.latitude != fixedCoordinate.latitude || - marker.position.longitude != fixedCoordinate.longitude { - marker.position = fixedCoordinate + if marker.position.lat != fixedCoordinate.lat || marker.position.lng != fixedCoordinate.lng { + marker.position = NMGLatLng(lat: fixedCoordinate.lat, lng: fixedCoordinate.lng) } } else { - let marker = GMSMarker() - marker.position = fixedCoordinate - marker.userData = clusterData - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: true, - regionName: clusterData.cluster.name, - count: clusterData.storeCount - )) - marker.iconView = markerView - marker.map = mainView.mapView + let marker = NMFMarker() + marker.position = NMGLatLng(lat: fixedCoordinate.lat, lng: fixedCoordinate.lng) + marker.userInfo = ["clusterData": clusterData] + + updateMarkerStyle(marker: marker, selected: false, isCluster: true, + count: clusterData.storeCount, regionName: clusterData.cluster.name) + marker.mapView = mainView.mapView clusterMarkerDictionary[clusterKey] = marker } } } + func presentFilterBottomSheet(for filterType: FilterType) { + guard let reactor = self.reactor else { return } + let sheetReactor = FilterBottomSheetReactor( + savedSubRegions: reactor.currentState.selectedLocationFilters, + savedCategories: reactor.currentState.selectedCategoryFilters + ) + let viewController = FilterBottomSheetViewController(reactor: sheetReactor) - func presentFilterBottomSheet(for filterType: FilterType) { - guard let reactor = self.reactor else { return } - - let sheetReactor = FilterBottomSheetReactor( - savedSubRegions: reactor.currentState.selectedLocationFilters, - savedCategories: reactor.currentState.selectedCategoryFilters - ) - let viewController = FilterBottomSheetViewController(reactor: sheetReactor) + let initialIndex = (filterType == .location) ? 0 : 1 + viewController.containerView.segmentedControl.selectedSegmentIndex = initialIndex + sheetReactor.action.onNext(.segmentChanged(initialIndex)) - let initialIndex = (filterType == .location) ? 0 : 1 - viewController.containerView.segmentedControl.selectedSegmentIndex = initialIndex - sheetReactor.action.onNext(.segmentChanged(initialIndex)) + viewController.onSave = { [weak self] filterData in + guard let self = self else { return } + self.reactor?.action.onNext(.updateBothFilters( + locations: filterData.locations, + categories: filterData.categories + )) + self.reactor?.action.onNext(.filterTapped(nil)) + + let bounds = self.getVisibleBounds() + self.reactor?.action.onNext(.viewportChanged( + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng + )) + } - viewController.onSave = { [weak self] filterData in - guard let self = self else { return } - self.reactor?.action.onNext(.updateBothFilters( - locations: filterData.locations, - categories: filterData.categories - )) - self.reactor?.action.onNext(.filterTapped(nil)) + viewController.onDismiss = { [weak self] in + self?.reactor?.action.onNext(.filterTapped(nil)) + } - let bounds = self.mainView.mapView.projection.visibleRegion() - self.reactor?.action.onNext(.viewportChanged( - northEastLat: bounds.farRight.latitude, - northEastLon: bounds.farRight.longitude, - southWestLat: bounds.nearLeft.latitude, - southWestLon: bounds.nearLeft.longitude - )) - } + viewController.modalPresentationStyle = .overFullScreen + present(viewController, animated: false) { + viewController.showBottomSheet() + } - viewController.onDismiss = { [weak self] in - self?.reactor?.action.onNext(.filterTapped(nil)) + currentFilterBottomSheet = viewController } - viewController.modalPresentationStyle = .overFullScreen - present(viewController, animated: false) { - viewController.showBottomSheet() + private func dismissFilterBottomSheet() { + if let bottomSheet = currentFilterBottomSheet { + bottomSheet.hideBottomSheet() + } + currentFilterBottomSheet = nil } - currentFilterBottomSheet = viewController - } - private func dismissFilterBottomSheet() { - if let bottomSheet = currentFilterBottomSheet { - bottomSheet.hideBottomSheet() - } - currentFilterBottomSheet = nil - } - //기본 마커 + //기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { - mainView.mapView.clear() + markerDictionary.values.forEach { $0.mapView = nil } markerDictionary.removeAll() for store in stores { - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store - - let markerView = MapMarker() - markerView.injection(with: .init( - isSelected: false, - isCluster: false - )) - marker.iconView = markerView - marker.map = mainView.mapView + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] + + updateMarkerStyle(marker: marker, selected: false, isCluster: false) + + // 직접 터치 핸들러 추가 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self else { return false } + + print("검색 결과 마커 터치됨! 스토어: \(store.name)") + return self.handleSingleStoreTap(marker, store: store) + } + + marker.mapView = mainView.mapView markerDictionary[store.id] = marker } } - private func updateListView(with results: [MapPopUpStore]) { - // MapPopUpStore 배열을 StoreItem 배열로 변환 - let storeItems = results.map { $0.toStoreItem() } - storeListViewController.reactor?.action.onNext(.setStores(storeItems)) - } - - private func showAlert(title: String, message: String) { - let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) - present(alert, animated: true, completion: nil) - } - // 캐러셀 영역을 제외한 실제 가시 영역 계산 함수 - private func getEffectiveViewport() -> GMSCoordinateBounds { - // 기본 가시 영역 가져오기 - let visibleRegion = mainView.mapView.projection.visibleRegion() - - // 캐러셀이 보이지 않으면 전체 영역 반환 - if carouselView.isHidden { - return GMSCoordinateBounds(region: visibleRegion) + private func updateListView(with results: [MapPopUpStore]) { + // MapPopUpStore 배열을 StoreItem 배열로 변환 + let storeItems = results.map { $0.toStoreItem() } + storeListViewController.reactor?.action.onNext(.setStores(storeItems)) } - // 캐러셀 상단 Y 좌표를 지도 좌표로 변환 - let carouselTopY = carouselView.frame.minY + private func showAlert(title: String, message: String) { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) + present(alert, animated: true, completion: nil) + } - // 화면 좌표계에서 캐러셀 상단 라인을 생성 (좌우 전체 폭) - let leftPoint = CGPoint(x: 0, y: carouselTopY) - let rightPoint = CGPoint(x: view.frame.width, y: carouselTopY) + // 캐러셀 영역을 제외한 실제 가시 영역 계산 함수 + private func getEffectiveViewport() -> NMGLatLngBounds { + // 기본 가시 영역 가져오기 + let bounds = getVisibleBounds() - // 화면 좌표를 지도 좌표로 변환 - let leftCoordinate = mainView.mapView.projection.coordinate(for: leftPoint) - let rightCoordinate = mainView.mapView.projection.coordinate(for: rightPoint) + // 캐러셀이 보이지 않으면 전체 영역 반환 + if carouselView.isHidden { + return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast) + } - // 캐러셀 영역을 제외한 북쪽 경계 결정 (원래 북쪽 경계는 그대로 유지) - let adjustedSouthWest = CLLocationCoordinate2D( - latitude: max(leftCoordinate.latitude, rightCoordinate.latitude), - longitude: visibleRegion.nearLeft.longitude - ) + // 캐러셀 상단 Y 좌표 + let carouselTopY = carouselView.frame.minY - // 조정된 경계로 새 영역 생성 - return GMSCoordinateBounds( - coordinate: visibleRegion.farLeft, - coordinate: adjustedSouthWest - ) - } + // 화면 좌표계에서 캐러셀 상단 라인을 생성 (좌우 전체 폭) + let leftPoint = CGPoint(x: 0, y: carouselTopY) + let rightPoint = CGPoint(x: view.frame.width, y: carouselTopY) + // 화면 좌표를 지도 좌표로 변환 + let leftCoordinate = mainView.mapView.projection.latlng(from: leftPoint) + let rightCoordinate = mainView.mapView.projection.latlng(from: rightPoint) + // 캐러셀 영역을 제외한 경계 계산 + let adjustedSouthWest = NMGLatLng( + lat: max(leftCoordinate.lat, rightCoordinate.lat), + lng: bounds.southWest.lng + ) - // MARK: - Location - private func checkLocationAuthorization() { - switch locationManager.authorizationStatus { - case .notDetermined: - locationManager.requestWhenInUseAuthorization() - case .authorizedWhenInUse, .authorizedAlways: - locationManager.startUpdatingLocation() - case .denied, .restricted: - Logger.log( - message: "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", - category: .error + // 조정된 경계로 새 영역 생성 + return NMGLatLngBounds( + southWest: adjustedSouthWest, + northEast: bounds.northEast ) - @unknown default: - break } - } -} -// MARK: - CLLocationManagerDelegate -extension MapViewController: CLLocationManagerDelegate { - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - guard let location = locations.last else { return } + // 현재 보이는 지도 영역의 경계를 가져오는 함수 + private func getVisibleBounds() -> (northEast: NMGLatLng, southWest: NMGLatLng) { + let mapBounds = mainView.mapView.contentBounds - currentMarker?.map = nil - currentMarker = nil - carouselView.isHidden = true - currentCarouselStores = [] + let northEast = NMGLatLng(lat: mapBounds.northEastLat, lng: mapBounds.northEastLng) + let southWest = NMGLatLng(lat: mapBounds.southWestLat, lng: mapBounds.southWestLng) - let camera = GMSCameraPosition.camera( - withLatitude: location.coordinate.latitude, - longitude: location.coordinate.longitude, - zoom: 15 - ) - mainView.mapView.animate(to: camera) + return (northEast: northEast, southWest: southWest) + } - // 카메라 이동이 완료된 후 가장 가까운 스토어 찾기 - mainView.mapView.rx.idleAtPosition - .take(1) - .subscribe(onNext: { [weak self] _ in - guard let self = self else { return } - self.findAndShowNearestStore(from: location) - }) - .disposed(by: disposeBag) + // MARK: - Location + private func checkLocationAuthorization() { + switch locationManager.authorizationStatus { + case .notDetermined: + locationManager.requestWhenInUseAuthorization() + case .authorizedWhenInUse, .authorizedAlways: + locationManager.startUpdatingLocation() + mainView.mapView.positionMode = .direction // 내 위치 트래킹 모드 활성화 + case .denied, .restricted: + Logger.log( + message: "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", + category: .error + ) + mainView.mapView.positionMode = .disabled // 내 위치 트래킹 모드 비활성화 + @unknown default: + break + } + } - locationManager.stopUpdatingLocation() - } + private func updateTooltipPosition() { + guard let marker = currentMarker, let tooltip = currentTooltipView else { return } + // 마커 위치를 화면 좌표로 변환 + let markerPoint = mainView.mapView.projection.point(from: marker.position) + var markerCenter = markerPoint - private func findAndShowNearestStore(from location: CLLocation) { - guard !currentStores.isEmpty else { - Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) - return - } + // 마커 높이 고려 (네이버 마커는 크기가 다를 수 있음) + markerCenter.y = markerPoint.y - 20 // 마커 이미지 높이의 절반 정도 - resetSelectedMarker() + // 오프셋 값 (디자인에 맞게 조정) + let offsetX: CGFloat = -10 + let offsetY: CGFloat = -10 - let nearestStore = currentStores.min { store1, store2 in - let location1 = CLLocation(latitude: store1.latitude, longitude: store1.longitude) - let location2 = CLLocation(latitude: store2.latitude, longitude: store2.longitude) - return location.distance(from: location1) < location.distance(from: location2) + tooltip.frame.origin = CGPoint( + x: markerCenter.x + offsetX, + y: markerCenter.y - tooltip.frame.height - offsetY + ) } - if let store = nearestStore, let marker = findMarkerForStore(for: store) { - // 카메라 이동 없이 선택된 마커만 업데이트합니다. - _ = handleSingleStoreTap(marker, store: store) - } - // 만약 마커가 없다면, 기존 로직과 같이 마커를 생성하고 선택 상태를 업데이트 - else if let store = nearestStore { - let marker = GMSMarker() - marker.position = store.coordinate - marker.userData = store - marker.groundAnchor = CGPoint(x: 0.5, y: 1.0) - - let markerView = MapMarker() - markerView.injection(with: .init(isSelected: true, isCluster: false, count: 1)) - marker.iconView = markerView - marker.map = mainView.mapView - - individualMarkerDictionary[store.id] = marker - currentMarker = marker - carouselView.updateCards([store]) - currentCarouselStores = [store] - carouselView.scrollToCard(index: 0) - mainView.setStoreCardHidden(false, animated: true) - } + private func resetSelectedMarker() { + if let currentMarker = currentMarker { + // 마커 스타일 업데이트 + updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) } - } - -// MARK: - GMSMapViewDelegate -extension MapViewController: GMSMapViewDelegate { - func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool { - let hitBoxSize: CGFloat = 44 // 터치 영역 크기 - let markerPoint = mapView.projection.point(for: marker.position) - let touchPoint = mapView.projection.point(for: marker.position) - let distance = sqrt( - pow(markerPoint.x - touchPoint.x, 2) + - pow(markerPoint.y - touchPoint.y, 2) - ) - - // 터치 영역을 벗어난 경우 무시 - if distance > hitBoxSize / 2 { - return false - } - // (1) 구/시 단위 클러스터 - if let clusterData = marker.userData as? ClusterMarkerData { - return handleRegionalClusterTap(marker, clusterData: clusterData) - } - // 동일 좌표 마이크로 클러스터 - else if let storeArray = marker.userData as? [MapPopUpStore] { - if storeArray.count > 1 { - return handleMicroClusterTap(marker, storeArray: storeArray) - } else if let singleStore = storeArray.first { - return handleSingleStoreTap(marker, store: singleStore) - } - } - // 단일 스토어 - else if let singleStore = marker.userData as? MapPopUpStore { - return handleSingleStoreTap(marker, store: singleStore) - } - - return false - } - - - func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { - if !isMovingToMarker { + // 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] - updateMapWithClustering() - - // 캐러셀 초기화 + currentTooltipCoordinate = nil carouselView.isHidden = true carouselView.updateCards([]) currentCarouselStores = [] - } - } - - - func mapView(_ mapView: GMSMapView, willMove gesture: Bool) { - if gesture && !isMovingToMarker { - resetSelectedMarker() - } - } - /// 지도 빈 공간 탭 → 기존 마커/캐러셀 해제 - func mapView(_ mapView: GMSMapView, didTapAt coordinate: CLLocationCoordinate2D) { - guard !isMovingToMarker else { return } - - // 현재 선택된 마커의 상태를 완전히 초기화 - if let currentMarker = currentMarker { - if let markerView = currentMarker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: (currentMarker.userData as? [MapPopUpStore])?.count ?? 1 - )) - } -// currentMarker.map = nil + // 현재 마커 참조 제거 self.currentMarker = nil } - // 툴팁 제거 - currentTooltipView?.removeFromSuperview() - currentTooltipView = nil - currentTooltipStores = [] - currentTooltipCoordinate = nil - - // 캐러셀 초기화 - carouselView.isHidden = true - carouselView.updateCards([]) - self.currentCarouselStores = [] - mainView.setStoreCardHidden(true, animated: true) - - // 클러스터링 업데이트 - updateMapWithClustering() - } - - - - - // MARK: - Helper for single marker tap - func handleSingleStoreTap(_ marker: GMSMarker, store: MapPopUpStore) -> Bool { - isMovingToMarker = true - - // 이전 마커 선택 상태 해제 - if let previousMarker = currentMarker, - let previousMarkerView = previousMarker.iconView as? MapMarker { - previousMarkerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: (previousMarker.userData as? [MapPopUpStore])?.count ?? 1 - )) + private func updateMarkersForCluster(stores: [MapPopUpStore]) { + // 전체 개별 및 클러스터 마커 제거 + for marker in individualMarkerDictionary.values { + marker.mapView = nil } + individualMarkerDictionary.removeAll() - // 새 마커 선택 상태로 설정 - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: (marker.userData as? [MapPopUpStore])?.count ?? 1 - )) + for marker in clusterMarkerDictionary.values { + marker.mapView = nil } + clusterMarkerDictionary.removeAll() - currentMarker = marker + // 클러스터에 포함된 스토어들만 새 마커 추가 + for store in stores { + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] + marker.anchor = CGPoint(x: 0.5, y: 1.0) - // 캐러셀에 표시할 스토어 확인 - if currentCarouselStores.isEmpty || !currentCarouselStores.contains(where: { $0.id == store.id }) { - // 현재 뷰포트의 모든 스토어를 가져오기 - let visibleRegion = mainView.mapView.projection.visibleRegion() - let bounds = GMSCoordinateBounds(region: visibleRegion) + updateMarkerStyle(marker: marker, selected: false, isCluster: false) - let visibleStores = currentStores.filter { store in - bounds.contains(CLLocationCoordinate2D( - latitude: store.latitude, - longitude: store.longitude - )) + // 직접 터치 핸들러 추가 + marker.touchHandler = { [weak self] (overlay) -> Bool in + guard let self = self else { return false } + + print("클러스터 내 마커 터치됨! 스토어: \(store.name)") + return self.handleSingleStoreTap(marker, store: store) } - if !visibleStores.isEmpty { - // 뷰포트의 모든 스토어를 캐러셀에 표시 - currentCarouselStores = visibleStores - carouselView.updateCards(visibleStores) + marker.mapView = mainView.mapView + individualMarkerDictionary[store.id] = marker + } + } + - // 선택한 스토어의 인덱스를 찾아 스크롤 - if let index = visibleStores.firstIndex(where: { $0.id == store.id }) { - carouselView.scrollToCard(index: index) + private func findMarkerForStore(for store: MapPopUpStore) -> NMFMarker? { + // individualMarkerDictionary에 저장된 모든 마커를 순회 + for marker in individualMarkerDictionary.values { + if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore, singleStore.id == store.id { + return marker + } + if let storeGroup = marker.userInfo["storeData"] as? [MapPopUpStore], + storeGroup.contains(where: { $0.id == store.id }) { + return marker } - } else { - // 뷰포트에 다른 스토어가 없는 경우, 선택한 스토어만 표시 - currentCarouselStores = [store] - carouselView.updateCards([store]) } - } else { - if let index = currentCarouselStores.firstIndex(where: { $0.id == store.id }) { - carouselView.scrollToCard(index: index) + // 상세 레벨이 아닐 경우 clusterMarkerDictionary에도 동일하게 검색 + for marker in clusterMarkerDictionary.values { + if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData, + clusterData.cluster.stores.contains(where: { $0.id == store.id }) { + return marker + } } + return nil } - carouselView.isHidden = false - mainView.setStoreCardHidden(false, animated: true) - - // 툴팁 처리 - if let storeArray = marker.userData as? [MapPopUpStore], storeArray.count > 1 { - // 마이크로 클러스터인 경우 툴팁 표시 - configureTooltip(for: marker, stores: storeArray) - // 해당 스토어의 툴팁 인덱스 선택 - if let index = storeArray.firstIndex(where: { $0.id == store.id }) { - (currentTooltipView as? MarkerTooltipView)?.selectStore(at: index) + private func fetchStoreDetails(for stores: [MapPopUpStore]) { + // 빈 목록이면 처리하지 않음 + guard !stores.isEmpty else { return } + + // 먼저 기본 정보로 StoreItem 생성하여 순서 유지 + let initialStoreItems = stores.map { store in + StoreItem( + id: store.id, + thumbnailURL: store.mainImageUrl ?? "", + category: store.category, + title: store.name, + location: store.address, + dateRange: "\(store.startDate ?? "") ~ \(store.endDate ?? "")", + isBookmarked: false + ) } - } else { - currentTooltipView?.removeFromSuperview() - currentTooltipView = nil - } - isMovingToMarker = false - return true - } + // 리스트에는 모든 스토어 정보 표시 (필터링된 모든 스토어) + self.storeListViewController.reactor?.action.onNext(.setStores(initialStoreItems)) + // 각 스토어의 상세 정보를 병렬로 가져와서 업데이트 (북마크 정보 등) + stores.forEach { store in + self.popUpAPIUseCase.getPopUpDetail( + commentType: "NORMAL", + popUpStoredId: store.id + ) + .asObservable() + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] detail in + self?.storeListViewController.reactor?.action.onNext(.updateStoreBookmark( + id: store.id, + isBookmarked: detail.bookmarkYn + )) + }) + .disposed(by: disposeBag) + } + } + func bindViewport(reactor: MapReactor) { + // 카메라 이동 완료 시 이벤트 발생되는 Subject + let cameraObservable = PublishSubject() + + // NMFMapViewCameraDelegate 메서드에서 호출할 수 있도록 설정 + + // 카메라 변경 감지해서 액션 전달 + cameraObservable + .throttle(.milliseconds(200), scheduler: MainScheduler.instance) + .map { [unowned self] _ -> MapReactor.Action in + let bounds = self.getVisibleBounds() + return .viewportChanged( + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng + ) + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + // 현재 뷰포트 내의 스토어 업데이트 - 초기 1회 + reactor.state + .map { $0.viewportStores } + .distinctUntilChanged() + .filter { !$0.isEmpty } + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] stores in + guard let self = self else { return } + + // 현재 위치가 있으면 가장 가까운 스토어, 없으면 첫 번째 스토어 표시 + if let location = self.locationManager.location { + self.findAndShowNearestStore(from: location) + } else if let firstStore = stores.first, + let marker = self.findMarkerForStore(for: firstStore) { + _ = self.handleSingleStoreTap(marker, store: firstStore) + } + + // 현재 스토어 목록 업데이트 및 클러스터링 + self.currentStores = stores + self.updateMapWithClustering() + }) + .disposed(by: disposeBag) + + // 뷰포트 내 마커 업데이트 및 캐러셀 표시 + reactor.state + .map { $0.viewportStores } + .distinctUntilChanged() + .throttle(.milliseconds(200), scheduler: MainScheduler.instance) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] stores in + guard let self = self else { return } - func handleRegionalClusterTap(_ marker: GMSMarker, clusterData: ClusterMarkerData) -> Bool { - let currentZoom = mainView.mapView.camera.zoom - let currentLevel = MapZoomLevel.getLevel(from: currentZoom) + let effectiveViewport = self.getEffectiveViewport() + let bounds = self.getVisibleBounds() - switch currentLevel { - case .city: // 시 단위 클러스터 - let districtZoomLevel: Float = 10.0 - let camera = GMSCameraPosition(target: marker.position, zoom: districtZoomLevel) - mainView.mapView.animate(to: camera) - case .district: // 구 단위 클러스터 - let detailedZoomLevel: Float = 12.0 - let camera = GMSCameraPosition(target: marker.position, zoom: detailedZoomLevel) - mainView.mapView.animate(to: camera) - default: - break - } + // 화면에 보이는 스토어만 필터링 + let visibleStores = stores.filter { store in + let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) + return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast).contains(storePosition) + } + self.currentStores = visibleStores - // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 - updateMarkersForCluster(stores: clusterData.cluster.stores) + // 개별 마커 레벨인지 확인 + let currentZoom = self.mainView.mapView.zoomLevel + let level = MapZoomLevel.getLevel(from: Float(currentZoom)) - // 캐러셀 업데이트 - carouselView.updateCards(clusterData.cluster.stores) - carouselView.isHidden = false - self.currentCarouselStores = clusterData.cluster.stores + if level == .detailed && !visibleStores.isEmpty { + // 캐러셀에 모든 마커 정보 표시 + let effectiveStores = visibleStores.filter { store in + let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) + return effectiveViewport.contains(storePosition) + } - return true - } + self.currentCarouselStores = visibleStores + self.carouselView.updateCards(visibleStores) + self.carouselView.isHidden = false + self.mainView.setStoreCardHidden(false, animated: true) + + // 현재 선택된 마커가 있으면 해당 위치로 스크롤 + if let currentMarker = self.currentMarker { + // 마커의 스토어 정보 체크 + if let currentStore = currentMarker.userInfo["storeData"] as? MapPopUpStore, + let index = visibleStores.firstIndex(where: { $0.id == currentStore.id }) { + self.carouselView.scrollToCard(index: index) + } else if let storeArray = currentMarker.userInfo["storeData"] as? [MapPopUpStore], + let firstStore = storeArray.first, + let index = visibleStores.firstIndex(where: { $0.id == firstStore.id }) { + self.carouselView.scrollToCard(index: index) + } else { + // 선택된 마커가 현재 뷰포트에 없는 경우 + self.updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) + self.currentMarker = nil + + // 첫 번째 스토어의 마커를 선택 상태로 설정 + if let firstStore = visibleStores.first, + let marker = self.findMarkerForStore(for: firstStore) { + self.updateMarkerStyle(marker: marker, selected: true, isCluster: false) + self.currentMarker = marker + } - func handleMicroClusterTap(_ marker: GMSMarker, storeArray: [MapPopUpStore]) -> Bool { - // 이미 선택된 마커를 다시 탭할 때 - if currentMarker == marker { - // 툴팁과 캐러셀만 숨기고, 마커의 선택 상태는 유지 - currentTooltipView?.removeFromSuperview() - currentTooltipView = nil - currentTooltipStores = [] - currentTooltipCoordinate = nil + self.carouselView.scrollToCard(index: 0) + } + } else { + // 선택된 마커가 없는 경우, 첫 번째 스토어로 설정 + if let firstStore = visibleStores.first, + let marker = self.findMarkerForStore(for: firstStore) { + self.updateMarkerStyle(marker: marker, selected: true, isCluster: false) + self.currentMarker = marker + } + self.carouselView.scrollToCard(index: 0) + } + } else { + // 클러스터 레벨이거나 마커가 없는 경우 + self.carouselView.isHidden = true + self.carouselView.updateCards([]) + self.currentCarouselStores = [] + self.mainView.setStoreCardHidden(true, animated: true) + + if level == .detailed && visibleStores.isEmpty { + // 개별 마커 레벨인데 마커가 없는 경우 토스트 표시 + self.showNoMarkersToast() + } + } - carouselView.isHidden = true - carouselView.updateCards([]) - currentCarouselStores = [] + self.updateMapWithClustering() + }) + .disposed(by: disposeBag) + } - // 마커 상태 업데이트 - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: storeArray.count - )) + private func findAndShowNearestStore(from location: CLLocation) { + guard !currentStores.isEmpty else { + Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) + return } - currentMarker = nil - isMovingToMarker = false - return false - } + resetSelectedMarker() - isMovingToMarker = true + let nearestStore = currentStores.min { store1, store2 in + let location1 = CLLocation(latitude: store1.latitude, longitude: store1.longitude) + let location2 = CLLocation(latitude: store2.latitude, longitude: store2.longitude) + return location.distance(from: location1) < location.distance(from: location2) + } - currentTooltipView?.removeFromSuperview() - currentTooltipView = nil + if let store = nearestStore, let marker = findMarkerForStore(for: store) { + // 카메라 이동 없이 선택된 마커만 업데이트 + _ = handleSingleStoreTap(marker, store: store) + } + // 마커가 없다면 새로 생성 + else if let store = nearestStore { + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.userInfo = ["storeData": store] + marker.anchor = CGPoint(x: 0.5, y: 1.0) - if let previousMarker = currentMarker, - let previousMarkerView = previousMarker.iconView as? MapMarker { - previousMarkerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: (previousMarker.userData as? [MapPopUpStore])?.count ?? 1 - )) - } + // 마커 스타일 설정 + updateMarkerStyle(marker: marker, selected: true, isCluster: false) + marker.mapView = mainView.mapView - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: storeArray.count - )) + individualMarkerDictionary[store.id] = marker + currentMarker = marker + carouselView.updateCards([store]) + currentCarouselStores = [store] + carouselView.scrollToCard(index: 0) + mainView.setStoreCardHidden(false, animated: true) + } } - currentMarker = marker - currentCarouselStores = storeArray - carouselView.updateCards(storeArray) - carouselView.isHidden = false - carouselView.scrollToCard(index: 0) + // MARK: - Marker Handling + func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { + isMovingToMarker = true - mainView.setStoreCardHidden(false, animated: true) + // 이전 마커 선택 상태 해제 + if let previousMarker = currentMarker { + updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) + } - // 지도 이동 및 툴팁 생성 - mainView.mapView.animate(toLocation: marker.position) + // 새 마커 선택 상태로 설정 + updateMarkerStyle(marker: marker, selected: true, isCluster: false) + currentMarker = marker - // 툴팁 생성을 idleAtPosition 이벤트까지 기다리지 않고 직접 호출 - if storeArray.count > 1 { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in - guard let self = self else { return } - self.configureTooltip(for: marker, stores: storeArray) - self.isMovingToMarker = false - } - } + // 캐러셀에 표시할 스토어 확인 + if currentCarouselStores.isEmpty || !currentCarouselStores.contains(where: { $0.id == store.id }) { + // 현재 뷰포트의 모든 스토어를 가져오기 + let bounds = getVisibleBounds() - return true - } - private func showNoMarkersToast() { - // 디자인 예정이므로 임시 구현 - Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) - } - private func updateTooltipPosition() { - guard let marker = currentMarker, let tooltip = currentTooltipView else { return } + let visibleStores = currentStores.filter { store in + let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) + return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast).contains(storePosition) + } - let markerPoint = mainView.mapView.projection.point(for: marker.position) - var markerCenter = markerPoint - if let iconView = marker.iconView { - markerCenter.y = markerPoint.y - iconView.bounds.height / 1.5 - } + if !visibleStores.isEmpty { + // 뷰포트의 모든 스토어를 캐러셀에 표시 + currentCarouselStores = visibleStores + carouselView.updateCards(visibleStores) - // 오프셋 값 (디자인에 맞게 조정) - let offsetX: CGFloat = -10 - let offsetY: CGFloat = -10 + // 선택한 스토어의 인덱스를 찾아 스크롤 + if let index = visibleStores.firstIndex(where: { $0.id == store.id }) { + carouselView.scrollToCard(index: index) + } + } else { + // 뷰포트에 다른 스토어가 없는 경우, 선택한 스토어만 표시 + currentCarouselStores = [store] + carouselView.updateCards([store]) + } + } else { + // 캐러셀에 이미 해당 스토어가 있는 경우, 해당 위치로 스크롤 + if let index = currentCarouselStores.firstIndex(where: { $0.id == store.id }) { + carouselView.scrollToCard(index: index) + } + } - tooltip.frame.origin = CGPoint( - x: markerCenter.x + offsetX, - y: markerCenter.y - tooltip.frame.height - offsetY - ) - } + carouselView.isHidden = false + mainView.setStoreCardHidden(false, animated: true) - private func resetSelectedMarker() { - if let currentMarker = currentMarker, - let markerView = currentMarker.iconView as? MapMarker { - // 기존 마커뷰 재사용, 새로 생성하지 않음 - if let storeArray = currentMarker.userData as? [MapPopUpStore] { - markerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: storeArray.count - )) + // 툴팁 처리 + if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { + // 마이크로 클러스터인 경우 툴팁 표시 + configureTooltip(for: marker, stores: storeArray) + // 해당 스토어의 툴팁 인덱스 선택 + if let index = storeArray.firstIndex(where: { $0.id == store.id }) { + (currentTooltipView as? MarkerTooltipView)?.selectStore(at: index) + } } else { - markerView.injection(with: .init( - isSelected: false, - isCluster: false - )) + // 단일 마커인 경우 툴팁 제거 + currentTooltipView?.removeFromSuperview() + currentTooltipView = nil } + + isMovingToMarker = false + return true } - // 툴팁 제거 - currentTooltipView?.removeFromSuperview() - currentTooltipView = nil - currentTooltipStores = [] - currentTooltipCoordinate = nil - carouselView.isHidden = true - carouselView.updateCards([]) - currentCarouselStores = [] + // 리전 클러스터 탭 처리 + func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { + print("handleRegionalClusterTap 함수 호출됨") - // 현재 마커 참조 제거 - self.currentMarker = nil - } + let currentZoom = mainView.mapView.zoomLevel + let currentLevel = MapZoomLevel.getLevel(from: Float(currentZoom)) + // 디버깅 + print("현재 줌 레벨: \(currentZoom), 모드: \(currentLevel)") + print("클러스터 정보: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)") -} + switch currentLevel { + case .city: // 시 단위 클러스터 + print("시 단위 클러스터 처리") + let districtZoomLevel: Double = 10.0 + let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: districtZoomLevel) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + mainView.mapView.moveCamera(cameraUpdate) + case .district: // 구 단위 클러스터 + print("구 단위 클러스터 처리") + let detailedZoomLevel: Double = 12.0 + let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: detailedZoomLevel) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + mainView.mapView.moveCamera(cameraUpdate) -extension MapViewController { - func bindViewport(reactor: MapReactor) { - let cameraObservable = Observable.merge([ - mainView.mapView.rx.didChangePosition, - mainView.mapView.rx.idleAtPosition - ]) - .throttle(.milliseconds(200), scheduler: MainScheduler.instance) - .map { [unowned self] in - self.mainView.mapView.camera + default: + print("기타 레벨 클러스터 처리") + break } - let distinctCameraObservable = cameraObservable.distinctUntilChanged { (cam1, cam2) -> Bool in - let loc1 = CLLocation(latitude: cam1.target.latitude, longitude: cam1.target.longitude) - let loc2 = CLLocation(latitude: cam2.target.latitude, longitude: cam2.target.longitude) - let distance = loc1.distance(from: loc2) - return distance < 40 - } + // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 + updateMarkersForCluster(stores: clusterData.cluster.stores) - // 뷰포트가 변경될 때마다 액션 전달 - distinctCameraObservable - .map { [unowned self] _ -> MapReactor.Action in - let visibleRegion = self.mainView.mapView.projection.visibleRegion() - return .viewportChanged( - northEastLat: visibleRegion.farRight.latitude, - northEastLon: visibleRegion.farRight.longitude, - southWestLat: visibleRegion.nearLeft.latitude, - southWestLon: visibleRegion.nearLeft.longitude - ) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) + // 캐러셀 업데이트 + carouselView.updateCards(clusterData.cluster.stores) + carouselView.isHidden = false + self.currentCarouselStores = clusterData.cluster.stores - // 현재 뷰포트 내의 스토어 업데이트 - 마커만 업데이트 - reactor.state - .map { $0.viewportStores } - .distinctUntilChanged() - .filter { !$0.isEmpty } - .take(1) // 초기 1회만 실행 - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] stores in - guard let self = self else { return } - - // 현재 위치가 있으면 가장 가까운 스토어, 없으면 첫 번째 스토어 표시 - if let location = self.locationManager.location { - self.findAndShowNearestStore(from: location) - } else if let firstStore = stores.first, - let marker = self.findMarkerForStore(for: firstStore) { - _ = self.handleSingleStoreTap(marker, store: firstStore) - } - - // 현재 스토어 목록 업데이트 및 클러스터링 - self.currentStores = stores - self.updateMapWithClustering() - }) - .disposed(by: disposeBag) - - - // 뷰포트 내 마커 업데이트 및 캐러셀 표시 (수정된 부분) - reactor.state - .map { $0.viewportStores } - .distinctUntilChanged() - .throttle(.milliseconds(200), scheduler: MainScheduler.instance) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] stores in - guard let self = self else { return } + return true + } - let effectiveViewport = self.getEffectiveViewport() - let visibleRegion = self.mainView.mapView.projection.visibleRegion() - let bounds = GMSCoordinateBounds(region: visibleRegion) - // 화면에 보이는 스토어만 필터링 - let visibleStores = stores.filter { store in - bounds.contains(CLLocationCoordinate2D( - latitude: store.latitude, - longitude: store.longitude - )) - } + // 마이크로 클러스터 탭 처리 + func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { + // 이미 선택된 마커를 다시 탭할 때 + if currentMarker == marker { + // 툴팁과 캐러셀만 숨기고, 마커의 선택 상태는 유지 + currentTooltipView?.removeFromSuperview() + currentTooltipView = nil + currentTooltipStores = [] + currentTooltipCoordinate = nil - self.currentStores = visibleStores + carouselView.isHidden = true + carouselView.updateCards([]) + currentCarouselStores = [] - // 개별 마커 레벨인지 확인 - let currentZoom = self.mainView.mapView.camera.zoom - let level = MapZoomLevel.getLevel(from: currentZoom) + // 마커 상태 업데이트 + updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeArray.count) - if level == .detailed && !visibleStores.isEmpty { - // 캐러셀에 모든 마커 정보 표시 - let effectiveViewport = self.getEffectiveViewport() - let effectiveStores = visibleStores.filter { store in - effectiveViewport.contains(CLLocationCoordinate2D( - latitude: store.latitude, - longitude: store.longitude - )) - } + currentMarker = nil + isMovingToMarker = false + return false + } - self.currentCarouselStores = visibleStores - self.carouselView.updateCards(visibleStores) - self.carouselView.isHidden = false - self.mainView.setStoreCardHidden(false, animated: true) + isMovingToMarker = true - // 현재 선택된 마커가 있으면 해당 위치로 스크롤 - if let currentMarker = self.currentMarker { - // 마커의 스토어 정보 체크 - if let currentStore = currentMarker.userData as? MapPopUpStore, - let index = visibleStores.firstIndex(where: { $0.id == currentStore.id }) { - self.carouselView.scrollToCard(index: index) - } else if let storeArray = currentMarker.userData as? [MapPopUpStore], - let firstStore = storeArray.first, - let index = visibleStores.firstIndex(where: { $0.id == firstStore.id }) { - self.carouselView.scrollToCard(index: index) - } else { - // 선택된 마커가 현재 뷰포트에 없는 경우 - if let markerView = currentMarker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: false, - isCluster: false, - count: (currentMarker.userData as? [MapPopUpStore])?.count ?? 1 - )) - } - self.currentMarker = nil + currentTooltipView?.removeFromSuperview() + currentTooltipView = nil - // 첫 번째 스토어의 마커를 선택 상태로 설정 - if let firstStore = visibleStores.first, - let marker = self.findMarkerForStore(for: firstStore) { - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: (marker.userData as? [MapPopUpStore])?.count ?? 1 - )) - } - self.currentMarker = marker - } + if let previousMarker = currentMarker { + updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) + } - self.carouselView.scrollToCard(index: 0) - } - } else { - // 선택된 마커가 없는 경우, 첫 번째 스토어로 설정 - if let firstStore = visibleStores.first, - let marker = self.findMarkerForStore(for: firstStore) { - if let markerView = marker.iconView as? MapMarker { - markerView.injection(with: .init( - isSelected: true, - isCluster: false, - count: (marker.userData as? [MapPopUpStore])?.count ?? 1 - )) - } - self.currentMarker = marker - } - self.carouselView.scrollToCard(index: 0) - } - } else { - // 클러스터 레벨이거나 마커가 없는 경우 - self.carouselView.isHidden = true - self.carouselView.updateCards([]) - self.currentCarouselStores = [] - self.mainView.setStoreCardHidden(true, animated: true) + updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: storeArray.count) + currentMarker = marker - if level == .detailed && visibleStores.isEmpty { - // 개별 마커 레벨인데 마커가 없는 경우 토스트 표시 - self.showNoMarkersToast() - } - } + currentCarouselStores = storeArray + carouselView.updateCards(storeArray) + carouselView.isHidden = false + carouselView.scrollToCard(index: 0) - self.updateMapWithClustering() - }) - .disposed(by: disposeBag) + mainView.setStoreCardHidden(false, animated: true) - } - private func fetchStoreDetails(for stores: [MapPopUpStore]) { - // 빈 목록이면 처리하지 않음 - guard !stores.isEmpty else { return } - - // 먼저 기본 정보로 StoreItem 생성하여 순서 유지 - let initialStoreItems = stores.map { store in - StoreItem( - id: store.id, - thumbnailURL: store.mainImageUrl ?? "", - category: store.category, - title: store.name, - location: store.address, - dateRange: "\(store.startDate ?? "") ~ \(store.endDate ?? "")", - isBookmarked: false - ) - } + // 지도 이동 + let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 + mainView.mapView.moveCamera(cameraUpdate) - self.storeListViewController.reactor?.action.onNext(.setStores(initialStoreItems)) + // 툴팁 생성 + if storeArray.count > 1 { + DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in + guard let self = self else { return } + self.configureTooltip(for: marker, stores: storeArray) + self.isMovingToMarker = false + } + } - stores.forEach { store in - self.popUpAPIUseCase.getPopUpDetail( - commentType: "NORMAL", - popUpStoredId: store.id - ) - .asObservable() - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] detail in - self?.storeListViewController.reactor?.action.onNext(.updateStoreBookmark( - id: store.id, - isBookmarked: detail.bookmarkYn - )) - }) - .disposed(by: disposeBag) + return true + } + + private func showNoMarkersToast() { + // 디자인 예정이므로 임시 구현 + Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) } } + // MARK: - CLLocationManagerDelegate + extension MapViewController { + func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + guard let location = locations.last else { return } - private func findMarkerForStore(for store: MapPopUpStore) -> GMSMarker? { - // individualMarkerDictionary에 저장된 모든 마커를 순회 - for marker in individualMarkerDictionary.values { - if let singleStore = marker.userData as? MapPopUpStore, singleStore.id == store.id { - return marker - } - if let storeGroup = marker.userData as? [MapPopUpStore], - storeGroup.contains(where: { $0.id == store.id }) { - return marker - } - } - // 상세 레벨이 아닐 경우 clusterMarkerDictionary에도 동일하게 검색 - for marker in clusterMarkerDictionary.values { - if let storeGroup = marker.userData as? [MapPopUpStore], - storeGroup.contains(where: { $0.id == store.id }) { - return marker + currentMarker?.mapView = nil + currentMarker = nil + carouselView.isHidden = true + currentCarouselStores = [] + + let position = NMGLatLng(lat: location.coordinate.latitude, lng: location.coordinate.longitude) + let cameraUpdate = NMFCameraUpdate(scrollTo: position, zoomTo: 15.0) + mainView.mapView.moveCamera(cameraUpdate) { [weak self] _ in + guard let self = self else { return } + self.findAndShowNearestStore(from: location) } + + locationManager.stopUpdatingLocation() } - return nil } - private func updateMarkersForCluster(stores: [MapPopUpStore]) { - for marker in individualMarkerDictionary.values { - marker.map = nil - } - individualMarkerDictionary.removeAll() - for marker in clusterMarkerDictionary.values { - marker.map = nil - } - clusterMarkerDictionary.removeAll() + // MARK: - NMFMapViewTouchDelegate + extension MapViewController { + // 마커 탭 이벤트 처리 + // 마커 탭 이벤트 처리 + func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { + Logger.log(message: "didTapMarker 호출됨: \(marker.position), userInfo: \(marker.userInfo)", category: .debug) + + // 클러스터 마커 확인 + if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { + Logger.log(message: "클러스터 데이터 감지: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)", category: .debug) + return handleRegionalClusterTap(marker, clusterData: clusterData) + } + // 마이크로 클러스터 또는 단일 스토어 마커 확인 + else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { + if storeArray.count > 1 { + Logger.log(message: "마이크로 클러스터 감지: \(storeArray.count)개 스토어", category: .debug) + return handleMicroClusterTap(marker, storeArray: storeArray) + } else if let singleStore = storeArray.first { + Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) + return handleSingleStoreTap(marker, store: singleStore) + } + } + // 단일 스토어 마커 (배열이 아닌 경우) 확인 + else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { + Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) + return handleSingleStoreTap(marker, store: singleStore) + } - for store in stores { - addMarker(for: store) + Logger.log(message: "인식할 수 없는 마커 타입", category: .error) + return false } - } + // 지도 탭 이벤트 처리 + func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { + guard !isMovingToMarker else { return } -private func handleMarkerTap(_ marker: GMSMarker) -> Bool { - isMovingToMarker = true - - if let clusterData = marker.userData as? ClusterMarkerData { - let clusterToIndividualZoom: Float = 14.0 - let currentZoom = mainView.mapView.camera.zoom - let newZoom: Float = (currentZoom < clusterToIndividualZoom) - ? clusterToIndividualZoom - : min(mainView.mapView.maxZoom, currentZoom + 1) + // 선택된 마커 초기화 + if let currentMarker = currentMarker { + updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) + self.currentMarker = nil + } - let camera = GMSCameraPosition(target: marker.position, zoom: newZoom) - mainView.mapView.animate(to: camera) + // 툴팁 제거 + currentTooltipView?.removeFromSuperview() + currentTooltipView = nil + currentTooltipStores = [] + currentTooltipCoordinate = nil - // 여러 스토어 캐러셀 업데이트 - let multiStores = clusterData.cluster.stores - carouselView.updateCards(multiStores) - carouselView.isHidden = multiStores.isEmpty - currentCarouselStores = multiStores - // 클러스터 마커 강조/해제 등 필요시 추가 + // 캐러셀 초기화 + carouselView.isHidden = true + carouselView.updateCards([]) + self.currentCarouselStores = [] + mainView.setStoreCardHidden(true, animated: true) - return true + // 클러스터링 업데이트 + updateMapWithClustering() } + } - // 2) 일반 마커일 때 - if let previousMarker = currentMarker { - let markerView = MapMarker() - markerView.injection(with: .init(isSelected: false, isCluster: false)) - previousMarker.iconView = markerView + // MARK: - NMFMapViewCameraDelegate + extension MapViewController { + // 카메라 이동 시작 시 호출 + func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) { + if reason == NMFMapChangedByGesture && !isMovingToMarker { + resetSelectedMarker() + } } - // 새 마커 강조 - let markerView = MapMarker() - markerView.injection(with: .init(isSelected: true, isCluster: false)) - marker.iconView = markerView - currentMarker = marker - - if let store = marker.userData as? MapPopUpStore { - // 캐러셀에 뷰포트 내 스토어들을 모두 표시 - carouselView.updateCards(currentStores) - carouselView.isHidden = currentStores.isEmpty - currentCarouselStores = currentStores - - // 탭한 스토어가 몇 번째인지 찾아서 스크롤 - if let idx = currentStores.firstIndex(where: { $0.id == store.id }) { - carouselView.scrollToCard(index: idx) + // 카메라 이동 중 호출 + func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) { + if !isMovingToMarker { + currentTooltipView?.removeFromSuperview() + currentTooltipView = nil + currentTooltipStores = [] + updateMapWithClustering() + + // 캐러셀 초기화 + carouselView.isHidden = true + carouselView.updateCards([]) + currentCarouselStores = [] } } - return true - } - - - private func getCurrentViewportBounds() -> (northEast: CLLocationCoordinate2D, southWest: CLLocationCoordinate2D) { - let region = mainView.mapView.projection.visibleRegion() - return (northEast: region.farRight, southWest: region.nearLeft) - } - // 커스텀 마커 - func updateMarkers(with newStores: [MapPopUpStore]) { - let newStoreIDs = Set(newStores.map { $0.id }) - - for store in newStores { - if let marker = individualMarkerDictionary[store.id] { - if abs(marker.position.latitude - store.latitude) > 0.0001 || - abs(marker.position.longitude - store.longitude) > 0.0001 { - marker.position = store.coordinate + // 카메라 이동 완료 시 호출 + func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) { + if let marker = self.currentMarker, + let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], + storeArray.count > 1 { + // 툴팁이 없으면 생성, 있으면 위치 업데이트 + if self.currentTooltipView == nil { + self.configureTooltip(for: marker, stores: storeArray) + } else { + self.updateTooltipPosition() } - } else { - let marker = GMSMarker(position: store.coordinate) - marker.userData = store - - let markerView = MapMarker() - markerView.injection(with: store.toMarkerInput()) - marker.iconView = markerView - marker.map = mainView.mapView - - individualMarkerDictionary[store.id] = marker } - } - - for (id, marker) in individualMarkerDictionary { - if !newStoreIDs.contains(id) { - marker.map = nil - individualMarkerDictionary.removeValue(forKey: id) + self.isMovingToMarker = false + + // 뷰포트 변경 이벤트 처리 - idleSubject 통해 알림 + idleSubject.onNext(()) + + // 뷰포트 변경 이벤트 처리 + if let reactor = self.reactor { + let bounds = self.getVisibleBounds() + reactor.action.onNext(.viewportChanged( + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng + )) } } } -} -// MARK: - Reactive Extensions -extension Reactive where Base: GMSMapView { - var delegate: DelegateProxy { - return GMSMapViewDelegateProxy.proxy(for: base) - } - - var didChangePosition: Observable { - let proxy = GMSMapViewDelegateProxy.proxy(for: base) - return proxy.didChangePositionSubject.asObservable() - } - var idleAtPosition: Observable { - let proxy = GMSMapViewDelegateProxy.proxy(for: base) - return proxy.idleAtPositionSubject.asObservable() - } -} -extension CLLocationCoordinate2D: Equatable { - public static func == (lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool { - return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude - } -} -extension MapViewController: UIGestureRecognizerDelegate { - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - return true - } + // MARK: - UIGestureRecognizerDelegate + extension MapViewController { + // 맵뷰의 다른 제스처와 충돌하지 않도록 함 + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { + // 맵의 내장 제스처와 동시 인식 허용 + return true + } - // 리스트뷰가 보일 때만 커스텀 탭 제스처 허용 - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - // 터치가 리스트뷰 영역에 있으면 커스텀 제스처 트리거하지 않음 - let touchPoint = touch.location(in: view) + // 리스트뷰가 보일 때만 커스텀 탭 제스처 허용 + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + // 터치가 리스트뷰 영역에 있으면 커스텀 제스처 트리거하지 않음 + let touchPoint = touch.location(in: view) - // 리스트뷰가 보이고 터치가 리스트뷰 위에 있으면 탭 처리하지 않음 - if modalState != .bottom { - let listViewY = storeListViewController.view.frame.minY - if touchPoint.y > listViewY { - return false + // 리스트뷰가 보이고 터치가 리스트뷰 위에 있으면 탭 처리하지 않음 + if modalState != .bottom { + let listViewY = storeListViewController.view.frame.minY + if touchPoint.y > listViewY { + return false + } } - } - return true + return true + } + } +extension NMGLatLngBounds { + func contains(_ point: NMGLatLng) -> Bool { + let southWestLat = self.southWest.lat + let southWestLng = self.southWest.lng + let northEastLat = self.northEast.lat + let northEastLng = self.northEast.lng + + return point.lat >= southWestLat && + point.lat <= northEastLat && + point.lng >= southWestLng && + point.lng <= northEastLng } } diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift index 864d2426..6d00be65 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift @@ -134,7 +134,8 @@ final class StoreListReactor: Reactor { guard let self = self else { return .empty() } return self.popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", - popUpStoredId: store.id + popUpStoredId: store.id, + isViewCount: false ) .map { detail in var updatedStore = store From 2a935372044e15c999121e4f93e814224bfe0e7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 24 Mar 2025 16:12:51 +0900 Subject: [PATCH 011/393] =?UTF-8?q?[REAFACTOR]=20:=20=EA=B4=80=EB=A6=AC?= =?UTF-8?q?=EC=9E=90=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=8B=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=A4=91=EC=B2=A9=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=EC=8A=A4=ED=86=A0=EC=96=B4=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D=EC=8B=9C=20=EC=9C=A0=ED=9A=A8=EC=84=B1=EA=B2=80?= =?UTF-8?q?=EC=82=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopUpStoreRegisterViewController.swift | 448 +++++++++++++++--- .../Admin/AdminViewController.swift | 41 +- .../FullScreenMapViewController.swift | 205 ++++++-- .../MapGuideView/MapGuideViewController.swift | 21 +- 4 files changed, 590 insertions(+), 125 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index a559ee54..0d7914ea 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -23,9 +23,14 @@ final class PopUpStoreRegisterViewController: BaseViewController { private var latField: UITextField? private var lonField: UITextField? private var descTV: UITextView? + private let popupName: String = "" + private var originalImageIds: [String: Int64] = [:] + private var deletedImageIds: [Int64] = [] + private var deletedImagePaths: [String] = [] + private let editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? let presignedService = PreSignedService() @@ -193,13 +198,21 @@ final class PopUpStoreRegisterViewController: BaseViewController { super.viewDidLoad() view.backgroundColor = UIColor(white:0.95, alpha:1) - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) - tapGesture.cancelsTouchesInView = false - view.addGestureRecognizer(tapGesture) if let store = editingStore { - loadStoreDetail(for: store.id) - } + // 삭제된 이미지 ID 복원 + if let savedIds = UserDefaults.standard.array(forKey: "deletedImageIds_\(store.id)") as? [Int64] { + self.deletedImageIds = savedIds + Logger.log(message: "저장된 삭제 이미지 ID 복원: \(savedIds.count)개", category: .debug) + } + + // 삭제된 이미지 경로 복원 + if let savedPaths = UserDefaults.standard.array(forKey: "deletedImagePaths_\(store.id)") as? [String] { + self.deletedImagePaths = savedPaths + Logger.log(message: "저장된 삭제 이미지 경로 복원: \(savedPaths.count)개", category: .debug) + } + loadStoreDetail(for: store.id) + } setupNavigation() setupLayout() setupRows() @@ -207,6 +220,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { setupImageCollectionActions() setupKeyboardHandling() setupAddressField() + setupAllFieldListeners() + } @@ -235,6 +250,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } private func fillFormWithExistingData(_ storeDetail: GetAdminPopUpStoreDetailResponseDTO) { + // 기본 필드 채우기 nameField?.text = storeDetail.name categoryButton.setTitle("\(storeDetail.categoryName) ▾", for: .normal) addressField?.text = storeDetail.address @@ -242,30 +258,121 @@ final class PopUpStoreRegisterViewController: BaseViewController { lonField?.text = String(storeDetail.longitude) descTV?.text = storeDetail.desc + // 중요: ID와 URL 매핑 초기화 및 설정 + self.originalImageIds.removeAll() + // deletedImageIds와 deletedImagePaths는 초기화하지 않음 (기존 삭제 정보 유지) + + // 중요: 여기서 originalImageIds 맵을 세팅합니다 + for image in storeDetail.imageList { + self.originalImageIds[image.imageUrl] = image.id + Logger.log(message: "이미지 ID 매핑: \(image.imageUrl) -> \(image.id)", category: .debug) + } + + // 날짜 설정 let isoFormatter = ISO8601DateFormatter() - + if let startDate = isoFormatter.date(from: storeDetail.startDate), let endDate = isoFormatter.date(from: storeDetail.endDate) { self.selectedStartDate = startDate self.selectedEndDate = endDate - self.updatePeriodButtonTitle() // 버튼에 날짜 텍스트 업데이트 - } - - // 대표 이미지 로드 (생략된 부분은 기존 코드 참고) - if let mainImageURL = presignedService.fullImageURL(from: storeDetail.mainImageUrl) { - URLSession.shared.dataTask(with: mainImageURL) { [weak self] data, response, error in - guard let self = self, - let data = data, - let image = UIImage(data: data) else { return } - let extendedImage = ExtendedImage(filePath: storeDetail.mainImageUrl, image: image, isMain: true) - DispatchQueue.main.async { - self.images.append(extendedImage) - self.imagesCollectionView.reloadData() - self.updateSaveButtonState() - } - }.resume() + self.updatePeriodButtonTitle() + } + + // 중요: 기존 이미지는 먼저 모두 제거 + self.images.removeAll() + + // 이미지 목록 디버깅 - 실제 서버에서 받은 목록 확인 + Logger.log(message: "서버에서 받은 이미지 목록 (총 \(storeDetail.imageList.count)개):", category: .debug) + for (index, image) in storeDetail.imageList.enumerated() { + Logger.log(message: " \(index+1). ID: \(image.id), URL: \(image.imageUrl)", category: .debug) + } + + // 삭제된 이미지 ID 집합 생성 (빠른 검색을 위해) + let deletedIdSet = Set(self.deletedImageIds) + Logger.log(message: "삭제된 이미지 ID 목록: \(deletedIdSet)", category: .debug) + + // 중복 및 삭제된 이미지 제외를 위한 집합 + var loadedImageUrls = Set() + + let dispatchGroup = DispatchGroup() + let mainImageUrl = storeDetail.mainImageUrl + Logger.log(message: "대표 이미지 URL: \(mainImageUrl)", category: .debug) + + for imageData in storeDetail.imageList { + // 중복 이미지 건너뛰기 + if loadedImageUrls.contains(imageData.imageUrl) { + Logger.log(message: "중복 이미지 스킵: \(imageData.imageUrl)", category: .debug) + continue + } + + // 삭제된 이미지 건너뛰기 + if deletedIdSet.contains(imageData.id) { + Logger.log(message: "삭제된 이미지 스킵: ID \(imageData.id), URL: \(imageData.imageUrl)", category: .debug) + continue + } + + loadedImageUrls.insert(imageData.imageUrl) + + dispatchGroup.enter() + + if let imageURL = presignedService.fullImageURL(from: imageData.imageUrl) { + Logger.log(message: "이미지 로드 시작: \(imageData.imageUrl)", category: .debug) + + URLSession.shared.dataTask(with: imageURL) { [weak self] data, response, error in + defer { dispatchGroup.leave() } + + // 응답 상태 코드 확인 추가 + if let httpResponse = response as? HTTPURLResponse { + Logger.log(message: "이미지 로드 응답 코드: \(httpResponse.statusCode) - URL: \(imageData.imageUrl)", category: .debug) + if httpResponse.statusCode != 200 { + Logger.log(message: "이미지 로드 실패 - 상태 코드: \(httpResponse.statusCode)", category: .error) + return + } + } + + if let error = error { + Logger.log(message: "이미지 로드 오류: \(error.localizedDescription)", category: .error) + return + } + + guard let self = self, + let data = data, + let image = UIImage(data: data) else { + Logger.log(message: "이미지 변환 실패", category: .error) + return + } + + let isMain = (imageData.imageUrl == mainImageUrl) + + let extendedImage = ExtendedImage(filePath: imageData.imageUrl, image: image, isMain: isMain) + + DispatchQueue.main.async { + self.images.append(extendedImage) + Logger.log(message: "이미지 로드 완료: \(imageData.imageUrl), isMain: \(isMain)", category: .debug) + } + }.resume() + } else { + Logger.log(message: "이미지 URL 생성 실패: \(imageData.imageUrl)", category: .error) + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue: .main) { [weak self] in + guard let self = self else { return } + + if !self.images.isEmpty && !self.images.contains(where: { $0.isMain }) { + self.images[0].isMain = true + Logger.log(message: "대표 이미지가 없어 첫 번째 이미지를 대표로 설정", category: .debug) + } + + Logger.log(message: "모든 이미지 로드 완료: 총 \(self.images.count)개", category: .debug) + self.imagesCollectionView.reloadData() + self.updateSaveButtonState() } } + + + func loadStoreDetail(for storeId: Int64) { Logger.log(message: "상세 정보 요청 시작 - Store ID: \(storeId)", category: .debug) @@ -281,7 +388,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } private func setupKeyboardHandling() { - // 키보드 Notification 등록 NotificationCenter.default.addObserver( self, selector: #selector(keyboardWillShow), @@ -449,10 +555,40 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.height.equalTo(44) } } + private func getCurrentFormattedTime() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM.dd HH:mm" + formatter.timeZone = TimeZone(identifier: "Asia/Seoul") // 한국 시간대 명시적 설정 + return formatter.string(from: Date()) + } + private func setupCreationTimeLabel() -> UILabel { + let currentTime = getCurrentFormattedTime() + return makeSimpleLabel(currentTime) + } + + private func setupAllFieldListeners() { + // 이름 필드 + nameField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) + + // 주소, 위도, 경도 필드 + addressField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) + latField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) + lonField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) + + // 설명 필드 (UITextView는 다르게 처리해야 함) + descTV?.delegate = self + + // 로그 추가 + Logger.log(message: "모든 필드에 변경 리스너가 설정되었습니다", category: .debug) + } + @objc private func anyFieldDidChange(_ textField: UITextField) { + Logger.log(message: "\(textField.accessibilityIdentifier ?? "알 수 없는 필드") 값 변경: \(textField.text ?? "nil")", category: .debug) + updateSaveButtonState() + } // MARK: - Setup Rows private func setupRows() { addRowTextField(leftTitle: "이름", placeholder: "팝업스토어 이름을 입력해 주세요.") - addRowTextField(leftTitle: "이미지", placeholder: "팝업스토어 대표 이미지를 업로드 해주세요.") +// addRowTextField(leftTitle: "이미지", placeholder: "팝업스토어 대표 이미지를 업로드 해주세요.") categoryButton.addTarget(self, action: #selector(didTapCategoryButton), for: .touchUpInside) addRowCustom(leftTitle: "카테고리", rightView: categoryButton) @@ -509,9 +645,9 @@ final class PopUpStoreRegisterViewController: BaseViewController { // (마커) => 2줄 // 1) (마커명 Label + TF) let markerLabel = makePlainLabel("마커명") - + let markerField = makeRoundedTextField("") - + let markerStackH = UIStackView(arrangedSubviews: [markerLabel, markerField]) markerStackH.axis = .horizontal markerStackH.spacing = 8 @@ -548,7 +684,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { addRowCustom(leftTitle: "작성자", rightView: writerLbl) // (13) 작성시간 - let timeLbl = makeSimpleLabel("2025.01.06 10:30") + let timeLbl = setupCreationTimeLabel() addRowCustom(leftTitle: "작성시간", rightView: timeLbl) // (14) 상태값 @@ -641,17 +777,22 @@ final class PopUpStoreRegisterViewController: BaseViewController { } .disposed(by: disposeBag) } - // 저장 버튼 활성화 여부 갱신 private func updateSaveButtonState() { - let isFormValid = validateForm() // 폼 유효성 검사 결과 + // 디버깅을 위한 로깅 추가 + Logger.log(message: "updateSaveButtonState 호출됨", category: .debug) + + let isFormValid = validateForm() + + // 이전 상태와 새 상태가 다를 때만 로그 출력 + if saveButton.isEnabled != isFormValid { + Logger.log(message: "저장 버튼 상태 변경: \(saveButton.isEnabled) -> \(isFormValid)", category: .debug) + } + saveButton.isEnabled = isFormValid saveButton.backgroundColor = isFormValid ? .systemBlue : .lightGray } - /** - rowHeight: 기본(41) - totalHeight: 2줄 필요한 경우(90~100), 3줄 등 필요 시 더 크게 - */ + private func addRowCustom(leftTitle: String, rightView: UIView, rowHeight: CGFloat? = 36, @@ -845,6 +986,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func updateCategoryButtonTitle(with category: String) { categoryButton.setTitle("\(category) ▾", for: .normal) +     updateSaveButtonState() } // MARK: - UI Helpers @@ -965,10 +1107,46 @@ private extension PopUpStoreRegisterViewController { /// 특정 index 이미지 삭제 func deleteImage(index: Int) { + // 삭제될 이미지가 대표 이미지였는지 확인 + let wasMainImage = images[index].isMain + + // 삭제된 이미지의 URL 가져오기 + let imageUrl = images[index].filePath + + // 기존에 존재하던 이미지인 경우 (ID가 있는 경우) + if let imageId = originalImageIds[imageUrl] { + // 이미 삭제 목록에 있는지 확인 (중복 방지) + if !deletedImageIds.contains(imageId) { + deletedImageIds.append(imageId) + deletedImagePaths.append(imageUrl) + Logger.log(message: "기존 이미지 삭제 목록에 추가: ID \(imageId), URL: \(imageUrl)", category: .debug) + + // 삭제된 이미지 정보 영구 저장 (앱 재시작 간에도 유지) + if let store = editingStore { + UserDefaults.standard.set(deletedImageIds, forKey: "deletedImageIds_\(store.id)") + UserDefaults.standard.set(deletedImagePaths, forKey: "deletedImagePaths_\(store.id)") + Logger.log(message: "삭제된 이미지 정보 저장 완료", category: .debug) + } + } + } + + // S3 삭제 로직 제거 - 서버 업데이트 후로 이동 + + // 이미지 배열에서 제거 images.remove(at: index) + + // 대표 이미지가 삭제되었고, 다른 이미지가 남아있다면 첫 번째 이미지를 대표 이미지로 설정 + if wasMainImage && !images.isEmpty { + images[0].isMain = true + } + imagesCollectionView.reloadData() updateSaveButtonState() } + + + + } extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { @@ -983,6 +1161,10 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { let itemProviders = results.map(\.itemProvider) let dispatchGroup = DispatchGroup() + // 이미 로드된 이미지 경로 목록 (중복 방지) + let existingPaths = Set(self.images.map { $0.filePath }) + Logger.log(message: "기존 이미지 경로 수: \(existingPaths.count)", category: .debug) + for (i, provider) in itemProviders.enumerated() { if provider.canLoadObject(ofClass: UIImage.self) { dispatchGroup.enter() @@ -992,6 +1174,13 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { let image = object as? UIImage else { return } let filePath = "PopUpImage/\(name)/\(uuid)/\(i).jpg" + + // 이미 같은 경로가 있는지 확인 (거의 발생하지 않겠지만 안전장치) + if existingPaths.contains(filePath) { + Logger.log(message: "중복된 이미지 경로 발견: \(filePath)", category: .debug) + return + } + let extended = ExtendedImage( filePath: filePath, image: image, @@ -1003,16 +1192,25 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { } dispatchGroup.notify(queue: .main) { + if newImages.isEmpty { + Logger.log(message: "추가할 새 이미지가 없음", category: .debug) + return + } + + Logger.log(message: "새 이미지 \(newImages.count)개 추가", category: .debug) self.images.append(contentsOf: newImages) + if !self.images.isEmpty && !self.images.contains(where: { $0.isMain }) { self.images[0].isMain = true // 첫 번째 이미지를 대표 이미지로 + Logger.log(message: "대표 이미지 설정: \(self.images[0].filePath)", category: .debug) } + self.imagesCollectionView.reloadData() self.updateSaveButtonState() } } - } + private extension PopUpStoreRegisterViewController { private func validateForm() -> Bool { // (1) 팝업스토어 이름 @@ -1138,14 +1336,87 @@ private extension PopUpStoreRegisterViewController { return true } private func updateStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - // 이미지가 수정되었다면 먼저 이미지 업로드 - if !images.isEmpty { - uploadImagesForUpdate(store) + // 기존에 저장된 이미지는 재업로드하지 않고 그대로 사용 + // 새로 추가된 이미지만 업로드 + + // 새로 추가된 이미지만 필터링 + let newImages = images.filter { image in + return !originalImageIds.keys.contains(image.filePath) + } + + if !newImages.isEmpty { + // 새 이미지만 업로드 + uploadNewImagesForUpdate(store, newImages: newImages) } else { - // 이미지 수정이 없다면 바로 스토어 정보 업데이트 + // 새 이미지가 없으면 바로 스토어 정보 업데이트 updateStoreInfo(store, updatedImagePaths: nil) } } + private func uploadNewImagesForUpdate(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore, newImages: [ExtendedImage]) { + let uuid = UUID().uuidString + let updatedImages = newImages.enumerated().map { index, image in + let filePath = "PopUpImage/\(nameField?.text ?? "")/\(uuid)/\(index).jpg" + return ExtendedImage( + filePath: filePath, + image: image.image, + isMain: image.isMain) + } + + presignedService.tryUpload(datas: updatedImages.map { + PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) + }) + .subscribe( + onSuccess: { [weak self] _ in + guard let self = self else { return } + Logger.log(message: "새 이미지 업로드 성공", category: .info) + + // 모든 이미지 경로 (기존 이미지 + 새 이미지) + var allPaths: [String] = [] + + // 삭제되지 않은 기존 이미지 경로 추가 + let deletedIdSet = Set(self.deletedImageIds) + for (path, id) in self.originalImageIds { + if !deletedIdSet.contains(id) { + allPaths.append(path) + } + } + + // 새로 업로드된 이미지 경로 추가 + let newPaths = updatedImages.map { $0.filePath } + allPaths.append(contentsOf: newPaths) + + // 메인 이미지 경로 결정 + let mainImage: String + if let mainImg = self.images.first(where: { $0.isMain }) { + if self.originalImageIds.keys.contains(mainImg.filePath) { + // 기존 이미지가 메인 + mainImage = mainImg.filePath + } else { + // 새 이미지가 메인인 경우, 새 경로 찾기 + let idx = self.images.firstIndex(where: { $0.filePath == mainImg.filePath }) ?? 0 + if idx < updatedImages.count { + mainImage = updatedImages[idx].filePath + } else { + mainImage = updatedImages.first?.filePath ?? "" + } + } + } else if !allPaths.isEmpty { + mainImage = allPaths[0] + } else { + mainImage = "" + } + + self.updateStoreInfo(store, updatedImagePaths: allPaths, mainImage: mainImage) + }, + onError: { [weak self] error in + Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) + self?.showErrorAlert(message: "이미지 업로드 실패: \(error.localizedDescription)") + } + ) + .disposed(by: disposeBag) + } + + private func uploadImagesForUpdate(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { let uuid = UUID().uuidString let updatedImages = images.enumerated().map { index, image in @@ -1174,7 +1445,7 @@ private extension PopUpStoreRegisterViewController { .disposed(by: disposeBag) } - private func updateStoreInfo(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore, updatedImagePaths: [String]?) { + private func updateStoreInfo(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore, updatedImagePaths: [String]?, mainImage: String? = nil) { guard let name = nameField?.text, let address = addressField?.text, let latitude = Double(latField?.text ?? ""), @@ -1184,9 +1455,27 @@ private extension PopUpStoreRegisterViewController { return } - // 업데이트할 이미지가 있다면 첫 번째 값을 대표 이미지로, 없으면 기존 스토어의 mainImageUrl 사용 - let mainImage = updatedImagePaths?.first ?? store.mainImageUrl + // 메인 이미지 결정 + let finalMainImage: String + if let mainImagePath = mainImage { + finalMainImage = mainImagePath + } else if let firstImage = updatedImagePaths?.first { + finalMainImage = firstImage + } else { + // 이미지가 없는 경우 기존 메인 이미지 사용 + finalMainImage = store.mainImageUrl + } + // 이미지 URL 목록 결정 + let imageUrls: [String] + if let paths = updatedImagePaths, !paths.isEmpty { + imageUrls = paths + } else { + // 이미지 변동이 없을 경우 + imageUrls = [store.mainImageUrl] + } + + // 서버에 스토어 정보 업데이트 요청 let request = UpdatePopUpStoreRequestDTO( popUpStore: .init( id: store.id, @@ -1196,9 +1485,9 @@ private extension PopUpStoreRegisterViewController { address: address, startDate: getFormattedDate(from: selectedStartDate), endDate: getFormattedDate(from: selectedEndDate), - mainImageUrl: mainImage, - bannerYn: !mainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, - imageUrl: updatedImagePaths ?? [store.mainImageUrl], + mainImageUrl: finalMainImage, + bannerYn: !finalMainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + imageUrl: imageUrls, startDateBeforeEndDate: true ), location: .init( @@ -1207,17 +1496,34 @@ private extension PopUpStoreRegisterViewController { markerTitle: "마커 제목", markerSnippet: "마커 설명" ), - imagesToAdd: updatedImagePaths ?? [], - imagesToDelete: [] // 필요한 경우 기존 이미지 삭제 로직 추가 - ) - + imagesToAdd: updatedImagePaths?.filter { !originalImageIds.keys.contains($0) } ?? [], + imagesToDelete: deletedImageIds + ) + // 요청 데이터 로깅 + Logger.log(message: "업데이트 요청 데이터: \(request)", category: .debug) adminUseCase.updateStore(request: request) .subscribe( onNext: { [weak self] _ in + guard let self = self else { return } Logger.log(message: "updateStore API 호출 성공", category: .info) - self?.showSuccessAlert(isUpdate: true) + + // 서버 응답이 성공하면 S3에서 이미지 삭제 수행 + self.deleteImagesFromS3() + + // 성공 시 저장된 삭제 이미지 정보 초기화 + if let storeId = self.editingStore?.id { + UserDefaults.standard.removeObject(forKey: "deletedImageIds_\(storeId)") + UserDefaults.standard.removeObject(forKey: "deletedImagePaths_\(storeId)") + Logger.log(message: "삭제된 이미지 정보 영구 저장소에서 제거 완료", category: .debug) + } + + // 메모리 내 삭제 목록도 초기화 + self.deletedImageIds.removeAll() + self.deletedImagePaths.removeAll() + + self.showSuccessAlert(isUpdate: true) }, onError: { [weak self] error in Logger.log(message: "updateStore API 호출 실패: \(error.localizedDescription)", category: .error) @@ -1225,6 +1531,23 @@ private extension PopUpStoreRegisterViewController { } ) .disposed(by: disposeBag) + + } + private func deleteImagesFromS3() { + // 삭제해야 할 이미지가 없으면 바로 리턴 + guard !deletedImagePaths.isEmpty else { return } + + // 모든 이미지 한 번에 삭제 + presignedService.tryDelete(targetPaths: .init(objectKeyList: deletedImagePaths)) + .subscribe( + onCompleted: { + Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(self.deletedImagePaths.count)개", category: .info) + }, + onError: { error in + Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) + } + ) + .disposed(by: disposeBag) } private func showSuccessAlert(isUpdate: Bool = false) { @@ -1450,6 +1773,7 @@ private extension PopUpStoreRegisterViewController { + private func showSuccessAlert() { let alert = UIAlertController( title: "등록 성공", @@ -1499,7 +1823,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { return Observable.create { observer in let geocoder = CLGeocoder() let fullAddress = "\(address), Korea" - + geocoder.geocodeAddressString( fullAddress, in: nil, @@ -1511,7 +1835,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { observer.onCompleted() return } - + if let location = placemarks?.first?.location { observer.onNext(location) } else { @@ -1519,7 +1843,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { } observer.onCompleted() } - + return Disposables.create() } } @@ -1532,23 +1856,23 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { }) .disposed(by: disposeBag) } - - + + @objc private func addressFieldDidChange(_ textField: UITextField) { guard let address = textField.text, !address.isEmpty else { return } - + // 한국 주소임을 명시 let geocoder = CLGeocoder() let addressWithCountry = address + ", South Korea" - + geocoder.geocodeAddressString(addressWithCountry) { [weak self] placemarks, error in if let error = error { print("Geocoding error: \(error.localizedDescription)") return } - + guard let location = placemarks?.first?.location else { return } - + DispatchQueue.main.async { self?.latField?.text = String(format: "%.6f", location.coordinate.latitude) self?.lonField?.text = String(format: "%.6f", location.coordinate.longitude) @@ -1556,5 +1880,11 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { } } } - + +} +extension PopUpStoreRegisterViewController: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + Logger.log(message: "설명 필드 값 변경: \(textView.text.count) 글자", category: .debug) + updateSaveButtonState() + } } diff --git a/Poppool/Poppool/Presentation/Admin/AdminViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminViewController.swift index 24e86807..f3a77fac 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminViewController.swift @@ -182,21 +182,46 @@ final class AdminViewController: BaseViewController, View { } private func deleteStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - let imageService = PreSignedService() - - imageService.tryDelete(targetPaths: .init(objectKeyList: [store.mainImageUrl])) - .andThen(adminUseCase.deleteStore(id: store.id)) + // 먼저 스토어 상세 정보를 가져와 모든 이미지 URL을 확인 + adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) .subscribe( - onNext: { [weak self] _ in - self?.reactor?.action.onNext(.reloadData) - ToastMaker.createToast(message: "삭제되었습니다") + onNext: { [weak self] storeDetail in + guard let self = self else { return } + + var allImageUrls = [String]() + + allImageUrls.append(storeDetail.mainImageUrl) + + // 다른 모든 이미지 URL 추가 + let otherImageUrls = storeDetail.imageList.map { $0.imageUrl } + allImageUrls.append(contentsOf: otherImageUrls) + + allImageUrls = Array(Set(allImageUrls)) + + Logger.log(message: "삭제할 이미지: \(allImageUrls.count)개", category: .debug) + + let imageService = PreSignedService() + imageService.tryDelete(targetPaths: .init(objectKeyList: allImageUrls)) + .andThen(self.adminUseCase.deleteStore(id: store.id)) + .observe(on: MainScheduler.instance) + .subscribe( + onNext: { [weak self] _ in + self?.reactor?.action.onNext(.reloadData) + ToastMaker.createToast(message: "삭제되었습니다") + }, + onError: { [weak self] error in + self?.showErrorAlert(message: "삭제 실패: \(error.localizedDescription)") + } + ) + .disposed(by: self.disposeBag) }, onError: { [weak self] error in - self?.showErrorAlert(message: "삭제 실패: \(error.localizedDescription)") + self?.showErrorAlert(message: "스토어 정보 조회 실패: \(error.localizedDescription)") } ) .disposed(by: disposeBag) + } private func showErrorAlert(message: String) { let alert = UIAlertController( diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 1cf7084a..083dfdc3 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -8,13 +8,20 @@ import CoreLocation class FullScreenMapViewController: MapViewController { // MARK: - Properties private var initialStore: MapPopUpStore? + private var isFullScreenMode = true // 풀스크린 모드 플래그 추가 + private var markerLocked = false // 마커 상태 잠금 플래그 + private var initialMarker: NMFMarker? + // MARK: - Initialization - init(store: MapPopUpStore?) { + init(store: MapPopUpStore?, existingMarker: NMFMarker? = nil) { self.initialStore = store + self.initialMarker = existingMarker super.init() } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -23,23 +30,55 @@ class FullScreenMapViewController: MapViewController { override func viewDidLoad() { super.viewDidLoad() setupFullScreenUI() + setupNavigation() +// configureInitialMapPosition() + self.navigationController?.navigationBar.isHidden = false + Logger.log(message: "💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) configureInitialMapPosition() - // 풀스크린 모드에서 별도 터치 델리게이트 설정 + + Logger.log(message: "✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) + mainView.mapView.touchDelegate = self } + private func setupNavigation() { + navigationItem.title = "찾아가는 길" + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.shadowColor = .clear + appearance.backgroundColor = .white + appearance.titleTextAttributes = [ + .foregroundColor: UIColor.black, + .font: UIFont.systemFont(ofSize: 15, weight: .regular) + ] + navigationController?.navigationBar.standardAppearance = appearance + navigationController?.navigationBar.scrollEdgeAppearance = appearance + navigationItem.leftBarButtonItem = UIBarButtonItem( + image: UIImage(named: "bakcbutton")?.withRenderingMode(.alwaysOriginal), + style: .plain, + target: self, + action: #selector(backButtonTapped) + ) + navigationItem.leftBarButtonItem?.imageInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0) + } + + @objc private func backButtonTapped() { + dismiss(animated: true) + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) navigationController?.setNavigationBarHidden(false, animated: false) tabBarController?.tabBar.isHidden = true + markerLocked = true // 마커 상태 잠금 } override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setNavigationBarHidden(false, animated: false) tabBarController?.tabBar.isHidden = false - navigationItem.title = "찾아가는 길" + navigationItem.title = "찾아가는 길" } // MARK: - Setup @@ -50,24 +89,6 @@ class FullScreenMapViewController: MapViewController { mainView.searchInput.isHidden = true carouselView.isHidden = false - // 닫기 버튼 추가 - let closeButton = UIButton(type: .system) - closeButton.setImage(UIImage(systemName: "xmark"), for: .normal) - closeButton.tintColor = .black - view.addSubview(closeButton) - - closeButton.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).offset(16) - make.trailing.equalToSuperview().offset(-16) - make.width.height.equalTo(44) - } - - closeButton.rx.tap - .subscribe(onNext: { [weak self] in - self?.dismiss(animated: true, completion: nil) - }) - .disposed(by: disposeBag) // 상위 클래스의 disposeBag 사용 - mainView.mapView.snp.remakeConstraints { make in make.edges.equalToSuperview() } @@ -83,16 +104,38 @@ class FullScreenMapViewController: MapViewController { guard let store = initialStore else { return } let position = NMGLatLng(lat: store.latitude, lng: store.longitude) + + // 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: position, zoomTo: 15.0) + cameraUpdate.animation = .easeIn + cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - // 여기서 마커를 선택 상태로 추가 - let marker = NMFMarker() - marker.position = position - marker.userInfo = ["storeData": store] - updateMarkerStyle(marker: marker, selected: true, isCluster: false) // 선택된 스타일 적용 - marker.mapView = mainView.mapView - currentMarker = marker + // 기존 마커가 있으면 재활용 + if let existingMarker = initialMarker { + // 기존 마커가 맵뷰에 설정되어 있지 않으면 설정 + if existingMarker.mapView == nil { + existingMarker.mapView = mainView.mapView + } + + // 마커 스타일 업데이트 + updateMarkerStyle(marker: existingMarker, selected: true, isCluster: false, count: 1) + currentMarker = existingMarker + } else { + // 기존 마커가 없는 경우에만 새로 생성 + let marker = NMFMarker() + marker.position = position + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.userInfo = ["storeData": store] + marker.mapView = mainView.mapView + currentMarker = marker + + // 마커 스타일 업데이트 + updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: 1) + } // 캐러셀 설정 currentCarouselStores = [store] @@ -100,54 +143,120 @@ class FullScreenMapViewController: MapViewController { carouselView.isHidden = false } - // MARK: - Override Methods + override func bind(reactor: MapReactor) { super.bind(reactor: reactor) - // 풀스크린 모드에서 추가 바인딩 필요 시 여기에 작성 + + // 캐러셀 상태 관찰 + carouselView.rx.observe(Bool.self, "isHidden") + .distinctUntilChanged() + .subscribe(onNext: { [weak self] isHidden in + if let isHidden = isHidden, isHidden == true, self?.isFullScreenMode == true { + // 풀스크린 모드에서 캐러셀이 숨겨진 경우 다시 표시 + DispatchQueue.main.async { + self?.carouselView.isHidden = false + } + } + }) + .disposed(by: disposeBag) + } + + // 마커 스타일 업데이트 함수 - 항상 TapMarker로만 설정하도록 수정 + private func fullScreenUpdateMarkerStyle(marker: NMFMarker, selected: Bool) { + // 선택 여부와 상관없이 항상 TapMarker + marker.width = 44 + marker.height = 44 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.anchor = CGPoint(x: 0.5, y: 1.0) } + override func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { + if selected { + // 선택된 경우 항상 TapMarker + marker.width = 44 + marker.height = 44 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + } else { + // 선택되지 않은 경우 일반 마커 + marker.width = 32 + marker.height = 32 + marker.iconImage = NMFOverlayImage(name: "Marker") + } + marker.anchor = CGPoint(x: 0.5, y: 1.0) + + if count > 1 { + marker.captionText = "\(count)" + } else { + marker.captionText = "" + } + } override func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true + markerLocked = true // 마커 상태 잠금 + // 이전 마커 선택 상태 해제 if let previousMarker = currentMarker, previousMarker != marker { - updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) + fullScreenUpdateMarkerStyle(marker: previousMarker, selected: false) } - updateMarkerStyle(marker: marker, selected: true, isCluster: false) + // 현재 마커를 TapMarker로 설정 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + fullScreenUpdateMarkerStyle(marker: marker, selected: true) currentMarker = marker + // 캐러셀 업데이트 및 표시 currentCarouselStores = [store] carouselView.updateCards([store]) carouselView.isHidden = false mainView.setStoreCardHidden(false, animated: true) + // 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: 15.0) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - isMovingToMarker = false + // 약간의 지연 후 플래그 리셋 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in + self?.isMovingToMarker = false + } + return true } + // 맵뷰 탭 처리 오버라이드 + override func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { + // 풀스크린 모드에서는 맵 탭 시 아무 동작도 하지 않음 (캐러셀 유지) + return + } + + // 카메라 이동 시작 시 호출 + override func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) { + // 풀스크린 모드에서는 캐러셀 유지 + if isFullScreenMode && markerLocked { + // 상위 클래스의 기본 동작 방지 + return + } + super.mapView(mapView, cameraWillChangeByReason: reason, animated: animated) + } + + // 카메라 이동 중 호출 + override func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) { + // 마커가 잠겨있을 때는 캐러셀 유지 + if isFullScreenMode && markerLocked { + // 기존 동작을 방지하고 풀스크린 동작 수행 + return + } + super.mapView(mapView, cameraIsChangingByReason: reason) + } + override func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { - return false // 풀스크린에서는 클러스터 비활성화 + return false } override func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { - return false // 풀스크린에서는 마이크로 클러스터 비활성화 + return false } } - -// MARK: - NMFMapViewTouchDelegate -//extension FullScreenMapViewController: NMFMapViewTouchDelegate { -// func mapView(_ mapView: NMFMapView, didTapOverlay overlay: NMFOverlay) -> Bool { -// guard let marker = overlay as? NMFMarker, -// let store = marker.userInfo["storeData"] as? MapPopUpStore else { return false } -// return handleSingleStoreTap(marker, store: store) -// } -// -// func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { -// // 풀스크린 모드에서는 지도 탭 시 동작 없음 -// } -//} diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index 12c65b08..78a5d81a 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -10,6 +10,7 @@ final class MapGuideViewController: UIViewController, View { var disposeBag = DisposeBag() private let popUpStoreId: Int64 private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 + init(popUpStoreId: Int64) { self.popUpStoreId = popUpStoreId @@ -156,7 +157,7 @@ final class MapGuideViewController: UIViewController, View { if let selectedStore = self.currentCarouselStores.first { reactor.action.onNext(.didSelectItem(selectedStore)) - // store: 매개변수명 사용 + // 기존 코드 그대로 사용 let fullScreenMapVC = FullScreenMapViewController(store: selectedStore) fullScreenMapVC.reactor = reactor @@ -171,8 +172,8 @@ final class MapGuideViewController: UIViewController, View { .distinctUntilChanged() .compactMap { $0 } .take(1) + .observe(on: MainScheduler.instance) .subscribe(onNext: { store in - // store: 매개변수명 사용 let fullScreenMapVC = FullScreenMapViewController(store: store) fullScreenMapVC.reactor = reactor @@ -186,6 +187,7 @@ final class MapGuideViewController: UIViewController, View { .disposed(by: disposeBag) + // 목적지 좌표로 마커 및 카메라 설정 reactor.state .map { $0.destinationCoordinate } @@ -257,15 +259,15 @@ final class MapGuideViewController: UIViewController, View { make.height.equalTo(320) } - mapView.addSubview(expandButton) + modalCardView.addSubview(expandButton) expandButton.snp.makeConstraints { make in - make.bottom.equalToSuperview().inset(10) - make.trailing.equalToSuperview().inset(10) + make.bottom.equalTo(mapView.snp.bottom).offset(-10) + make.trailing.equalTo(mapView.snp.trailing).offset(-10) make.width.height.equalTo(32) } let bottomContainer = UIView() - modalCardView.addSubview(bottomContainer) // 오타 수정 필요: bottomContainer로 변경 + modalCardView.addSubview(bottomContainer) bottomContainer.snp.makeConstraints { make in make.top.equalTo(mapView.snp.bottom).offset(20) make.leading.trailing.equalToSuperview().inset(20) @@ -312,15 +314,14 @@ final class MapGuideViewController: UIViewController, View { } private func setupMarker(at coordinate: CLLocationCoordinate2D) { - // 기존 마커 제거 mapView.subviews.forEach { if $0 is NMFMarker { $0.removeFromSuperview() } } // 새 마커 생성 및 설정 let marker = NMFMarker() marker.position = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) - marker.iconImage = NMFOverlayImage(name: "TapMarker") // MapViewController에서 사용하는 기본 마커 - marker.width = 32 - marker.height = 32 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 marker.anchor = CGPoint(x: 0.5, y: 1.0) // 먼저 마커를 지도에 추가 From 2bfc482869e25692b27c73dd5a25a050b19d83b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 24 Mar 2025 21:32:28 +0900 Subject: [PATCH 012/393] =?UTF-8?q?[REFACTOR]=20=20=F0=9F=94=84=20SPM?= =?UTF-8?q?=ED=99=94=20=ED=95=B4=EB=91=94=20=EB=84=A4=EC=9D=B4=EB=B2=84?= =?UTF-8?q?=EB=A7=B5=EC=9D=B4=EC=95=84=EB=8B=8C=20=EA=B3=B5=EC=8B=9D=20?= =?UTF-8?q?=EB=B0=B0=ED=8F=AC=EC=A4=91=EC=9D=B8=20SPM=20=EC=B1=84=ED=83=9D?= =?UTF-8?q?=ED=9B=84=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 28 +++++++++---------- .../Map/MapView/MapViewController.swift | 21 +++----------- 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 026dc060..eac04068 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -373,7 +373,6 @@ 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */; }; 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 08F403322D884F4D00BFA61A /* KakaoSDKUser */; }; 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; - 4E60C2262D8AABBF003A5A37 /* NMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E60C2252D8AABBF003A5A37 /* NMap */; }; 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */; }; 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */; }; 4E685ECE2D12CEB6001EF91C /* BalloonBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAA2D12CEB6001EF91C /* BalloonBackgroundView.swift */; }; @@ -433,6 +432,7 @@ 4EDDEFB42D2D285900CFAFA5 /* DateTimePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */; }; 4EDE57012D5E6A5F0014D924 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */; }; 4EDE57032D5E70650014D924 /* LocationPermissionBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */; }; + 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; 4EE5A3D32D40E4A600A2469A /* MapGuideReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */; }; 4EEA1D8F2D352012003E7DE9 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */; }; 4EEA1D912D352027003E7DE9 /* ExtendedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */; }; @@ -982,8 +982,8 @@ BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, - 4E60C2262D8AABBF003A5A37 /* NMap in Frameworks */, 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, + 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, ); @@ -4089,20 +4089,20 @@ minimumVersion = 2.8.6; }; }; - 4E60C2242D8AABBF003A5A37 /* XCRemoteSwiftPackageReference "NaverMap" */ = { + 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/zzangzzangguy/NaverMap.git"; + repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources.git"; requirement = { - branch = main; - kind = branch; + kind = upToNextMajorVersion; + minimumVersion = 5.0.2; }; }; - 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */ = { + 4EE360FB2D91876300D2441D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources.git"; + repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.0.2; + minimumVersion = 3.21.0; }; }; BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */ = { @@ -4218,16 +4218,16 @@ package = 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */; productName = FloatingPanel; }; - 4E60C2252D8AABBF003A5A37 /* NMap */ = { - isa = XCSwiftPackageProductDependency; - package = 4E60C2242D8AABBF003A5A37 /* XCRemoteSwiftPackageReference "NaverMap" */; - productName = NMap; - }; 4EA9989C2D21C404009DC30B /* RxDataSources */ = { isa = XCSwiftPackageProductDependency; package = 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */; productName = RxDataSources; }; + 4EE360FC2D91876300D2441D /* NMapsMap */ = { + isa = XCSwiftPackageProductDependency; + package = 4EE360FB2D91876300D2441D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; + productName = NMapsMap; + }; BDCA41F12CF35D0D005EECF6 /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */; diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index a2c36d29..98aee4ca 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -402,7 +402,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.resetSelectedMarker() - // 만약 지도 위 마커를 전부 제거하고 싶다면 (상황에 따라) + // 만약 지도 위 마커를 전부 제거 (상황에 따라) // self.clearAllMarkers() // self.clusterMarkerDictionary.values.forEach { $0.mapView = nil } // self.clusterMarkerDictionary.removeAll() @@ -681,8 +681,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func updateMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { - let progress = (maxOffset - offset) / (maxOffset - minOffset) // 0(탑) ~ 1(바텀) - mainView.mapView.alpha = max(0, min(progress, 1)) // 0(완전히 가림) ~ 1(완전히 보임) + let progress = (maxOffset - offset) / (maxOffset - minOffset) + mainView.mapView.alpha = max(0, min(progress, 1)) } @@ -712,21 +712,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.mainView.mapView.isHidden = false self.mainView.searchInput.setBackgroundColor(.white) - // 리스트뷰 표시 시, 전체 한국 영역의 스토어 가져오기 if let reactor = self.reactor { - // 한국 전체 영역에 대한 경계값 설정 - let koreaRegion = ( - northEast: NMGLatLng(lat: 38.0, lng: 132.0), - southWest: NMGLatLng(lat: 33.0, lng: 124.0) - ) - - // 전체 스토어를 가져오기 위해 한국 전체 영역 요청 - reactor.action.onNext(.viewportChanged( - northEastLat: koreaRegion.northEast.lat, - northEastLon: koreaRegion.northEast.lng, - southWestLat: koreaRegion.southWest.lat, - southWestLon: koreaRegion.southWest.lng - )) + reactor.action.onNext(.fetchAllStores) reactor.state .map { $0.viewportStores } From 7821d39917599bd194e6ae60e845495a5722b9f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 26 Mar 2025 18:57:35 +0900 Subject: [PATCH 013/393] =?UTF-8?q?[REFACTOR]=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=ED=83=AD=EC=97=90=EC=84=9C=20=EC=A7=80?= =?UTF-8?q?=EC=97=AD=ED=83=AD=EC=9D=B4=20=EA=B2=B9=EC=B3=90=EB=B3=B4?= =?UTF-8?q?=EC=9D=B4=EB=8D=98=20=EC=9D=B4=EC=8A=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BalloonBackgroundView.swift | 39 +- .../FilterBottomSheetView.swift | 46 +- .../FilterBottomSheetViewController.swift | 489 ++++++++++-------- .../FillterSheetView/FilterChipsView.swift | 2 +- .../Map/MapView/MapViewController.swift | 2 +- .../StoreListViewController.swift | 4 +- 6 files changed, 330 insertions(+), 252 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift index 3c673979..28a18b39 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift @@ -99,9 +99,11 @@ final class BalloonBackgroundView: UIView { private func setupLayout() { addSubview(containerView) containerView.snp.makeConstraints { make in - make.left.right.bottom.equalToSuperview() - make.top.equalToSuperview().offset(arrowHeight) - } + make.left.right.equalToSuperview() + make.top.equalToSuperview().offset(arrowHeight) + make.bottom.equalToSuperview().priority(.high) + } + containerView.addSubview(collectionView) containerView.addSubview(singleRegionIcon) @@ -109,9 +111,11 @@ final class BalloonBackgroundView: UIView { containerView.addSubview(singleRegionDetailLabel) collectionView.snp.makeConstraints { make in - make.edges.equalToSuperview() + make.top.left.right.equalToSuperview() + make.bottom.equalToSuperview().priority(.high) } + singleRegionIcon.snp.makeConstraints { make in make.top.equalToSuperview().offset(24) make.centerX.equalToSuperview() @@ -140,29 +144,25 @@ final class BalloonBackgroundView: UIView { override func draw(_ rect: CGRect) { super.draw(rect) - let arrowWidth: CGFloat = 12 // 화살표 너비 조정 - let arrowHeight: CGFloat = 8 // 화살표 높이 조정 + let arrowWidth: CGFloat = 12 + let arrowHeight: CGFloat = 8 - // 화살표의 시작 x좌표 계산 let arrowX = bounds.width * arrowPosition - (arrowWidth / 2) - // 경로 그리기 let path = UIBezierPath() - // 1. 화살표 그리기 - path.move(to: CGPoint(x: arrowX, y: arrowHeight)) // 왼쪽 아래 - path.addLine(to: CGPoint(x: arrowX + (arrowWidth / 2), y: 0)) // 상단 중앙 - path.addLine(to: CGPoint(x: arrowX + arrowWidth, y: arrowHeight)) // 오른쪽 아래 + path.move(to: CGPoint(x: arrowX, y: arrowHeight)) + path.addLine(to: CGPoint(x: arrowX + (arrowWidth / 2), y: 0)) + path.addLine(to: CGPoint(x: arrowX + arrowWidth, y: arrowHeight)) - // 2. 말풍선 본체 그리기 let balloonRect = CGRect(x: 0, y: arrowHeight, width: bounds.width, height: bounds.height - arrowHeight) - path.addLine(to: CGPoint(x: balloonRect.maxX, y: balloonRect.minY)) // 오른쪽 상단 - path.addLine(to: CGPoint(x: balloonRect.maxX, y: balloonRect.maxY)) // 오른쪽 하단 - path.addLine(to: CGPoint(x: balloonRect.minX, y: balloonRect.maxY)) // 왼쪽 하단 - path.addLine(to: CGPoint(x: balloonRect.minX, y: balloonRect.minY)) // 왼쪽 상단 + path.addLine(to: CGPoint(x: balloonRect.maxX, y: balloonRect.minY)) + path.addLine(to: CGPoint(x: balloonRect.maxX, y: balloonRect.maxY)) + path.addLine(to: CGPoint(x: balloonRect.minX, y: balloonRect.maxY)) + path.addLine(to: CGPoint(x: balloonRect.minX, y: balloonRect.minY)) path.close() @@ -246,7 +246,6 @@ final class BalloonBackgroundView: UIView { collectionView.layoutIfNeeded() - print("실제 contentSize 높이: \(collectionView.collectionViewLayout.collectionViewContentSize.height)") let balloonWidth = self.bounds.width let horizontalSpacing: CGFloat = 8 @@ -254,24 +253,20 @@ final class BalloonBackgroundView: UIView { let rightPadding: CGFloat = 20 let availableWidth = balloonWidth - leftPadding - rightPadding - print("사용 가능한 너비: \(availableWidth)") var currentRowWidth: CGFloat = 0 var numberOfRows: Int = 1 for input in inputDataList { let buttonWidth = calculateButtonWidth(for: input.title ?? "", font: .systemFont(ofSize: 12), isSelected: input.isSelected ?? false) - print("버튼 너비 [\(input.title ?? "")]: \(buttonWidth)") let widthWithSpacing = currentRowWidth == 0 ? buttonWidth : buttonWidth + horizontalSpacing if currentRowWidth + widthWithSpacing > availableWidth { numberOfRows += 1 currentRowWidth = buttonWidth - print("새로운 줄 시작: \(numberOfRows)번째 줄") } else { currentRowWidth += widthWithSpacing - print("현재 줄 너비: \(currentRowWidth)") } } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index 3301184c..67034b7f 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -96,7 +96,7 @@ final class FilterBottomSheetView: UIView { }() - private let buttonStack: UIStackView = { + let buttonStack: UIStackView = { let stack = UIStackView() stack.axis = .horizontal stack.spacing = 12 @@ -158,18 +158,15 @@ final class FilterBottomSheetView: UIView { } private func setupConstraints() { - // 1. 먼저 self의 width 설정 self.snp.makeConstraints { make in make.width.equalTo(UIScreen.main.bounds.width) } - // 2. containerView 설정 containerView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.top.equalTo(headerView.snp.top) } - // 3. headerView 및 내부 요소들 headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(70) @@ -186,13 +183,11 @@ final class FilterBottomSheetView: UIView { make.size.equalTo(24) } - // 4. segmentedControl segmentedControl.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() } - // 5. locationScrollView 및 contentView locationScrollView.snp.makeConstraints { make in make.top.equalTo(segmentedControl.snp.bottom).offset(20) make.leading.trailing.equalToSuperview() @@ -204,28 +199,24 @@ final class FilterBottomSheetView: UIView { make.height.equalToSuperview() } - // 6. categoryCollectionView categoryCollectionView.snp.makeConstraints { make in make.top.equalTo(segmentedControl.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() // categoryHeightConstraint = make.height.equalTo(160).constraint } - // 7. balloonBackgroundView balloonBackgroundView.snp.makeConstraints { make in make.top.equalTo(locationScrollView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(16) balloonHeightConstraint = make.height.equalTo(0).constraint } - // 8. filterChipsView filterChipsView.snp.makeConstraints { make in make.top.equalTo(balloonBackgroundView.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(80) } - // 9. buttonStack buttonStack.snp.makeConstraints { make in make.top.equalTo(filterChipsView.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(16) @@ -314,7 +305,7 @@ final class FilterBottomSheetView: UIView { } func updateContentVisibility(isCategorySelected: Bool) { - UIView.animate(withDuration: 0.3) { + UIView.performWithoutAnimation { self.locationScrollView.alpha = isCategorySelected ? 0 : 1 self.balloonBackgroundView.alpha = isCategorySelected ? 0 : 1 self.categoryCollectionView.alpha = isCategorySelected ? 1 : 0 @@ -323,6 +314,17 @@ final class FilterBottomSheetView: UIView { self.balloonBackgroundView.isHidden = isCategorySelected self.categoryCollectionView.isHidden = !isCategorySelected + // filterChipsView 제약조건 업데이트 + self.filterChipsView.snp.remakeConstraints { make in + if isCategorySelected { + make.top.equalTo(self.categoryCollectionView.snp.bottom).offset(16) + } else { + make.top.equalTo(self.balloonBackgroundView.snp.bottom).offset(24) + } + make.leading.trailing.equalToSuperview().inset(16) + make.height.equalTo(80) + } + let newHeight = isCategorySelected ? 170 : self.balloonBackgroundView.calculateHeight() self.balloonHeightConstraint?.update(offset: newHeight) @@ -333,6 +335,7 @@ final class FilterBottomSheetView: UIView { + private func createStyledButton(title: String, isSelected: Bool = false) -> PPButton { let button = PPButton( style: .secondary, @@ -375,14 +378,22 @@ final class FilterBottomSheetView: UIView { } func updateBalloonHeight(isHidden: Bool, dynamicHeight: CGFloat = 160) { - UIView.animate(withDuration: 0.3) { - self.balloonBackgroundView.alpha = isHidden ? 0 : 1 - self.balloonHeightConstraint?.update(offset: isHidden ? 0 : dynamicHeight) - self.layoutIfNeeded() + if isHidden { + balloonBackgroundView.alpha = 0 + balloonBackgroundView.isHidden = true + balloonHeightConstraint?.update(offset: 0) + } else { + balloonBackgroundView.alpha = 1 + balloonBackgroundView.isHidden = false + balloonHeightConstraint?.update(offset: dynamicHeight) } + + self.setNeedsLayout() + self.layoutIfNeeded() } + func updateBalloonPosition(for button: UIButton) { guard let window = button.window else { return } @@ -395,8 +406,8 @@ final class FilterBottomSheetView: UIView { let position = relativeX / balloonBackgroundView.bounds.width - let minPosition: CGFloat = 0.1 // 왼쪽 여백 - let maxPosition: CGFloat = 0.9 // 오른쪽 여백 + let minPosition: CGFloat = 0.1 + let maxPosition: CGFloat = 0.9 let clampedPosition = min(maxPosition, max(minPosition, position)) balloonBackgroundView.arrowPosition = clampedPosition @@ -433,7 +444,6 @@ extension FilterBottomSheetView: UIScrollViewDelegate { return button.backgroundColor == .blu500 }) as? PPButton else { return } - // 스크롤 중에도 실시간으로 위치 업데이트 DispatchQueue.main.async { [weak self] in self?.updateBalloonPosition(for: selectedButton) } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift index c398fc99..67663ca4 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift @@ -1,3 +1,4 @@ + import UIKit import SnapKit import RxSwift @@ -12,21 +13,28 @@ final class FilterBottomSheetViewController: UIViewController, View { var disposeBag = DisposeBag() var onSave: ((FilterData) -> Void)? var onDismiss: (() -> Void)? + + // Container height를 업데이트할 때 SnapKit Constraint를 직접 저장해 둠 private var bottomConstraint: Constraint? + private var containerHeightConstraint: Constraint? + + // 바텀시트 실제 UI let containerView = FilterBottomSheetView() - private var containerViewBottomConstraint: NSLayoutConstraint? + + // 필요하다면 다른 속성들 private var savedLocation: String? private var savedCategory: String? private var tagSection: TagSection? - private lazy var dimmedView: UIView = { - let view = UIView() - view.backgroundColor = .black.withAlphaComponent(0.4) - view.alpha = 0 - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapDimmedView)) - view.addGestureRecognizer(tapGesture) - return view + private lazy var dimmedView: UIControl = { + let control = UIControl() + control.backgroundColor = .black.withAlphaComponent(0.4) + control.alpha = 0 + control.addTarget(self, action: #selector(hideBottomSheet), for: .touchUpInside) + return control }() + + // MARK: - Initialization init(reactor: Reactor) { super.init(nibName: nil, bundle: nil) @@ -38,28 +46,32 @@ final class FilterBottomSheetViewController: UIViewController, View { } // MARK: - Lifecycle - override func viewDidLoad() { super.viewDidLoad() - setupLayout() + + setupLayout() // 오토레이아웃 setupGestures() setupCollectionView() + + // ChipsView에서 필터 제거 로직 containerView.filterChipsView.onRemoveChip = { [weak self] removedOption in guard let self = self, let reactor = self.reactor else { return } + if reactor.currentState.selectedCategories.contains(removedOption) { reactor.action.onNext(.toggleCategory(removedOption)) } else if reactor.currentState.selectedSubRegions.contains(removedOption) { reactor.action.onNext(.toggleSubRegion(removedOption)) + + let currentSegment = self.containerView.segmentedControl.selectedSegmentIndex + if currentSegment == 0 { + self.updateUIForCurrentTab(segment: currentSegment) + } } } - -// let tapOutsideGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOutside)) -// tapOutsideGesture.cancelsTouchesInView = false -// self.view.addGestureRecognizer(tapOutsideGesture) - } - // MARK: - Setup + + // MARK: - Setup Layout private func setupLayout() { view.backgroundColor = .clear @@ -71,13 +83,178 @@ final class FilterBottomSheetViewController: UIViewController, View { view.addSubview(containerView) containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() - make.height.equalTo(UIScreen.main.bounds.height * 0.7) + containerHeightConstraint = make.height.equalTo(UIScreen.main.bounds.height * 0.7).constraint + bottomConstraint = make.bottom.equalToSuperview().offset(UIScreen.main.bounds.height).constraint } - view.sendSubviewToBack(dimmedView) - dimmedView.isUserInteractionEnabled = true + containerView.isUserInteractionEnabled = true + } + + // MARK: - Setup Gestures + private func setupGestures() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideBottomSheet)) + tapGesture.delegate = self + dimmedView.addGestureRecognizer(tapGesture) + } + + + +// @objc private func didTapDimmedView() { +// // 딤드 뷰 탭 → 시트 닫기 +// hideBottomSheet() +// } + + @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { + let translation = gesture.translation(in: view) + + switch gesture.state { + case .changed: + guard translation.y >= 0 else { return } + bottomConstraint?.update(offset: translation.y) + view.layoutIfNeeded() + + case .ended: + let velocity = gesture.velocity(in: view) + if translation.y > 150 || velocity.y > 1000 { + // (pan) 시트 끌어내리면 닫기 + let currentSegment = containerView.segmentedControl.selectedSegmentIndex + updateUIForCurrentTab(segment: currentSegment) + hideBottomSheet() + } else { + UIView.animate(withDuration: 0.25) { + self.bottomConstraint?.update(offset: 0) + self.view.layoutIfNeeded() + } + } + default: + break + } + } + + // MARK: - Public Show / Hide + func showBottomSheet() { + guard let reactor = reactor else { return } + + // (A) location 초기선택 + if let locations = reactor.currentState.savedSubRegions.first?.split(separator: "/").first.map(String.init), + let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { + reactor.action.onNext(.selectLocation(index)) + } + + // (B) 필터 칩 뷰 업데이트 + containerView.update( + locationText: reactor.currentState.savedSubRegions.joined(separator: ", "), + categoryText: reactor.currentState.savedCategories.joined(separator: ", ") + ) + + // (C) 시트 애니메이션 + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { + self.dimmedView.alpha = 1 + self.bottomConstraint?.update(offset: 0) + self.view.layoutIfNeeded() + } + } + + @objc func hideBottomSheet() { + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { + self.dimmedView.alpha = 0 + self.bottomConstraint?.update(offset: UIScreen.main.bounds.height) + self.view.layoutIfNeeded() + } completion: { _ in + self.dismiss(animated: false) + self.onDismiss?() + } + } + + // MARK: - UI Update (for tabs) + private func updateUIForCurrentTab(segment: Int) { + UIView.performWithoutAnimation { + // 탭 전환 시 모든 뷰 숨김 + containerView.categoryCollectionView.isHidden = true + containerView.locationScrollView.isHidden = true + containerView.balloonBackgroundView.isHidden = true + + if segment == 0 { + // 지역 탭 + containerView.locationScrollView.isHidden = false + if let selectedLocationIndex = reactor?.currentState.selectedLocationIndex, + let locations = reactor?.currentState.locations, + selectedLocationIndex >= 0, selectedLocationIndex < locations.count { + + let location = locations[selectedLocationIndex] + containerView.balloonBackgroundView.configure( + for: location.main, + subRegions: location.sub, + selectedRegions: reactor?.currentState.selectedSubRegions ?? [], + selectionHandler: { [weak self] subRegion in + self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) + }, + allSelectionHandler: { [weak self] in + self?.reactor?.action.onNext(.toggleAllSubRegions) + } + ) + containerView.balloonBackgroundView.isHidden = false + let dynamicHeight = containerView.balloonBackgroundView.calculateHeight() + containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) + } + } else { + containerView.categoryCollectionView.isHidden = false + containerView.updateBalloonHeight(isHidden: true) + + // ★ 카테고리 탭에 들어오면, 한 번 더 layout 후 시트 높이 갱신 + DispatchQueue.main.async { + // 콜렉션뷰 레이아웃 강제 반영 + self.containerView.categoryCollectionView.layoutIfNeeded() + let contentHeight = self.containerView.categoryCollectionView.contentSize.height + + // 콜렉션뷰 높이 업데이트 + self.containerView.categoryCollectionView.snp.updateConstraints { make in + make.height.equalTo(contentHeight + 40) + } + self.containerView.layoutIfNeeded() + + // 시트 높이 갱신 + self.updateContainerHeight() + } + } + + containerView.layoutIfNeeded() + view.layoutIfNeeded() + } + + // 시트 높이 업데이트 + updateContainerHeight() + } + + private func updateContainerHeight() { + let segmentIndex = containerView.segmentedControl.selectedSegmentIndex + + let headerHeight = containerView.headerView.frame.height + let segmentHeight = containerView.segmentedControl.frame.height + let filterHeight = containerView.filterChipsView.frame.height + let buttonHeight: CGFloat = 52 + let padding: CGFloat = 60 + + let contentHeight: CGFloat + if segmentIndex == 0 { + let locationHeight = containerView.locationScrollView.frame.height + let balloonHeight = containerView.balloonBackgroundView.isHidden ? 0 : containerView.balloonBackgroundView.calculateHeight() + contentHeight = headerHeight + segmentHeight + locationHeight + balloonHeight + filterHeight + buttonHeight + padding + } else { + let categoryHeight = containerView.categoryCollectionView.frame.height + contentHeight = headerHeight + segmentHeight + categoryHeight + filterHeight + buttonHeight + padding + } + + let minHeight: CGFloat = 300 + let maxHeight = UIScreen.main.bounds.height * 0.7 + let newHeight = min(max(contentHeight, minHeight), maxHeight) + + UIView.animate(withDuration: 0.2) { + self.containerHeightConstraint?.update(offset: newHeight) + self.view.layoutIfNeeded() + } } private func setupCollectionView() { @@ -85,60 +262,57 @@ final class FilterBottomSheetViewController: UIViewController, View { containerView.categoryCollectionView.delegate = self } - // MARK: - Binding + // MARK: - Reactor Binding func bind(reactor: Reactor) { - // 1. 세그먼트 컨트롤 바인딩 + // (1) 세그먼트 컨트롤 containerView.segmentedControl.rx.selectedSegmentIndex + .do(onNext: { [weak self] segmentIndex in + self?.updateUIForCurrentTab(segment: segmentIndex) + self?.updateContainerHeight() + }) .map { Reactor.Action.segmentChanged($0) } .bind(to: reactor.action) .disposed(by: disposeBag) - // 2. 리셋 버튼 바인딩 + // (2) 리셋 버튼 containerView.resetButton.rx.tap - .do(onNext: { [weak self] _ in - guard let self = self, - let reactor = self.reactor, - let selectedIndex = reactor.currentState.selectedLocationIndex else { return } - - let location = reactor.currentState.locations[selectedIndex] - // 현재 location에 대한 configure 재설정 - self.containerView.balloonBackgroundView.configure( - for: location.main, - subRegions: location.sub, - selectedRegions: reactor.currentState.selectedSubRegions, - selectionHandler: { [weak self] subRegion in - self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) - }, - allSelectionHandler: { [weak self] in - self?.reactor?.action.onNext(.toggleAllSubRegions) - } - ) + .do(onNext: { [weak self] _ in + guard let self = self, + let reactor = self.reactor, + let selectedIndex = reactor.currentState.selectedLocationIndex else { return } + let location = reactor.currentState.locations[selectedIndex] + self.containerView.balloonBackgroundView.configure( + for: location.main, + subRegions: location.sub, + selectedRegions: reactor.currentState.selectedSubRegions, + selectionHandler: { [weak self] subRegion in + self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) + }, + allSelectionHandler: { [weak self] in + self?.reactor?.action.onNext(.toggleAllSubRegions) + } + ) + }) + .map { Reactor.Action.resetFilters } + .bind(to: reactor.action) + .disposed(by: disposeBag) - }) - .map { Reactor.Action.resetFilters } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - - + // (3) 저장 containerView.saveButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } - let filterData: FilterData = ( locations: reactor.currentState.selectedSubRegions, categories: reactor.currentState.selectedCategories ) - self.onSave?(filterData) reactor.action.onNext(.applyFilters(filterData.locations + filterData.categories)) - self.hideBottomSheet() } .disposed(by: disposeBag) - + // (4) 닫기 containerView.closeButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -151,8 +325,7 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - - // 5. 탭 변경 + // (5) 액티브 세그먼트 reactor.state.map { $0.activeSegment } .distinctUntilChanged() .bind { [weak self] activeSegment in @@ -160,24 +333,20 @@ final class FilterBottomSheetViewController: UIViewController, View { if activeSegment == 0 { let dynamicHeight = self.containerView.balloonBackgroundView.calculateHeight() self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) - } else if activeSegment == 1 { + } else { self.containerView.updateBalloonHeight(isHidden: true) } self.containerView.updateContentVisibility(isCategorySelected: activeSegment == 1) } .disposed(by: disposeBag) - // 6. 위치 데이터 바인딩 - let locations = reactor.state - .map { $0.locations } - .distinctUntilChanged() - .share(replay: 1) + // (6) 위치 리스트 + let locations = reactor.state.map { $0.locations }.distinctUntilChanged().share(replay: 1) locations .observe(on: MainScheduler.instance) .bind { [weak self] locations in self?.containerView.setupLocationScrollView(locations: locations) { [weak self] index, button in guard let self = self else { return } - if index == 0 { if let selectedSubRegions = self.reactor?.currentState.selectedSubRegions, !selectedSubRegions.isEmpty { @@ -187,38 +356,39 @@ final class FilterBottomSheetViewController: UIViewController, View { } } self.reactor?.action.onNext(.selectLocation(index)) - self.containerView.updateBalloonPosition(for: button) } } .disposed(by: disposeBag) - - let locationAndSubRegions = reactor.state - .map { ($0.selectedLocationIndex, $0.selectedSubRegions) } + // (7) locationAndSubRegions + reactor.state.map { ($0.selectedLocationIndex, $0.selectedSubRegions) } .distinctUntilChanged { prev, curr in let isIndexSame = prev.0 == curr.0 let isSubRegionsSame = prev.1 == curr.1 return isIndexSame && isSubRegionsSame } .share(replay: 1) - - locationAndSubRegions .observe(on: MainScheduler.instance) .bind { [weak self] data in guard let self = self, let reactor = self.reactor else { return } let (selectedIndexOptional, selectedSubRegions) = data - guard let selectedIndex = selectedIndexOptional, selectedIndex >= 0, selectedIndex < reactor.currentState.locations.count else { return } + // 현재 탭이 지역(0)인지 체크해서 풍선 뷰 노출할지 결정 + let currentSegment = self.containerView.segmentedControl.selectedSegmentIndex + if currentSegment != 0 { + return + } + let location = reactor.currentState.locations[selectedIndex] self.containerView.balloonBackgroundView.configure( - for: location.main, // 첫 번째 인자는 메인 지역(String) - subRegions: location.sub, // 두 번째 인자는 [String] - selectedRegions: selectedSubRegions, // 세 번째 인자는 [String] + for: location.main, + subRegions: location.sub, + selectedRegions: selectedSubRegions, selectionHandler: { [weak self] subRegion in self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) }, @@ -227,20 +397,23 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - - if let button = self.containerView.locationContentView.subviews[selectedIndex] as? UIButton { + // 화살표 위치 + let subviews = self.containerView.locationContentView.subviews + if selectedIndex < subviews.count, + let button = subviews[selectedIndex] as? UIButton { self.containerView.updateBalloonPosition(for: button) } DispatchQueue.main.async { let dynamicHeight = self.containerView.balloonBackgroundView.calculateHeight() self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) + self.updateContainerHeight() } - self.containerView.balloonBackgroundView.isHidden = false } .disposed(by: disposeBag) + // (8) 카테고리 바인딩 Observable.combineLatest( reactor.state.map { $0.categories }.distinctUntilChanged(), reactor.state.map { $0.selectedCategories }.distinctUntilChanged() @@ -266,14 +439,14 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - - + // (9) 필터칩 업데이트 reactor.state.map { $0.selectedSubRegions + $0.selectedCategories } .distinctUntilChanged() .bind { [weak self] selectedOptions in UIView.performWithoutAnimation { self?.containerView.filterChipsView.updateChips(with: selectedOptions) self?.containerView.layoutIfNeeded() + self?.updateContainerHeight() } } .disposed(by: disposeBag) @@ -288,7 +461,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - reactor.state.map { $0.isSaveEnabled } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -305,173 +477,74 @@ final class FilterBottomSheetViewController: UIViewController, View { } } .disposed(by: disposeBag) - Observable.just(()) - .withLatestFrom(reactor.state) - .take(1) - .subscribe(onNext: { [weak self] state in - // 저장된 지역 필터 설정 - if !state.savedSubRegions.isEmpty { - state.savedSubRegions.forEach { region in - reactor.action.onNext(.toggleSubRegion(region)) - } - } - // 저장된 카테고리 필터 설정 - if !state.savedCategories.isEmpty { - state.savedCategories.forEach { category in - reactor.action.onNext(.toggleCategory(category)) - } + Observable.just(()) + .withLatestFrom(reactor.state) + .take(1) + .subscribe(onNext: { [weak self] state in + // 저장된 지역 필터 + if !state.savedSubRegions.isEmpty { + state.savedSubRegions.forEach { region in + reactor.action.onNext(.toggleSubRegion(region)) } - - // 지역이 선택되어 있다면 해당 지역 버튼도 활성화 - if let locations = state.savedSubRegions.first?.split(separator: "/").first.map(String.init), - let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { - reactor.action.onNext(.selectLocation(index)) + } + // 저장된 카테고리 필터 + if !state.savedCategories.isEmpty { + state.savedCategories.forEach { category in + reactor.action.onNext(.toggleCategory(category)) } - }) - .disposed(by: disposeBag) - - Observable.combineLatest( - reactor.state.map { $0.savedSubRegions }.distinctUntilChanged(), - reactor.state.map { $0.savedCategories }.distinctUntilChanged() - ) - .take(1) - .subscribe(onNext: { [weak self] (subRegions, categories) in - guard let self = self else { return } - - subRegions.forEach { region in - reactor.action.onNext(.toggleSubRegion(region)) } - categories.forEach { category in - reactor.action.onNext(.toggleCategory(category)) + // 위치 선택 + if let locMain = state.savedSubRegions.first?.split(separator: "/").first.map(String.init), + let idx = reactor.currentState.locations.firstIndex(where: { $0.main == locMain }) { + reactor.action.onNext(.selectLocation(idx)) } - - self.containerView.categoryCollectionView.reloadData() - self.containerView.balloonBackgroundView.setNeedsDisplay() }) .disposed(by: disposeBag) + // (12) 추가 combineLatest... (생략) } +} - private func updateContentVisibility(_ isCategoryTab: Bool, subRegionCount: Int) { - UIView.animate(withDuration: 0.3) { - self.containerView.updateContentVisibility(isCategorySelected: isCategoryTab) - if !isCategoryTab { - self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: subRegionCount > 0 ? self.containerView.balloonBackgroundView.calculateHeight() : 80) - } - self.view.layoutIfNeeded() - } - } - - private func setupGestures() { - // dimmedView에만 탭 제스처를 설정하고 다른 제스처와의 충돌을 방지 - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapDimmedView)) - dimmedView.addGestureRecognizer(tapGesture) - dimmedView.isUserInteractionEnabled = true // 확실히 활성화 - - // 패닝 제스처는 유지 - let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture)) - containerView.addGestureRecognizer(panGesture) - } - - @objc private func handleDimmedViewTap() { - hideBottomSheet() - } - func showBottomSheet() { - guard let reactor = reactor else { return } - - // 1. 이전에 저장된 지역 필터가 있다면 해당 지역 버튼 활성화 - if let locations = reactor.currentState.savedSubRegions.first?.split(separator: "/").first.map(String.init), - let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { - reactor.action.onNext(.selectLocation(index)) - - - } - - // 4. 필터 칩 뷰 업데이트 - containerView.update( - locationText: reactor.currentState.savedSubRegions.joined(separator: ", "), - categoryText: reactor.currentState.savedCategories.joined(separator: ", ") - ) - - // 5. 애니메이션 - UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { - self.dimmedView.alpha = 1 - self.bottomConstraint?.update(offset: 0) - self.view.layoutIfNeeded() - } - } - - +// MARK: - UIGestureRecognizerDelegate +extension FilterBottomSheetViewController: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + let point = touch.location(in: self.view) - func hideBottomSheet() { - UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { - self.dimmedView.alpha = 0 - self.bottomConstraint?.update(offset: UIScreen.main.bounds.height) - self.view.layoutIfNeeded() - } completion: { _ in - self.dismiss(animated: false) - self.onDismiss?() + if containerView.frame.contains(point) { + return false } - } - @objc private func handleTapDimmedView() { - hideBottomSheet() - } - - @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { - let translation = gesture.translation(in: view) - - switch gesture.state { - case .changed: - guard translation.y >= 0 else { return } - bottomConstraint?.update(offset: translation.y) - view.layoutIfNeeded() - - case .ended: - let velocity = gesture.velocity(in: view) - if translation.y > 150 || velocity.y > 1000 { - hideBottomSheet() - } else { - UIView.animate(withDuration: 0.25) { - self.bottomConstraint?.update(offset: 0) - self.view.layoutIfNeeded() - } - } - - default: - break - } + return true } } +// MARK: - UICollectionViewDataSource extension FilterBottomSheetViewController: UICollectionViewDataSource { - func numberOfSections(in collectionView: UICollectionView) -> Int { - return 1 - } + func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, + numberOfItemsInSection section: Int) -> Int { return tagSection?.inputDataList.count ?? 0 } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, + cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell( withReuseIdentifier: TagSectionCell.identifiers, for: indexPath ) as? TagSectionCell else { return UICollectionViewCell() } - if let input = tagSection?.inputDataList[indexPath.item] { cell.injection(with: input) } - return cell } } -// MARK: - UICollectionViewDelegate +// MARK: - UICollectionViewDelegateFlowLayout extension FilterBottomSheetViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let category = tagSection?.inputDataList[indexPath.item].title else { return } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift index 68aa555a..1e108269 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift @@ -63,7 +63,7 @@ final class FilterChipsView: UIView { make.top.equalTo(titleLabel.snp.bottom).offset(12) make.leading.trailing.equalToSuperview() make.bottom.equalToSuperview().offset(-8) - make.height.equalTo(44) + make.height.greaterThanOrEqualTo(44).priority(.high) } emptyStateLabel.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index 98aee4ca..c08ca70d 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -278,7 +278,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM setupPanAndSwipeGestures() let mapViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleMapViewTap(_:))) - mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 +// mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 mapViewTapGesture.delaysTouchesBegan = false // 터치 지연 없음 mainView.mapView.addGestureRecognizer(mapViewTapGesture) mapViewTapGesture.delegate = self diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift index e8fb9c69..9fc339e9 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift @@ -180,13 +180,13 @@ final class StoreListViewController: UIViewController, View { viewController.modalPresentationStyle = .overFullScreen present(viewController, animated: false) { - viewController.showBottomSheet() +// viewController.showBottomSheet() } } private func dismissFilterBottomSheet() { if let sheet = presentedViewController as? FilterBottomSheetViewController { - sheet.hideBottomSheet() + sheet.dismiss(animated: true) } } } From 21c6036bc2fbd30a801f9c87ebc9b508bad413b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Thu, 27 Mar 2025 16:04:52 +0900 Subject: [PATCH 014/393] =?UTF-8?q?[FIX]=20=EB=93=B1=EB=A1=9D=EC=8B=9C=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=EB=9E=80=EC=97=90=EC=84=9C=20=EB=B9=88?= =?UTF-8?q?=EA=B3=B5=EA=B0=84=ED=83=AD=EC=8B=9C=20=ED=82=A4=EB=B3=B4?= =?UTF-8?q?=EB=93=9C=20=EB=82=B4=EB=A0=A4=EA=B0=80=EB=8F=84=EB=A1=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 --- .../AdminRegister/PopUpStoreRegisterViewController.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 0d7914ea..82515490 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -196,6 +196,10 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) + tapGesture.cancelsTouchesInView = false + view.addGestureRecognizer(tapGesture) + view.backgroundColor = UIColor(white:0.95, alpha:1) if let store = editingStore { From 86f2abd783ca9336ec2d2d3dcbeb49a4137e95e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Thu, 27 Mar 2025 17:49:23 +0900 Subject: [PATCH 015/393] =?UTF-8?q?[REFACTOR]=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=EB=90=98=EC=A7=80=EC=95=8A=EB=8A=94=20Google=20SDK=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 --- Poppool/Poppool.xcodeproj/project.pbxproj | 12 +----------- .../PopUpStoreRegisterViewController.swift | 1 - 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index eac04068..9285ca81 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -236,7 +236,6 @@ 086F8A182D265C5F00CA4FC9 /* MyPageListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A172D265C5F00CA4FC9 /* MyPageListSection.swift */; }; 086F8A1A2D265C6300CA4FC9 /* MyPageListSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A192D265C6300CA4FC9 /* MyPageListSectionCell.swift */; }; 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 088DE2432D104EE70030FA9E /* SwiftSoup */; }; - 088DE2472D12DB5C0030FA9E /* GoogleMaps in Frameworks */ = {isa = PBXBuildFile; productRef = 088DE2462D12DB5C0030FA9E /* GoogleMaps */; }; 088DE24A2D12F3360030FA9E /* DetailInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2492D12F3360030FA9E /* DetailInfoSection.swift */; }; 088DE24C2D12F33B0030FA9E /* DetailInfoSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE24B2D12F33B0030FA9E /* DetailInfoSectionCell.swift */; }; 088DE24F2D13019A0030FA9E /* DetailCommentTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE24E2D13019A0030FA9E /* DetailCommentTitleSection.swift */; }; @@ -975,8 +974,6 @@ BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, - 088DE2472D12DB5C0030FA9E /* GoogleMaps in Frameworks */, - 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */, BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, @@ -3084,7 +3081,6 @@ BDCA420F2CF35FF5005EECF6 /* Lottie */, 083A25CF2CF364B70099B58E /* Alamofire */, 088DE2432D104EE70030FA9E /* SwiftSoup */, - 088DE2462D12DB5C0030FA9E /* GoogleMaps */, 4E5825662D1951DF00EE83EF /* FloatingPanel */, 4EA9989C2D21C404009DC30B /* RxDataSources */, 082197A02D426DCB0054094A /* Then */, @@ -3175,7 +3171,6 @@ BDCA420E2CF35FF5005EECF6 /* XCRemoteSwiftPackageReference "lottie-spm" */, 083A25CE2CF364B70099B58E /* XCRemoteSwiftPackageReference "Alamofire" */, 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */, - 088DE2452D12DB5C0030FA9E /* XCRemoteSwiftPackageReference "ios-maps-sdk" */, 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */, 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */, 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */, @@ -4203,12 +4198,7 @@ package = 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; - 088DE2462D12DB5C0030FA9E /* GoogleMaps */ = { - isa = XCSwiftPackageProductDependency; - package = 088DE2452D12DB5C0030FA9E /* XCRemoteSwiftPackageReference "ios-maps-sdk" */; - productName = GoogleMaps; - }; - 08F403322D884F4D00BFA61A /* KakaoSDKUser */ = { + 08B191B92CF609AE0057BC04 /* RxKakaoSDK */ = { isa = XCSwiftPackageProductDependency; package = 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; productName = KakaoSDKUser; diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 82515490..d17c9fa0 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -5,7 +5,6 @@ import RxSwift import RxCocoa import PhotosUI import Alamofire -import GoogleMaps import CoreLocation final class PopUpStoreRegisterViewController: BaseViewController { From f7e543999e3aa9c8df7d7fe7ad0de17ed4591aad Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 30 Mar 2025 18:44:25 +0900 Subject: [PATCH 016/393] =?UTF-8?q?remove/#93:=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Convention/ControllerConvention.swift | 47 ------------------- .../ConventionCollectionViewCell.swift | 40 ---------------- .../Convention/ConventionTableViewCell.swift | 44 ----------------- .../Convention/ReactorConvention.swift | 46 ------------------ .../Convention/TestDynamicCell.swift | 44 ----------------- .../Convention/TestDynamicSection.swift | 44 ----------------- .../Convention/ViewConvention.swift | 31 ------------ 7 files changed, 296 deletions(-) delete mode 100644 Poppool/Poppool/Presentation/Convention/ControllerConvention.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/ConventionCollectionViewCell.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/ConventionTableViewCell.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/ReactorConvention.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/TestDynamicCell.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/TestDynamicSection.swift delete mode 100644 Poppool/Poppool/Presentation/Convention/ViewConvention.swift diff --git a/Poppool/Poppool/Presentation/Convention/ControllerConvention.swift b/Poppool/Poppool/Presentation/Convention/ControllerConvention.swift deleted file mode 100644 index bdce512a..00000000 --- a/Poppool/Poppool/Presentation/Convention/ControllerConvention.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// ControllerConvention.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/20/24. -// - -import UIKit - -import SnapKit -import RxCocoa -import RxSwift -import ReactorKit - -final class ControllerConvention: BaseViewController, View { - - typealias Reactor = ReactorConvention - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = ViewConvention() -} - -// MARK: - Life Cycle -extension ControllerConvention { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } -} - -// MARK: - SetUp -private extension ControllerConvention { - func setUp() { - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension ControllerConvention { - func bind(reactor: Reactor) { - } -} diff --git a/Poppool/Poppool/Presentation/Convention/ConventionCollectionViewCell.swift b/Poppool/Poppool/Presentation/Convention/ConventionCollectionViewCell.swift deleted file mode 100644 index e5eb7c34..00000000 --- a/Poppool/Poppool/Presentation/Convention/ConventionCollectionViewCell.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// ConventionCollectionViewCell.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/27/24. -// - -import UIKit - -import SnapKit - -final class ConventionCollectionViewCell: UICollectionViewCell { - - // MARK: - Components - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } -} - -// MARK: - SetUp -private extension ConventionCollectionViewCell { - func setUpConstraints() { - } -} - -extension ConventionCollectionViewCell: Inputable { - struct Input { - } - - func injection(with input: Input) { - } -} diff --git a/Poppool/Poppool/Presentation/Convention/ConventionTableViewCell.swift b/Poppool/Poppool/Presentation/Convention/ConventionTableViewCell.swift deleted file mode 100644 index c80f88aa..00000000 --- a/Poppool/Poppool/Presentation/Convention/ConventionTableViewCell.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// ConventionTableViewCell.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/26/24. -// - -import UIKit - -import SnapKit -import RxSwift - -final class ConventionTableViewCell: UITableViewCell { - - // MARK: - Components - - let disposeBag = DisposeBag() - - // MARK: - init - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp, Methods -private extension ConventionTableViewCell { - func setUpConstraints() { - } -} - -// MARK: - Inputable -extension ConventionTableViewCell: Inputable { - struct Input { - } - - func injection(with input: Input) { - - } -} diff --git a/Poppool/Poppool/Presentation/Convention/ReactorConvention.swift b/Poppool/Poppool/Presentation/Convention/ReactorConvention.swift deleted file mode 100644 index cc9a1ed9..00000000 --- a/Poppool/Poppool/Presentation/Convention/ReactorConvention.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// ReactorConvention.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/20/24. -// - -import ReactorKit -import RxSwift -import RxCocoa - -final class ReactorConvention: 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/Poppool/Presentation/Convention/TestDynamicCell.swift b/Poppool/Poppool/Presentation/Convention/TestDynamicCell.swift deleted file mode 100644 index 2f3ae731..00000000 --- a/Poppool/Poppool/Presentation/Convention/TestDynamicCell.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// TestDynamicCell.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/27/24. -// - -import UIKit - -import SnapKit -import RxSwift - -final class TestDynamicCell: UICollectionViewCell { - - // MARK: - Components - - let disposeBag = DisposeBag() - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } -} - -// MARK: - SetUp -private extension TestDynamicCell { - func setUpConstraints() { - } -} - -extension TestDynamicCell: Inputable { - struct Input { - - } - - func injection(with input: Input) { - - } -} diff --git a/Poppool/Poppool/Presentation/Convention/TestDynamicSection.swift b/Poppool/Poppool/Presentation/Convention/TestDynamicSection.swift deleted file mode 100644 index 04b245b8..00000000 --- a/Poppool/Poppool/Presentation/Convention/TestDynamicSection.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// TestDynamicSection.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/27/24. -// - -import UIKit - -import RxSwift - -struct TestDynamicSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = TestDynamicCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .estimated(1000) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(1000) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - - // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 20, leading: 20, bottom: 20, trailing: 20) - - return section - } -} diff --git a/Poppool/Poppool/Presentation/Convention/ViewConvention.swift b/Poppool/Poppool/Presentation/Convention/ViewConvention.swift deleted file mode 100644 index 8222a445..00000000 --- a/Poppool/Poppool/Presentation/Convention/ViewConvention.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// ViewConvention.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/20/24. -// -import UIKit - -import SnapKit - -final class ViewConvention: UIView { - - // MARK: - Components - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension ViewConvention { - - func setUpConstraints() { - } -} From 4795a0afbef0232ca6a7951e48c74785651ddafd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 30 Mar 2025 18:44:47 +0900 Subject: [PATCH 017/393] =?UTF-8?q?chore/#93:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=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 | 36 ----------------------- 1 file changed, 36 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index d05337a5..02a36d99 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -97,13 +97,6 @@ 082197B42D4E4E280054094A /* NormalCommentEditReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197B32D4E4E280054094A /* NormalCommentEditReactor.swift */; }; 083A25822CF361EF0099B58E /* BaseTabmanController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A257F2CF361EF0099B58E /* BaseTabmanController.swift */; }; 083A25832CF361EF0099B58E /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25802CF361EF0099B58E /* BaseViewController.swift */; }; - 083A258C2CF361F90099B58E /* ControllerConvention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25842CF361F90099B58E /* ControllerConvention.swift */; }; - 083A258D2CF361F90099B58E /* ConventionCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25852CF361F90099B58E /* ConventionCollectionViewCell.swift */; }; - 083A258E2CF361F90099B58E /* ConventionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25862CF361F90099B58E /* ConventionTableViewCell.swift */; }; - 083A258F2CF361F90099B58E /* ReactorConvention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25872CF361F90099B58E /* ReactorConvention.swift */; }; - 083A25902CF361F90099B58E /* TestDynamicCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25882CF361F90099B58E /* TestDynamicCell.swift */; }; - 083A25912CF361F90099B58E /* TestDynamicSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25892CF361F90099B58E /* TestDynamicSection.swift */; }; - 083A25922CF361F90099B58E /* ViewConvention.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A258A2CF361F90099B58E /* ViewConvention.swift */; }; 083A25992CF362090099B58E /* Sectionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25932CF362090099B58E /* Sectionable.swift */; }; 083A259A2CF362090099B58E /* SectionDecorationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25942CF362090099B58E /* SectionDecorationItem.swift */; }; 083A259B2CF362090099B58E /* SectionSupplementaryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25952CF362090099B58E /* SectionSupplementaryItem.swift */; }; @@ -590,13 +583,6 @@ 082197B32D4E4E280054094A /* NormalCommentEditReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentEditReactor.swift; sourceTree = ""; }; 083A257F2CF361EF0099B58E /* BaseTabmanController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTabmanController.swift; sourceTree = ""; }; 083A25802CF361EF0099B58E /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; - 083A25842CF361F90099B58E /* ControllerConvention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ControllerConvention.swift; sourceTree = ""; }; - 083A25852CF361F90099B58E /* ConventionCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConventionCollectionViewCell.swift; sourceTree = ""; }; - 083A25862CF361F90099B58E /* ConventionTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConventionTableViewCell.swift; sourceTree = ""; }; - 083A25872CF361F90099B58E /* ReactorConvention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactorConvention.swift; sourceTree = ""; }; - 083A25882CF361F90099B58E /* TestDynamicCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDynamicCell.swift; sourceTree = ""; }; - 083A25892CF361F90099B58E /* TestDynamicSection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestDynamicSection.swift; sourceTree = ""; }; - 083A258A2CF361F90099B58E /* ViewConvention.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewConvention.swift; sourceTree = ""; }; 083A25932CF362090099B58E /* Sectionable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sectionable.swift; sourceTree = ""; }; 083A25942CF362090099B58E /* SectionDecorationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionDecorationItem.swift; sourceTree = ""; }; 083A25952CF362090099B58E /* SectionSupplementaryItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionSupplementaryItem.swift; sourceTree = ""; }; @@ -1407,7 +1393,6 @@ children = ( 4E755B1B2D2B9ABF00ADFB21 /* Admin */, 4E685ECD2D12CEB6001EF91C /* Map */, - 083A258B2CF361F90099B58E /* Convention */, 08B1915F2CF430D40057BC04 /* Components */, 083A25C22CF3635B0099B58E /* Scene */, 083A259D2CF3620B0099B58E /* Utills */, @@ -1425,20 +1410,6 @@ path = Controllers; sourceTree = ""; }; - 083A258B2CF361F90099B58E /* Convention */ = { - isa = PBXGroup; - children = ( - 083A25842CF361F90099B58E /* ControllerConvention.swift */, - 083A25852CF361F90099B58E /* ConventionCollectionViewCell.swift */, - 083A25862CF361F90099B58E /* ConventionTableViewCell.swift */, - 083A25872CF361F90099B58E /* ReactorConvention.swift */, - 083A25882CF361F90099B58E /* TestDynamicCell.swift */, - 083A25892CF361F90099B58E /* TestDynamicSection.swift */, - 083A258A2CF361F90099B58E /* ViewConvention.swift */, - ); - path = Convention; - sourceTree = ""; - }; 083A25962CF362090099B58E /* Sectionable */ = { isa = PBXGroup; children = ( @@ -3269,7 +3240,6 @@ 4EECA3942D56770B00A07CCA /* MapPopUpStore.swift in Sources */, 4E9C12782D2BC7A0006744D6 /* AdminBottomSheetView.swift in Sources */, 086DD9362D00963900B97D3B /* SearchTitleSection.swift in Sources */, - 083A25922CF361F90099B58E /* ViewConvention.swift in Sources */, 086F89D92D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift in Sources */, 083C864F2D0DD3A6003F441C /* AddCommentImageSection.swift in Sources */, 08B191372CF366680057BC04 /* UICollectionViewCell+.swift in Sources */, @@ -3327,7 +3297,6 @@ 083C86622D0EC49E003F441C /* InstaCommentAddController.swift in Sources */, 08B1919C2CF4A77C0057BC04 /* TagSection.swift in Sources */, BD91038B2CF614A900BBCCAE /* AuthRepository.swift in Sources */, - 083A25902CF361F90099B58E /* TestDynamicCell.swift in Sources */, 08DC61F32CF75037002A2F44 /* KeyChainService.swift in Sources */, 086DD93B2D009A1C00B97D3B /* CancelableTagSection.swift in Sources */, 4E755B232D2B9C5D00ADFB21 /* AdminViewController.swift in Sources */, @@ -3345,7 +3314,6 @@ 083C864C2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift in Sources */, 086DD8CE2CFDFEB000B97D3B /* HomeListView.swift in Sources */, 08B191C22CF615CA0057BC04 /* Secrets.swift in Sources */, - 083A258C2CF361F90099B58E /* ControllerConvention.swift in Sources */, 083C86242D087A44003F441C /* DetailTitleSection.swift in Sources */, 08A2E4822D1BCDEA00102313 /* CommentDetailController.swift in Sources */, 0841BAA32CFA31A300049E31 /* SpacingSection.swift in Sources */, @@ -3390,7 +3358,6 @@ 081899452D35FEA10067BF01 /* RecentPopUpSection.swift in Sources */, 083A259A2CF362090099B58E /* SectionDecorationItem.swift in Sources */, BD9103892CF614A900BBCCAE /* PopUpStoreResponse.swift in Sources */, - 083A258F2CF361F90099B58E /* ReactorConvention.swift in Sources */, 086F89C72D1E348400CA4FC9 /* CommentUserInfoReactor.swift in Sources */, 0818990E2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift in Sources */, 086DD8C82CFDEA9200B97D3B /* UIView+.swift in Sources */, @@ -3468,7 +3435,6 @@ BD9103652CF6149D00BBCCAE /* HomeAPIEndpoint.swift in Sources */, 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */, 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */, - 083A258D2CF361F90099B58E /* ConventionCollectionViewCell.swift in Sources */, 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */, 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */, 081898FB2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift in Sources */, @@ -3581,7 +3547,6 @@ 081898EE2D33A39D0067BF01 /* MyCommentSortedModalController.swift in Sources */, 081898AA2D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift in Sources */, 086DD8CC2CFDFEA800B97D3B /* HomeListController.swift in Sources */, - 083A258E2CF361F90099B58E /* ConventionTableViewCell.swift in Sources */, 086DD9342D00962500B97D3B /* SearchTitleSectionCell.swift in Sources */, 08B191592CF41E610057BC04 /* SignUpMainController.swift in Sources */, 0899526C2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift in Sources */, @@ -3658,7 +3623,6 @@ 08A2E49D2D1C416800102313 /* CommentListTitleSection.swift in Sources */, 0818989C2D2BAA570067BF01 /* WithdrawlCheckModalView.swift in Sources */, 081899122D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift in Sources */, - 083A25912CF361F90099B58E /* TestDynamicSection.swift in Sources */, 086DD8D82CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift in Sources */, 089952422D031E650022AEF9 /* SearchSortedController.swift in Sources */, 089952532D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift in Sources */, From 847a7256f7dc3a1ccf1f2ff22718bd09a6fed676 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 30 Mar 2025 19:22:20 +0900 Subject: [PATCH 018/393] =?UTF-8?q?chore/#93:=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EC=84=B8=ED=8C=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Run SwiftLint 스크립트 추가 - SwiftLint rule 파일 추가 --- Poppool/Poppool.xcodeproj/project.pbxproj | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 02a36d99..0026d3f0 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -493,6 +493,7 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSection.swift; sourceTree = ""; }; 0818988F2D295DC80067BF01 /* MyPageLogoutSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSectionCell.swift; sourceTree = ""; }; 081898932D2965C20067BF01 /* ProfileEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditController.swift; sourceTree = ""; }; @@ -2976,6 +2977,7 @@ BDCA41B42CF35AC0005EECF6 = { isa = PBXGroup; children = ( + 05229DD02D99519200D88E73 /* .swiftlint.yml */, BDCA41BF2CF35AC0005EECF6 /* Poppool */, BDCA41D62CF35AC1005EECF6 /* PoppoolTests */, BDCA41E02CF35AC1005EECF6 /* PoppoolUITests */, @@ -3031,6 +3033,7 @@ isa = PBXNativeTarget; buildConfigurationList = BDCA41E72CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "Poppool" */; buildPhases = ( + 05229DCF2D99507C00D88E73 /* Run SwiftLint */, BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, BDCA41BB2CF35AC0005EECF6 /* Resources */, @@ -3198,6 +3201,28 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run SwiftLint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif which swiftlint > /dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ BDCA41B92CF35AC0005EECF6 /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -3816,6 +3841,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; @@ -3858,6 +3884,7 @@ CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; From 6cec4f8f188edbe12efcafca581e899a1dceb8c9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 30 Mar 2025 19:22:38 +0900 Subject: [PATCH 019/393] =?UTF-8?q?add/#93:=20swiftlint=20rule=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/.swiftlint.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 Poppool/.swiftlint.yml diff --git a/Poppool/.swiftlint.yml b/Poppool/.swiftlint.yml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Poppool/.swiftlint.yml @@ -0,0 +1 @@ + From 578230532227336081fe9dce28467bcf82856035 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 31 Mar 2025 09:07:18 +0900 Subject: [PATCH 020/393] =?UTF-8?q?chore/#93:=20SwiftLint=20rule=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B8=B0=EB=B3=B8=20=EC=84=B8=ED=8C=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/.swiftlint.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Poppool/.swiftlint.yml b/Poppool/.swiftlint.yml index 8b137891..b1f8439d 100644 --- a/Poppool/.swiftlint.yml +++ b/Poppool/.swiftlint.yml @@ -1 +1,15 @@ +# 기본 활성화된 룰 중에 비활성화할 룰을 지정 +disabled_rules: + + +# 기본(default) 룰이 아닌 룰들을 활성화 +opt_in_rules: + + +# swiftlint 과정에 포함할 파일 경로 +included: + + +# swiftlint를 적용하지 않게 설정할 파일 경로 +excluded: From 8f392db9a11d480bbe2617e425782549221de92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 30 Mar 2025 18:23:05 +0900 Subject: [PATCH 021/393] =?UTF-8?q?[FIX]=20=ED=95=84=ED=84=B0=EB=B0=94?= =?UTF-8?q?=ED=85=80=EC=8B=9C=ED=8A=B8=EB=B7=B0=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=94=A4=EB=93=9C=EC=98=81=EC=97=AD=20=ED=83=AD=EC=8B=9C=20?= =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EB=8B=AB=ED=9E=88=EC=A7=80?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EB=AC=B8=EC=A0=9C=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 | 45 +- .../xcshareddata/swiftpm/Package.resolved | 177 ++++++ Poppool/Poppool/Application/AppDelegate.swift | 25 +- .../Map/Common/ClusteringManager.swift | 66 +-- .../Map/Common/MapUtilities.swift | 21 - .../BalloonBackgroundView.swift | 22 +- .../FillterSheetView/BalloonChipCell.swift | 1 + .../FilterBottomSheetView.swift | 155 ++--- .../FilterBottomSheetViewController.swift | 549 +++++++++--------- 9 files changed, 556 insertions(+), 505 deletions(-) create mode 100644 Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 9285ca81..ada98d42 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -370,7 +370,10 @@ 08DE8A1D2D5261E70049BCAC /* MyPageTermsReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A1C2D5261E70049BCAC /* MyPageTermsReactor.swift */; }; 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A3E2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift */; }; 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */; }; - 08F403332D884F4D00BFA61A /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 08F403322D884F4D00BFA61A /* KakaoSDKUser */; }; + 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; + 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142B2D994A3A00DFD08F /* KakaoSDK */; }; + 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; + 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */; }; 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */; }; 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */; }; @@ -973,7 +976,9 @@ BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, + 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, + 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, @@ -982,7 +987,9 @@ 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, + 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, + 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3084,7 +3091,10 @@ 4E5825662D1951DF00EE83EF /* FloatingPanel */, 4EA9989C2D21C404009DC30B /* RxDataSources */, 082197A02D426DCB0054094A /* Then */, - 08F403322D884F4D00BFA61A /* KakaoSDKUser */, + 4E1514292D99480200DFD08F /* NMapsMap */, + 4E15142B2D994A3A00DFD08F /* KakaoSDK */, + 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */, + 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */, ); productName = Poppool; productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; @@ -3175,6 +3185,7 @@ 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */, 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */, 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, + 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, ); productRefGroup = BDCA41BE2CF35AC0005EECF6 /* Products */; projectDirPath = ""; @@ -4060,20 +4071,20 @@ minimumVersion = 2.7.6; }; }; - 088DE2452D12DB5C0030FA9E /* XCRemoteSwiftPackageReference "ios-maps-sdk" */ = { + 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/googlemaps/ios-maps-sdk"; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 9.2.0; + minimumVersion = 2.24.0; }; }; - 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kakao/kakao-ios-sdk.git"; + repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 2.24.0; + minimumVersion = 3.21.0; }; }; 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { @@ -4198,10 +4209,22 @@ package = 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */; productName = SwiftSoup; }; - 08B191B92CF609AE0057BC04 /* RxKakaoSDK */ = { + 4E1514292D99480200DFD08F /* NMapsMap */ = { + isa = XCSwiftPackageProductDependency; + package = 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; + productName = NMapsMap; + }; + 4E15142B2D994A3A00DFD08F /* KakaoSDK */ = { + isa = XCSwiftPackageProductDependency; + productName = KakaoSDK; + }; + 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + productName = KakaoSDKAuth; + }; + 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */ = { isa = XCSwiftPackageProductDependency; - package = 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKUser; + productName = KakaoSDKCommon; }; 4E5825662D1951DF00EE83EF /* FloatingPanel */ = { isa = XCSwiftPackageProductDependency; diff --git a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..172ad40b --- /dev/null +++ b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,177 @@ +{ + "originHash" : "a264a064b245047631c2da3a5af0624dcc8a9814c5033d1880880c8dbbdc6a20", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", + "version" : "5.10.2" + } + }, + { + "identity" : "floatingpanel", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scenee/FloatingPanel.git", + "state" : { + "revision" : "b6e8928b1a3ad909e6db6a0278d286c33cfd0dc3", + "version" : "2.8.6" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk.git", + "state" : { + "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", + "version" : "2.24.0" + } + }, + { + "identity" : "kingfisher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/onevcat/Kingfisher.git", + "state" : { + "revision" : "3db26ab625d194c38e68c1a40e43d1bc12743fe0", + "version" : "8.2.0" + } + }, + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", + "version" : "4.5.1" + } + }, + { + "identity" : "pageboy", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Pageboy.git", + "state" : { + "revision" : "be0c1f6f1964cfb07f9d819b0863f2c3f255f612", + "version" : "4.2.0" + } + }, + { + "identity" : "panmodal", + "kind" : "remoteSourceControl", + "location" : "https://github.com/slackhq/PanModal.git", + "state" : { + "revision" : "b012aecb6b67a8e46369227f893c12544846613f", + "version" : "1.2.7" + } + }, + { + "identity" : "reactorkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/ReactorKit.git", + "state" : { + "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", + "version" : "3.2.0" + } + }, + { + "identity" : "rxdatasources", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxDataSources.git", + "state" : { + "revision" : "90c29b48b628479097fe775ed1966d75ac374518", + "version" : "5.0.2" + } + }, + { + "identity" : "rxgesture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxGesture.git", + "state" : { + "revision" : "1b137c576b4aaaab949235752278956697c9e4a0", + "version" : "4.0.4" + } + }, + { + "identity" : "rxkeyboard", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxKeyboard.git", + "state" : { + "revision" : "63f6377975c962a1d89f012a6f1e5bebb2c502b7", + "version" : "2.0.1" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "5dd1907d64f0d36f158f61a466bab75067224893", + "version" : "6.9.0" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + }, + { + "identity" : "spm-nmapsgeometry", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsGeometry.git", + "state" : { + "revision" : "436d5e2e684f557faf5ef5862fd6633a42d7af11", + "version" : "1.0.2" + } + }, + { + "identity" : "spm-nmapsmap", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsMap", + "state" : { + "revision" : "ad89e53fdfec3b8d8994280fb0414b5a7b1c3e8e", + "version" : "3.21.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "18ad8b8ff0f03f3c0a5544ffccfa2ea1c051ae6e", + "version" : "2.8.0" + } + }, + { + "identity" : "tabman", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Tabman.git", + "state" : { + "revision" : "3b2213290eb93e55bb50b49d1a179033005c11ab", + "version" : "3.2.0" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + }, + { + "identity" : "weakmaptable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/WeakMapTable.git", + "state" : { + "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", + "version" : "1.2.1" + } + } + ], + "version" : 3 +} diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 8d2ed331..bf1ed285 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,36 +1,23 @@ -// -// AppDelegate.swift -// Poppoolasdasdasdasda -// -// Created by Porori on 11/24/24. -// import UIKit -import RxKakaoSDKAuth -import KakaoSDKAuth -import RxKakaoSDKCommon -import NMapsMap -import UIKit - import KakaoSDKCommon -import GoogleMaps import CoreLocation +import NMapsMap @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue) - GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) - + NMFAuthManager.shared().clientId = Secrets.naverMapClientId.rawValue + let locationManager = CLLocationManager() - locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 - + locationManager.requestWhenInUseAuthorization() + return true - } + } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } } - diff --git a/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift b/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift index 8b8600f6..ad3b265f 100644 --- a/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift +++ b/Poppool/Poppool/Presentation/Map/Common/ClusteringManager.swift @@ -37,7 +37,6 @@ class ClusteringManager { func clusterStores(_ stores: [MapPopUpStore], at zoomLevel: Float) -> [ClusterMarkerData] { let level = MapZoomLevel.getLevel(from: zoomLevel) - // partition() 호출 결과를 별도의 변수에 할당 let partitionedStores = stores.partition { store in let city = extractCity(from: store.address) return city == "서울" || city == "경기" @@ -49,7 +48,7 @@ class ClusteringManager { case .country: return clusterByProvince(stores) case .city: - let seoulGyeonggiClusters = clusterByCityLevel(seoulGyeonggiStores) + let seoulGyeonggiClusters = clusterByDistrict(seoulGyeonggiStores) let otherClusters = clusterByMetropolitan(otherStores) return seoulGyeonggiClusters + otherClusters case .district: @@ -64,13 +63,11 @@ class ClusteringManager { private func clusterByMetropolitan(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { var clusters: [String: MutableCluster] = [:] - // 광역시/도 클러스터 초기화 let allClusters = RegionType.RegionDefinitions.metropolitanClusters + RegionType.RegionDefinitions.provinceClusters for cluster in allClusters { clusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - // 스토어 할당 for store in stores { let city = extractCity(from: store.address) if let cluster = clusters[city] { @@ -83,78 +80,17 @@ class ClusteringManager { return validClusters.map { $0.toMarkerData() } } - private func clusterByCityLevel(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { - var clusters: [String: MutableCluster] = [:] - - // 서울/경기 클러스터 초기화 - initializeSeoulGyeonggiClusters(&clusters) - - for store in stores { - let city = extractCity(from: store.address) - let clusterKey = determineClusterKey(for: store, city: city, clusters: &clusters) - if let cluster = clusters[clusterKey] { - cluster.stores.append(store) - cluster.storeCount += 1 - } - } - - let validClusters = clusters.values.filter { $0.storeCount > 0 } - return validClusters.map { $0.toMarkerData() } - } - - private func initializeSeoulGyeonggiClusters(_ clusters: inout [String: MutableCluster]) { - let predefinedClusters = [ - ("서울 북부", RepresentativeScope.seoulNorth.center), - ("서울 남부", RepresentativeScope.seoulSouth.center), - ("경기 북부", RepresentativeScope.gyeonggiNorth.center), - ("경기 남부", RepresentativeScope.gyeonggiSouth.center) - ] - - for (name, coordinate) in predefinedClusters { - let baseRegion = RegionCluster( - name: name, - subRegions: [name], - coordinate: coordinate, - type: .metropolitan - ) - clusters[name] = MutableCluster(base: baseRegion, fixedCenter: coordinate) - } - } - - private func determineClusterKey(for store: MapPopUpStore, city: String, clusters: inout [String: MutableCluster]) -> String { - if city == "서울" { - return seoulNorthRegions.contains(where: { store.address.contains($0) }) ? "서울 북부" : "서울 남부" - } else if city == "경기" { - return gyeonggiNorthRegions.contains(where: { store.address.contains($0) }) ? "경기 북부" : "경기 남부" - } else { - if clusters[city] == nil { - if let coordinate = getFixedCenterForCity(city) { - let baseRegion = RegionCluster( - name: city, - subRegions: [city], - coordinate: coordinate, - type: .metropolitan - ) - clusters[city] = MutableCluster(base: baseRegion, fixedCenter: coordinate) - } - } - return city - } - } - private func clusterByDistrict(_ stores: [MapPopUpStore]) -> [ClusterMarkerData] { var seoulClusters: [String: MutableCluster] = [:] var gyeonggiClusters: [String: MutableCluster] = [:] var otherClusters: [String: MutableCluster] = [:] - // 서울/경기 클러스터 초기화 for cluster in RegionType.RegionDefinitions.seoulClusters { seoulClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } for cluster in RegionType.RegionDefinitions.gyeonggiClusters { gyeonggiClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } - // 다른 지역 클러스터 초기화 for cluster in RegionType.RegionDefinitions.metropolitanClusters { otherClusters[cluster.name] = MutableCluster(base: cluster, fixedCenter: cluster.coordinate) } diff --git a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift index 1b20d677..3b4cd3bf 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift @@ -29,24 +29,3 @@ public let gyeonggiSouthRegions: [String] = [ "여주시", "양평군", "광주시", "이천시" ] -// RepresentativeScope 수정 -public struct RepresentativeScope { - public static let seoulNorth = ( - center: NMGLatLng(lat: 37.6020, lng: 127.0350), - radius: 3000.0 - ) - public static let seoulSouth = ( - center: NMGLatLng(lat: 37.4959, lng: 127.0664), // 강남/서초 중심 - radius: 3000.0 - ) - - // 경기 북부/남부 좌표 조정 - public static let gyeonggiNorth = ( - center: NMGLatLng(lat: 37.7358, lng: 127.0346), // 의정부 중심 - radius: 4000.0 - ) - public static let gyeonggiSouth = ( - center: NMGLatLng(lat: 37.2911, lng: 127.0876), // 용인/분당 중심 - radius: 4000.0 - ) -} diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift index 28a18b39..4b9afe75 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift @@ -13,7 +13,6 @@ final class BalloonBackgroundView: UIView { return view }() - // 기존 말풍선 UI: 서브 지역을 나열하는 CollectionView (서울/경기/부산용) private let collectionView: UICollectionView = { let layout = UICollectionViewCompositionalLayout { section, env in let itemSize = NSCollectionLayoutSize( @@ -44,8 +43,8 @@ final class BalloonBackgroundView: UIView { let iv = UIImageView() iv.contentMode = .scaleAspectFit iv.tintColor = .blu500 - iv.image = UIImage(named: "Marker") // 에셋에 추가된 Marker 이미지 - iv.isHidden = true // 기본은 숨김 + iv.image = UIImage(named: "Marker") + iv.isHidden = true return iv }() @@ -62,7 +61,7 @@ final class BalloonBackgroundView: UIView { label.font = UIFont.systemFont(ofSize: 14, weight: .medium) label.textColor = UIColor.g400 label.textAlignment = .center - label.numberOfLines = 2 // 두 줄 표시 + label.numberOfLines = 2 return label }() @@ -88,15 +87,21 @@ final class BalloonBackgroundView: UIView { override init(frame: CGRect) { super.init(frame: frame) backgroundColor = .clear + self.isUserInteractionEnabled = true + containerView.isUserInteractionEnabled = true + collectionView.isUserInteractionEnabled = true setupLayout() setupCollectionView() } + + required init?(coder: NSCoder) { fatalError() } // MARK: - Setup private func setupLayout() { + addSubview(containerView) containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() @@ -169,12 +174,6 @@ final class BalloonBackgroundView: UIView { UIColor.g50.setFill() path.fill() - // 그림자 설정 -// layer.shadowPath = path.cgPath -// layer.shadowColor = UIColor.black.cgColor -// layer.shadowOpacity = 0.1 -// layer.shadowOffset = CGSize(width: 0, height: 2) -// layer.shadowRadius = 4 } @@ -272,7 +271,7 @@ final class BalloonBackgroundView: UIView { let itemHeight: CGFloat = 36 let interGroupSpacing: CGFloat = 8 - let verticalInset: CGFloat = 20 + 20 // top: 20, bottom: 20 + let verticalInset: CGFloat = 20 + 20 let totalHeight = max( (itemHeight * CGFloat(numberOfRows)) + (interGroupSpacing * CGFloat(numberOfRows - 1)) + @@ -280,7 +279,6 @@ final class BalloonBackgroundView: UIView { 36 ) - print("계산된 최종 높이: \(totalHeight)") return totalHeight } private func calculateButtonWidth(for text: String, font: UIFont, isSelected: Bool) -> CGFloat { diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift index 3919e1bf..afea2a71 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift @@ -11,6 +11,7 @@ final class BalloonChipCell: UICollectionViewCell { font: .KorFont(style: .medium, size: 11), cornerRadius: 15 ) + button.titleLabel?.lineBreakMode = .byTruncatingTail button.titleLabel?.adjustsFontSizeToFitWidth = false return button diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index 67034b7f..c233ea4b 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -9,15 +9,9 @@ final class FilterBottomSheetView: UIView { view.layer.cornerRadius = 20 view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] view.layer.masksToBounds = true - return view }() - let headerView: UIView = { - let view = UIView() - view.backgroundColor = .white - return view - }() let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") label.textColor = .black @@ -41,6 +35,7 @@ final class FilterBottomSheetView: UIView { scrollView.showsHorizontalScrollIndicator = false return scrollView }() + let locationContentView = UIView() var categoryHeightConstraint: Constraint? @@ -81,7 +76,6 @@ final class FilterBottomSheetView: UIView { return collectionView }() - let balloonBackgroundView = BalloonBackgroundView() let resetButton: PPButton = { @@ -89,14 +83,12 @@ final class FilterBottomSheetView: UIView { return button }() - let saveButton: PPButton = { let button = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") return button }() - - let buttonStack: UIStackView = { + private let buttonStack: UIStackView = { let stack = UIStackView() stack.axis = .horizontal stack.spacing = 12 @@ -112,7 +104,7 @@ final class FilterBottomSheetView: UIView { private var balloonHeightConstraint: Constraint? // MARK: - Initialization - + override init(frame: CGRect) { super.init(frame: frame) setupLayout() @@ -127,33 +119,19 @@ final class FilterBottomSheetView: UIView { backgroundColor = .clear addSubview(containerView) - containerView.addSubview(headerView) - headerView.addSubview(titleLabel) - headerView.addSubview(closeButton) - + containerView.addSubview(titleLabel) + containerView.addSubview(closeButton) containerView.addSubview(segmentedControl) containerView.addSubview(locationScrollView) locationScrollView.addSubview(locationContentView) - containerView.addSubview(balloonBackgroundView) containerView.addSubview(categoryCollectionView) - categoryCollectionView.snp.makeConstraints { make in - make.top.equalTo(segmentedControl.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview() - categoryHeightConstraint = make.height.equalTo(160).constraint - } - containerView.addSubview(filterChipsView) + buttonStack.addArrangedSubview(resetButton) buttonStack.addArrangedSubview(saveButton) containerView.addSubview(buttonStack) - filterChipsView.snp.makeConstraints { make in - make.top.equalTo(balloonBackgroundView.snp.bottom).offset(24) - make.leading.trailing.equalToSuperview().inset(16) - make.height.equalTo(80) - } - setupConstraints() } @@ -164,12 +142,7 @@ final class FilterBottomSheetView: UIView { containerView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() - make.top.equalTo(headerView.snp.top) - } - - headerView.snp.makeConstraints { make in - make.top.leading.trailing.equalToSuperview() - make.height.equalTo(70) + make.top.equalTo(self.snp.top) } titleLabel.snp.makeConstraints { make in @@ -184,7 +157,7 @@ final class FilterBottomSheetView: UIView { } segmentedControl.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom).offset(16) + make.top.equalTo(titleLabel.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() } @@ -202,7 +175,7 @@ final class FilterBottomSheetView: UIView { categoryCollectionView.snp.makeConstraints { make in make.top.equalTo(segmentedControl.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() -// categoryHeightConstraint = make.height.equalTo(160).constraint + categoryHeightConstraint = make.height.equalTo(160).constraint } balloonBackgroundView.snp.makeConstraints { make in @@ -260,6 +233,7 @@ final class FilterBottomSheetView: UIView { } } } + func updateCategoryButtonSelection(_ category: String) { categoryCollectionView.subviews.forEach { subview in if let stackView = subview as? UIStackView { @@ -305,36 +279,19 @@ final class FilterBottomSheetView: UIView { } func updateContentVisibility(isCategorySelected: Bool) { - UIView.performWithoutAnimation { - self.locationScrollView.alpha = isCategorySelected ? 0 : 1 - self.balloonBackgroundView.alpha = isCategorySelected ? 0 : 1 - self.categoryCollectionView.alpha = isCategorySelected ? 1 : 0 - - self.locationScrollView.isHidden = isCategorySelected - self.balloonBackgroundView.isHidden = isCategorySelected - self.categoryCollectionView.isHidden = !isCategorySelected - - // filterChipsView 제약조건 업데이트 - self.filterChipsView.snp.remakeConstraints { make in - if isCategorySelected { - make.top.equalTo(self.categoryCollectionView.snp.bottom).offset(16) - } else { - make.top.equalTo(self.balloonBackgroundView.snp.bottom).offset(24) - } - make.leading.trailing.equalToSuperview().inset(16) - make.height.equalTo(80) - } - - let newHeight = isCategorySelected ? 170 : self.balloonBackgroundView.calculateHeight() - self.balloonHeightConstraint?.update(offset: newHeight) - - self.layoutIfNeeded() - } - } - + locationScrollView.isHidden = isCategorySelected + balloonBackgroundView.isHidden = isCategorySelected + categoryCollectionView.isHidden = !isCategorySelected + locationScrollView.alpha = isCategorySelected ? 0 : 1 + balloonBackgroundView.alpha = isCategorySelected ? 0 : 1 + categoryCollectionView.alpha = isCategorySelected ? 1 : 0 + let newHeight = isCategorySelected ? 170 : self.balloonBackgroundView.calculateHeight() + balloonHeightConstraint?.update(offset: newHeight) + self.layoutIfNeeded() + } private func createStyledButton(title: String, isSelected: Bool = false) -> PPButton { let button = PPButton( @@ -371,49 +328,37 @@ final class FilterBottomSheetView: UIView { button.setBackgroundColor(.w100, for: .normal) button.setTitleColor(.g400, for: .normal) button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .KorFont(style: .medium, size: 13) + button.titleLabel?.font = .KorFont(style: .medium, size: 13) button.layer.borderWidth = 1 } } } func updateBalloonHeight(isHidden: Bool, dynamicHeight: CGFloat = 160) { - if isHidden { - balloonBackgroundView.alpha = 0 - balloonBackgroundView.isHidden = true - balloonHeightConstraint?.update(offset: 0) - } else { - balloonBackgroundView.alpha = 1 - balloonBackgroundView.isHidden = false - balloonHeightConstraint?.update(offset: dynamicHeight) - } - - self.setNeedsLayout() + balloonBackgroundView.alpha = isHidden ? 0 : 1 + balloonBackgroundView.isHidden = isHidden + balloonHeightConstraint?.update(offset: isHidden ? 0 : dynamicHeight) self.layoutIfNeeded() } - - - + func updateBalloonPosition(for button: UIButton) { - guard let window = button.window else { return } - - let buttonFrameInWindow = button.convert(button.bounds, to: window) - let balloonFrameInWindow = balloonBackgroundView.convert(balloonBackgroundView.bounds, to: window) + DispatchQueue.main.async { + guard let window = button.window else { return } - let buttonCenterX = buttonFrameInWindow.midX + let buttonFrameInWindow = button.convert(button.bounds, to: window) + let balloonFrameInWindow = self.balloonBackgroundView.convert(self.balloonBackgroundView.bounds, to: window) - let relativeX = buttonCenterX - balloonFrameInWindow.minX + let buttonCenterX = buttonFrameInWindow.midX + let relativeX = buttonCenterX - balloonFrameInWindow.minX + let position = relativeX / self.balloonBackgroundView.bounds.width + let minPosition: CGFloat = 0.1 + let maxPosition: CGFloat = 0.9 + let clampedPosition = min(maxPosition, max(minPosition, position)) - let position = relativeX / balloonBackgroundView.bounds.width - - let minPosition: CGFloat = 0.1 - let maxPosition: CGFloat = 0.9 - let clampedPosition = min(maxPosition, max(minPosition, position)) - - balloonBackgroundView.arrowPosition = clampedPosition - balloonBackgroundView.setNeedsDisplay() + self.balloonBackgroundView.arrowPosition = clampedPosition + self.balloonBackgroundView.setNeedsDisplay() + } } - private func updateBalloonPositionAccurately(for button: PPButton) { let buttonFrameInBalloon = button.convert(button.bounds, to: balloonBackgroundView) let arrowPosition = buttonFrameInBalloon.midX / balloonBackgroundView.bounds.width @@ -435,17 +380,29 @@ extension FilterBottomSheetView { filterChipsView.updateChips(with: filters) } -} +} + extension FilterBottomSheetView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { - // 선택된 버튼 찾기 + updateSelectedButtonPosition() + } + + func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) { + updateSelectedButtonPosition() + } + + func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) { + if !decelerate { + updateSelectedButtonPosition() + } + } + + private func updateSelectedButtonPosition() { guard let selectedButton = locationContentView.subviews.first(where: { view in guard let button = view as? PPButton else { return false } return button.backgroundColor == .blu500 }) as? PPButton else { return } - - DispatchQueue.main.async { [weak self] in - self?.updateBalloonPosition(for: selectedButton) - } + + updateBalloonPosition(for: selectedButton) } } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift index 67663ca4..828141f5 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift @@ -1,4 +1,3 @@ - import UIKit import SnapKit import RxSwift @@ -13,28 +12,21 @@ final class FilterBottomSheetViewController: UIViewController, View { var disposeBag = DisposeBag() var onSave: ((FilterData) -> Void)? var onDismiss: (() -> Void)? - - // Container height를 업데이트할 때 SnapKit Constraint를 직접 저장해 둠 private var bottomConstraint: Constraint? private var containerHeightConstraint: Constraint? - // 바텀시트 실제 UI let containerView = FilterBottomSheetView() - - // 필요하다면 다른 속성들 + private var containerViewBottomConstraint: NSLayoutConstraint? private var savedLocation: String? private var savedCategory: String? private var tagSection: TagSection? - private lazy var dimmedView: UIControl = { - let control = UIControl() - control.backgroundColor = .black.withAlphaComponent(0.4) - control.alpha = 0 - control.addTarget(self, action: #selector(hideBottomSheet), for: .touchUpInside) - return control + private lazy var dimmedView: UIView = { + let view = UIView() + view.backgroundColor = .black.withAlphaComponent(0.4) + view.alpha = 0 + return view }() - - // MARK: - Initialization init(reactor: Reactor) { super.init(nibName: nil, bundle: nil) @@ -46,32 +38,55 @@ final class FilterBottomSheetViewController: UIViewController, View { } // MARK: - Lifecycle + override func viewDidLoad() { super.viewDidLoad() - - setupLayout() // 오토레이아웃 + setupLayout() setupGestures() setupCollectionView() - - // ChipsView에서 필터 제거 로직 + containerView.isUserInteractionEnabled = true containerView.filterChipsView.onRemoveChip = { [weak self] removedOption in guard let self = self, let reactor = self.reactor else { return } - if reactor.currentState.selectedCategories.contains(removedOption) { + let isCategory = reactor.currentState.selectedCategories.contains(removedOption) + let isSubRegion = reactor.currentState.selectedSubRegions.contains(removedOption) + + if isCategory { reactor.action.onNext(.toggleCategory(removedOption)) - } else if reactor.currentState.selectedSubRegions.contains(removedOption) { + } else if isSubRegion { reactor.action.onNext(.toggleSubRegion(removedOption)) + } - let currentSegment = self.containerView.segmentedControl.selectedSegmentIndex - if currentSegment == 0 { - self.updateUIForCurrentTab(segment: currentSegment) + DispatchQueue.main.async { + let activeSegment = reactor.currentState.activeSegment + + if isCategory && activeSegment == 1 { + self.containerView.categoryCollectionView.reloadData() + } else if isSubRegion && activeSegment == 0 { + if let selectedIndex = reactor.currentState.selectedLocationIndex { + let location = reactor.currentState.locations[selectedIndex] + self.containerView.balloonBackgroundView.configure( + for: location.main, + subRegions: location.sub, + selectedRegions: reactor.currentState.selectedSubRegions, + selectionHandler: { [weak self] subRegion in + self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) + }, + allSelectionHandler: { [weak self] in + self?.reactor?.action.onNext(.toggleAllSubRegions) + } + ) + } } + + self.updateContainerHeight() + self.containerView.updateContentVisibility(isCategorySelected: activeSegment == 1) } } - } + } - // MARK: - Setup Layout + // MARK: - Setup private func setupLayout() { view.backgroundColor = .clear @@ -83,178 +98,13 @@ final class FilterBottomSheetViewController: UIViewController, View { view.addSubview(containerView) containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() - containerHeightConstraint = make.height.equalTo(UIScreen.main.bounds.height * 0.7).constraint - + containerHeightConstraint = make.height.greaterThanOrEqualTo(400).constraint bottomConstraint = make.bottom.equalToSuperview().offset(UIScreen.main.bounds.height).constraint } - containerView.isUserInteractionEnabled = true - } - - // MARK: - Setup Gestures - private func setupGestures() { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(hideBottomSheet)) - tapGesture.delegate = self - dimmedView.addGestureRecognizer(tapGesture) - } - - - -// @objc private func didTapDimmedView() { -// // 딤드 뷰 탭 → 시트 닫기 -// hideBottomSheet() -// } - - @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { - let translation = gesture.translation(in: view) - - switch gesture.state { - case .changed: - guard translation.y >= 0 else { return } - bottomConstraint?.update(offset: translation.y) - view.layoutIfNeeded() - - case .ended: - let velocity = gesture.velocity(in: view) - if translation.y > 150 || velocity.y > 1000 { - // (pan) 시트 끌어내리면 닫기 - let currentSegment = containerView.segmentedControl.selectedSegmentIndex - updateUIForCurrentTab(segment: currentSegment) - hideBottomSheet() - } else { - UIView.animate(withDuration: 0.25) { - self.bottomConstraint?.update(offset: 0) - self.view.layoutIfNeeded() - } - } - default: - break - } - } - - // MARK: - Public Show / Hide - func showBottomSheet() { - guard let reactor = reactor else { return } - - // (A) location 초기선택 - if let locations = reactor.currentState.savedSubRegions.first?.split(separator: "/").first.map(String.init), - let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { - reactor.action.onNext(.selectLocation(index)) - } - - // (B) 필터 칩 뷰 업데이트 - containerView.update( - locationText: reactor.currentState.savedSubRegions.joined(separator: ", "), - categoryText: reactor.currentState.savedCategories.joined(separator: ", ") - ) - - // (C) 시트 애니메이션 - UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { - self.dimmedView.alpha = 1 - self.bottomConstraint?.update(offset: 0) - self.view.layoutIfNeeded() - } - } - - @objc func hideBottomSheet() { - UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { - self.dimmedView.alpha = 0 - self.bottomConstraint?.update(offset: UIScreen.main.bounds.height) - self.view.layoutIfNeeded() - } completion: { _ in - self.dismiss(animated: false) - self.onDismiss?() - } - } - - // MARK: - UI Update (for tabs) - private func updateUIForCurrentTab(segment: Int) { - UIView.performWithoutAnimation { - // 탭 전환 시 모든 뷰 숨김 - containerView.categoryCollectionView.isHidden = true - containerView.locationScrollView.isHidden = true - containerView.balloonBackgroundView.isHidden = true - - if segment == 0 { - // 지역 탭 - containerView.locationScrollView.isHidden = false - if let selectedLocationIndex = reactor?.currentState.selectedLocationIndex, - let locations = reactor?.currentState.locations, - selectedLocationIndex >= 0, selectedLocationIndex < locations.count { - - let location = locations[selectedLocationIndex] - containerView.balloonBackgroundView.configure( - for: location.main, - subRegions: location.sub, - selectedRegions: reactor?.currentState.selectedSubRegions ?? [], - selectionHandler: { [weak self] subRegion in - self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) - }, - allSelectionHandler: { [weak self] in - self?.reactor?.action.onNext(.toggleAllSubRegions) - } - ) - - containerView.balloonBackgroundView.isHidden = false - let dynamicHeight = containerView.balloonBackgroundView.calculateHeight() - containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) - } - } else { - containerView.categoryCollectionView.isHidden = false - containerView.updateBalloonHeight(isHidden: true) - - // ★ 카테고리 탭에 들어오면, 한 번 더 layout 후 시트 높이 갱신 - DispatchQueue.main.async { - // 콜렉션뷰 레이아웃 강제 반영 - self.containerView.categoryCollectionView.layoutIfNeeded() - let contentHeight = self.containerView.categoryCollectionView.contentSize.height - - // 콜렉션뷰 높이 업데이트 - self.containerView.categoryCollectionView.snp.updateConstraints { make in - make.height.equalTo(contentHeight + 40) - } - self.containerView.layoutIfNeeded() - - // 시트 높이 갱신 - self.updateContainerHeight() - } - } - - containerView.layoutIfNeeded() - view.layoutIfNeeded() - } - - // 시트 높이 업데이트 - updateContainerHeight() - } - - private func updateContainerHeight() { - let segmentIndex = containerView.segmentedControl.selectedSegmentIndex - - let headerHeight = containerView.headerView.frame.height - let segmentHeight = containerView.segmentedControl.frame.height - let filterHeight = containerView.filterChipsView.frame.height - let buttonHeight: CGFloat = 52 - let padding: CGFloat = 60 - - let contentHeight: CGFloat - if segmentIndex == 0 { - let locationHeight = containerView.locationScrollView.frame.height - let balloonHeight = containerView.balloonBackgroundView.isHidden ? 0 : containerView.balloonBackgroundView.calculateHeight() - contentHeight = headerHeight + segmentHeight + locationHeight + balloonHeight + filterHeight + buttonHeight + padding - } else { - let categoryHeight = containerView.categoryCollectionView.frame.height - contentHeight = headerHeight + segmentHeight + categoryHeight + filterHeight + buttonHeight + padding - } - - let minHeight: CGFloat = 300 - let maxHeight = UIScreen.main.bounds.height * 0.7 - let newHeight = min(max(contentHeight, minHeight), maxHeight) + view.sendSubviewToBack(dimmedView) + dimmedView.isUserInteractionEnabled = true - UIView.animate(withDuration: 0.2) { - self.containerHeightConstraint?.update(offset: newHeight) - self.view.layoutIfNeeded() - } } private func setupCollectionView() { @@ -262,57 +112,60 @@ final class FilterBottomSheetViewController: UIViewController, View { containerView.categoryCollectionView.delegate = self } - // MARK: - Reactor Binding + // MARK: - Binding func bind(reactor: Reactor) { - // (1) 세그먼트 컨트롤 + // 1. 세그먼트 컨트롤 바인딩 containerView.segmentedControl.rx.selectedSegmentIndex - .do(onNext: { [weak self] segmentIndex in - self?.updateUIForCurrentTab(segment: segmentIndex) - self?.updateContainerHeight() - }) .map { Reactor.Action.segmentChanged($0) } .bind(to: reactor.action) .disposed(by: disposeBag) - // (2) 리셋 버튼 + // 2. 리셋 버튼 바인딩 containerView.resetButton.rx.tap - .do(onNext: { [weak self] _ in - guard let self = self, - let reactor = self.reactor, - let selectedIndex = reactor.currentState.selectedLocationIndex else { return } + .do(onNext: { [weak self] _ in + guard let self = self, + let reactor = self.reactor, + let selectedIndex = reactor.currentState.selectedLocationIndex else { return } + + let location = reactor.currentState.locations[selectedIndex] + // 현재 location에 대한 configure 재설정 + self.containerView.balloonBackgroundView.configure( + for: location.main, + subRegions: location.sub, + selectedRegions: reactor.currentState.selectedSubRegions, + selectionHandler: { [weak self] subRegion in + self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) + }, + allSelectionHandler: { [weak self] in + self?.reactor?.action.onNext(.toggleAllSubRegions) + } + ) + + + }) + .map { Reactor.Action.resetFilters } + .bind(to: reactor.action) + .disposed(by: disposeBag) + - let location = reactor.currentState.locations[selectedIndex] - self.containerView.balloonBackgroundView.configure( - for: location.main, - subRegions: location.sub, - selectedRegions: reactor.currentState.selectedSubRegions, - selectionHandler: { [weak self] subRegion in - self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) - }, - allSelectionHandler: { [weak self] in - self?.reactor?.action.onNext(.toggleAllSubRegions) - } - ) - }) - .map { Reactor.Action.resetFilters } - .bind(to: reactor.action) - .disposed(by: disposeBag) - // (3) 저장 containerView.saveButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } + let filterData: FilterData = ( locations: reactor.currentState.selectedSubRegions, categories: reactor.currentState.selectedCategories ) + self.onSave?(filterData) reactor.action.onNext(.applyFilters(filterData.locations + filterData.categories)) + self.hideBottomSheet() } .disposed(by: disposeBag) - // (4) 닫기 + containerView.closeButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -325,7 +178,8 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - // (5) 액티브 세그먼트 + + // 5. 탭 변경 reactor.state.map { $0.activeSegment } .distinctUntilChanged() .bind { [weak self] activeSegment in @@ -333,20 +187,28 @@ final class FilterBottomSheetViewController: UIViewController, View { if activeSegment == 0 { let dynamicHeight = self.containerView.balloonBackgroundView.calculateHeight() self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) - } else { + } else if activeSegment == 1 { self.containerView.updateBalloonHeight(isHidden: true) } self.containerView.updateContentVisibility(isCategorySelected: activeSegment == 1) + + // 여기에 컨테이너 높이 업데이트 추가 + self.updateContainerHeight() } .disposed(by: disposeBag) - // (6) 위치 리스트 - let locations = reactor.state.map { $0.locations }.distinctUntilChanged().share(replay: 1) + + // 6. 위치 데이터 바인딩 + let locations = reactor.state + .map { $0.locations } + .distinctUntilChanged() + .share(replay: 1) locations .observe(on: MainScheduler.instance) .bind { [weak self] locations in self?.containerView.setupLocationScrollView(locations: locations) { [weak self] index, button in guard let self = self else { return } + if index == 0 { if let selectedSubRegions = self.reactor?.currentState.selectedSubRegions, !selectedSubRegions.isEmpty { @@ -356,39 +218,38 @@ final class FilterBottomSheetViewController: UIViewController, View { } } self.reactor?.action.onNext(.selectLocation(index)) + self.containerView.updateBalloonPosition(for: button) } } .disposed(by: disposeBag) - // (7) locationAndSubRegions - reactor.state.map { ($0.selectedLocationIndex, $0.selectedSubRegions) } + + let locationAndSubRegions = reactor.state + .map { ($0.selectedLocationIndex, $0.selectedSubRegions) } .distinctUntilChanged { prev, curr in let isIndexSame = prev.0 == curr.0 let isSubRegionsSame = prev.1 == curr.1 return isIndexSame && isSubRegionsSame } .share(replay: 1) + + locationAndSubRegions .observe(on: MainScheduler.instance) .bind { [weak self] data in guard let self = self, let reactor = self.reactor else { return } let (selectedIndexOptional, selectedSubRegions) = data + guard let selectedIndex = selectedIndexOptional, selectedIndex >= 0, selectedIndex < reactor.currentState.locations.count else { return } - // 현재 탭이 지역(0)인지 체크해서 풍선 뷰 노출할지 결정 - let currentSegment = self.containerView.segmentedControl.selectedSegmentIndex - if currentSegment != 0 { - return - } - let location = reactor.currentState.locations[selectedIndex] self.containerView.balloonBackgroundView.configure( - for: location.main, - subRegions: location.sub, - selectedRegions: selectedSubRegions, + for: location.main, // 첫 번째 인자는 메인 지역(String) + subRegions: location.sub, // 두 번째 인자는 [String] + selectedRegions: selectedSubRegions, // 세 번째 인자는 [String] selectionHandler: { [weak self] subRegion in self?.reactor?.action.onNext(.toggleSubRegion(subRegion)) }, @@ -397,23 +258,20 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - // 화살표 위치 - let subviews = self.containerView.locationContentView.subviews - if selectedIndex < subviews.count, - let button = subviews[selectedIndex] as? UIButton { + + if let button = self.containerView.locationContentView.subviews[selectedIndex] as? UIButton { self.containerView.updateBalloonPosition(for: button) } DispatchQueue.main.async { let dynamicHeight = self.containerView.balloonBackgroundView.calculateHeight() self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: dynamicHeight) - self.updateContainerHeight() } + self.containerView.balloonBackgroundView.isHidden = false } .disposed(by: disposeBag) - // (8) 카테고리 바인딩 Observable.combineLatest( reactor.state.map { $0.categories }.distinctUntilChanged(), reactor.state.map { $0.selectedCategories }.distinctUntilChanged() @@ -439,14 +297,14 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - // (9) 필터칩 업데이트 + + reactor.state.map { $0.selectedSubRegions + $0.selectedCategories } .distinctUntilChanged() .bind { [weak self] selectedOptions in UIView.performWithoutAnimation { self?.containerView.filterChipsView.updateChips(with: selectedOptions) self?.containerView.layoutIfNeeded() - self?.updateContainerHeight() } } .disposed(by: disposeBag) @@ -461,6 +319,7 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) + reactor.state.map { $0.isSaveEnabled } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -477,77 +336,211 @@ final class FilterBottomSheetViewController: UIViewController, View { } } .disposed(by: disposeBag) - Observable.just(()) - .withLatestFrom(reactor.state) - .take(1) - .subscribe(onNext: { [weak self] state in - // 저장된 지역 필터 - if !state.savedSubRegions.isEmpty { - state.savedSubRegions.forEach { region in - reactor.action.onNext(.toggleSubRegion(region)) + .withLatestFrom(reactor.state) + .take(1) + .subscribe(onNext: { [weak self] state in + // 저장된 지역 필터 설정 + if !state.savedSubRegions.isEmpty { + state.savedSubRegions.forEach { region in + reactor.action.onNext(.toggleSubRegion(region)) + } } - } - // 저장된 카테고리 필터 - if !state.savedCategories.isEmpty { - state.savedCategories.forEach { category in - reactor.action.onNext(.toggleCategory(category)) + + // 저장된 카테고리 필터 설정 + if !state.savedCategories.isEmpty { + state.savedCategories.forEach { category in + reactor.action.onNext(.toggleCategory(category)) + } + } + + // 지역이 선택되어 있다면 해당 지역 버튼도 활성화 + if let locations = state.savedSubRegions.first?.split(separator: "/").first.map(String.init), + let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { + reactor.action.onNext(.selectLocation(index)) } + }) + .disposed(by: disposeBag) + + Observable.combineLatest( + reactor.state.map { $0.savedSubRegions }.distinctUntilChanged(), + reactor.state.map { $0.savedCategories }.distinctUntilChanged() + ) + .take(1) + .subscribe(onNext: { [weak self] (subRegions, categories) in + guard let self = self else { return } + + subRegions.forEach { region in + reactor.action.onNext(.toggleSubRegion(region)) } - // 위치 선택 - if let locMain = state.savedSubRegions.first?.split(separator: "/").first.map(String.init), - let idx = reactor.currentState.locations.firstIndex(where: { $0.main == locMain }) { - reactor.action.onNext(.selectLocation(idx)) + categories.forEach { category in + reactor.action.onNext(.toggleCategory(category)) } + + self.containerView.categoryCollectionView.reloadData() + self.containerView.balloonBackgroundView.setNeedsDisplay() }) .disposed(by: disposeBag) - // (12) 추가 combineLatest... (생략) } -} -// MARK: - UIGestureRecognizerDelegate -extension FilterBottomSheetViewController: UIGestureRecognizerDelegate { - func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - let point = touch.location(in: self.view) + private func updateContentVisibility(_ isCategoryTab: Bool, subRegionCount: Int) { + UIView.animate(withDuration: 0.3) { + self.containerView.updateContentVisibility(isCategorySelected: isCategoryTab) + if !isCategoryTab { + self.containerView.updateBalloonHeight(isHidden: false, dynamicHeight: subRegionCount > 0 ? self.containerView.balloonBackgroundView.calculateHeight() : 80) + } + self.view.layoutIfNeeded() + } + } + func updateContainerHeight() { + let contentHeight: CGFloat - if containerView.frame.contains(point) { - return false + if containerView.segmentedControl.selectedSegmentIndex == 0 { + // 지역탭일 때 + contentHeight = containerView.balloonBackgroundView.calculateHeight() + + containerView.filterChipsView.frame.height + + containerView.segmentedControl.frame.height + + containerView.saveButton.frame.height + 100 // 패딩 및 여유 높이 + } else { + // 카테고리탭일 때 + contentHeight = containerView.categoryCollectionView.contentSize.height + + containerView.filterChipsView.frame.height + + containerView.segmentedControl.frame.height + + containerView.saveButton.frame.height + 100 } - return true + // 최소 400, 최대는 화면 높이의 80%로 제한 + let finalHeight = min(max(contentHeight, 400), UIScreen.main.bounds.height * 0.8) + containerHeightConstraint?.update(offset: finalHeight) + + // 컨테이너 크기 변경 후 레이아웃 업데이트 + view.layoutIfNeeded() + } + + + private func setupGestures() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapDimmedView)) + tapGesture.delegate = self + dimmedView.addGestureRecognizer(tapGesture) + dimmedView.isUserInteractionEnabled = true + + // 패닝 제스처는 유지 + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture)) + containerView.addGestureRecognizer(panGesture) + } + + @objc private func handleDimmedViewTap() { + hideBottomSheet() + } + func showBottomSheet() { + guard let reactor = reactor else { return } + + // 1. 이전에 저장된 지역 필터가 있다면 해당 지역 버튼 활성화 + if let locations = reactor.currentState.savedSubRegions.first?.split(separator: "/").first.map(String.init), + let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { + reactor.action.onNext(.selectLocation(index)) + + + } + + // 4. 필터 칩 뷰 업데이트 + containerView.update( + locationText: reactor.currentState.savedSubRegions.joined(separator: ", "), + categoryText: reactor.currentState.savedCategories.joined(separator: ", ") + ) + + // 5. 애니메이션 + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseOut) { + self.dimmedView.alpha = 1 + self.bottomConstraint?.update(offset: 0) + self.view.layoutIfNeeded() + } + } + + + + func hideBottomSheet() { + UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { + self.dimmedView.alpha = 0 + self.bottomConstraint?.update(offset: UIScreen.main.bounds.height) + self.view.layoutIfNeeded() + } completion: { _ in + self.dismiss(animated: false) + self.onDismiss?() + } + } + + @objc private func handleTapDimmedView() { + hideBottomSheet() + } + + @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { + let translation = gesture.translation(in: view) + + switch gesture.state { + case .changed: + guard translation.y >= 0 else { return } + bottomConstraint?.update(offset: translation.y) + view.layoutIfNeeded() + + case .ended: + let velocity = gesture.velocity(in: view) + if translation.y > 150 || velocity.y > 1000 { + hideBottomSheet() + } else { + UIView.animate(withDuration: 0.25) { + self.bottomConstraint?.update(offset: 0) + self.view.layoutIfNeeded() + } + } + + default: + break + } } } -// MARK: - UICollectionViewDataSource extension FilterBottomSheetViewController: UICollectionViewDataSource { - func numberOfSections(in collectionView: UICollectionView) -> Int { return 1 } + func numberOfSections(in collectionView: UICollectionView) -> Int { + return 1 + } - func collectionView(_ collectionView: UICollectionView, - numberOfItemsInSection section: Int) -> Int { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return tagSection?.inputDataList.count ?? 0 } - func collectionView(_ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell( withReuseIdentifier: TagSectionCell.identifiers, for: indexPath ) as? TagSectionCell else { return UICollectionViewCell() } + if let input = tagSection?.inputDataList[indexPath.item] { cell.injection(with: input) } + return cell } } -// MARK: - UICollectionViewDelegateFlowLayout +// MARK: - UICollectionViewDelegate extension FilterBottomSheetViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { guard let category = tagSection?.inputDataList[indexPath.item].title else { return } reactor?.action.onNext(.toggleCategory(category)) } } +extension FilterBottomSheetViewController: UIGestureRecognizerDelegate { + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + if gestureRecognizer.view == dimmedView { + // 딤드 영역에서만 터치 인식 + let touchPoint = touch.location(in: view) + return !containerView.frame.contains(touchPoint) + } + return true + } +} From 3e5021321d75046fb69d9676ff7b160edb2ccdad Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 31 Mar 2025 14:33:51 +0900 Subject: [PATCH 022/393] =?UTF-8?q?feat/#92:=20URL=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EC=B4=88?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 12 +++++ .../ImageLoader/ImageLoader.swift | 50 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index a38282f7..10e9c12c 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -270,6 +270,7 @@ 0899526E2D0474340022AEF9 /* GetSearchPopUpListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899526D2D0474340022AEF9 /* GetSearchPopUpListResponse.swift */; }; 089952732D0475E90022AEF9 /* SearchResultCountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */; }; 089952752D0475F20022AEF9 /* SearchResultCountSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */; }; + 089B4FD82D9A57AE00FC0CC3 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */; }; 08A2E46C2D15BC5000102313 /* CommentLikeRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */; }; 08A2E4792D1B06A300102313 /* ImageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4782D1B06A300102313 /* ImageDetailView.swift */; }; 08A2E47B2D1B06AA00102313 /* ImageDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */; }; @@ -762,6 +763,7 @@ 0899526D2D0474340022AEF9 /* GetSearchPopUpListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSearchPopUpListResponse.swift; sourceTree = ""; }; 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSection.swift; sourceTree = ""; }; 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSectionCell.swift; sourceTree = ""; }; + 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentLikeRequestDTO.swift; sourceTree = ""; }; 08A2E4782D1B06A300102313 /* ImageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailView.swift; sourceTree = ""; }; 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailController.swift; sourceTree = ""; }; @@ -1494,6 +1496,7 @@ 083A25A02CF3623C0099B58E /* Infrastructure */ = { isa = PBXGroup; children = ( + 089B4FD62D9A576F00FC0CC3 /* ImageLoader */, 0841BA832CF9F61500049E31 /* PreSignedService */, 083A25B12CF362670099B58E /* NetworkLayer */, 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */, @@ -2311,6 +2314,14 @@ path = View; sourceTree = ""; }; + 089B4FD62D9A576F00FC0CC3 /* ImageLoader */ = { + isa = PBXGroup; + children = ( + 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */, + ); + path = ImageLoader; + sourceTree = ""; + }; 08A2E4772D1B069300102313 /* ImageDetail */ = { isa = PBXGroup; children = ( @@ -3316,6 +3327,7 @@ 4E685EDB2D12CEB6001EF91C /* MapAPIEndpoint.swift in Sources */, 086F89CC2D1E42B000CA4FC9 /* CommentUserBlockController.swift in Sources */, 08DC62032CF8AC06002A2F44 /* HomeView.swift in Sources */, + 089B4FD82D9A57AE00FC0CC3 /* ImageLoader.swift in Sources */, BD9103622CF6149D00BBCCAE /* LoginResponseDTO.swift in Sources */, 083C86642D0EC4A5003F441C /* InstaCommentAddReactor.swift in Sources */, 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */, diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift new file mode 100644 index 00000000..798074e6 --- /dev/null +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -0,0 +1,50 @@ +import UIKit + +//URL을 사용한 이미지 로드 +//메모리 캐싱 +//디스크 캐싱 +//일정 시간 후 캐싱 데이터를 제거 +//이미지 리사이징 모듈 + +enum ImageLoaderError: Error { + case invalidURL + case networkError(description: String?) +} + +class ImageLoader { + + static let shared = ImageLoader() + + private init() {} + + func loadImage(with stringURL: String?, completion: @escaping (Result) -> Void) { + guard let stringURL = stringURL, + let url = URL(string: stringURL) else { + completion(.failure(ImageLoaderError.invalidURL)) + return + } + + fetchImageFrom(url: url) { result in + completion(result) + } + } +} + +private extension ImageLoader { + func fetchImageFrom(url: URL, completion: @escaping (Result) -> Void) { + let task = URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + completion(.failure(ImageLoaderError.networkError(description: "Network Error: \(error.localizedDescription)"))) + return + } + + guard let data = data, let image = UIImage(data: data) else { + completion(.failure(ImageLoaderError.networkError(description: "Network Error: Invalid image data"))) + return + } + + completion(.success(image)) + } + task.resume() + } +} From 1541948e05f46dec9ef7832f4094053af5e6c350 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 31 Mar 2025 16:49:46 +0900 Subject: [PATCH 023/393] =?UTF-8?q?remove#93:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- "Poppool/AdminRepository\\.swift" | 8 -------- 1 file changed, 8 deletions(-) delete mode 100644 "Poppool/AdminRepository\\.swift" diff --git "a/Poppool/AdminRepository\\.swift" "b/Poppool/AdminRepository\\.swift" deleted file mode 100644 index b5f7eebc..00000000 --- "a/Poppool/AdminRepository\\.swift" +++ /dev/null @@ -1,8 +0,0 @@ -// -// AdminRepository\.swift -// Poppool -// -// Created by 김기현 on 1/6/25. -// - -import Foundation From c3ad17a0e1a1dd96ce0110b5f7477de92d163f59 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 31 Mar 2025 17:47:30 +0900 Subject: [PATCH 024/393] =?UTF-8?q?feat/#92:=20NSCache=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 --- .../ImageLoader/ImageLoader.swift | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift index 798074e6..bed25c13 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -14,6 +14,7 @@ enum ImageLoaderError: Error { class ImageLoader { static let shared = ImageLoader() + private static let memoryCache = NSCache() private init() {} @@ -24,8 +25,24 @@ class ImageLoader { return } + let cacheKey = url.absoluteString as NSString + + if let cachedImage = fetchImageFromMemory(forKey: cacheKey) { + completion(.success(cachedImage)) + return + } + fetchImageFrom(url: url) { result in - completion(result) + switch result { + case .success(let image): + if let image = image { + self.storeInMemoryCache(image: image, forKey: cacheKey) + } + completion(.success(image)) + + case .failure(let error): + completion(.failure(error)) + } } } } @@ -47,4 +64,12 @@ private extension ImageLoader { } task.resume() } + + func storeInMemoryCache(image: UIImage, forKey key: NSString) { + ImageLoader.memoryCache.setObject(image, forKey: key) + } + + func fetchImageFromMemory(forKey key: NSString) -> UIImage? { + return ImageLoader.memoryCache.object(forKey: key) + } } From 42d4a9541feee8ed37a7dedbc3d414bca1f97952 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 31 Mar 2025 19:43:18 +0900 Subject: [PATCH 025/393] =?UTF-8?q?feat/#92:=20=EB=A9=94=EB=AA=A8=EB=A6=AC?= =?UTF-8?q?=20=EC=BA=90=EC=8B=9C=20=EC=8A=A4=ED=86=A0=EB=A6=AC=EC=A7=80=20?= =?UTF-8?q?=EB=B6=84=EB=A6=AC=20=EB=B0=8F=20=EC=9E=90=EB=8F=99=20=EC=BA=90?= =?UTF-8?q?=EC=8B=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 + .../ImageLoader/ImageLoader.swift | 91 ++++++++++-------- .../ImageLoader/MemoryStorage.swift | 92 +++++++++++++++++++ .../Presentation/Extension/UIImageView+.swift | 19 ++-- 4 files changed, 161 insertions(+), 45 deletions(-) create mode 100644 Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 87a218d9..c447b668 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -271,6 +271,7 @@ 089952732D0475E90022AEF9 /* SearchResultCountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */; }; 089952752D0475F20022AEF9 /* SearchResultCountSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */; }; 089B4FD82D9A57AE00FC0CC3 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */; }; + 089B4FDF2D9A8F9A00FC0CC3 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */; }; 08A2E46C2D15BC5000102313 /* CommentLikeRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */; }; 08A2E4792D1B06A300102313 /* ImageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4782D1B06A300102313 /* ImageDetailView.swift */; }; 08A2E47B2D1B06AA00102313 /* ImageDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */; }; @@ -762,6 +763,7 @@ 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSection.swift; sourceTree = ""; }; 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSectionCell.swift; sourceTree = ""; }; 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; + 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = ""; }; 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentLikeRequestDTO.swift; sourceTree = ""; }; 08A2E4782D1B06A300102313 /* ImageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailView.swift; sourceTree = ""; }; 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailController.swift; sourceTree = ""; }; @@ -2314,6 +2316,7 @@ isa = PBXGroup; children = ( 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */, + 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */, ); path = ImageLoader; sourceTree = ""; @@ -3327,6 +3330,7 @@ 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */, 081898C52D30AEF40067BF01 /* GetMyProfileResponse.swift in Sources */, BD9103922CF6166800BBCCAE /* SplashView.swift in Sources */, + 089B4FDF2D9A8F9A00FC0CC3 /* MemoryStorage.swift in Sources */, 0899526E2D0474340022AEF9 /* GetSearchPopUpListResponse.swift in Sources */, 08B191392CF366680057BC04 /* UITableViewCell+.swift in Sources */, 08A2E48F2D1BF6E500102313 /* CommentListView.swift in Sources */, diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift index bed25c13..512beafd 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -1,75 +1,90 @@ import UIKit -//URL을 사용한 이미지 로드 -//메모리 캐싱 -//디스크 캐싱 -//일정 시간 후 캐싱 데이터를 제거 -//이미지 리사이징 모듈 - enum ImageLoaderError: Error { case invalidURL case networkError(description: String?) + case convertError(description: String?) +} + +/// 이미지 로더 설정 클래스 +/// - `memoryCacheExpiration`: 메모리 캐시 만료 시간 (기본값 300초) +class ImageLoaderConfigure { + var memoryCacheExpiration: TimeInterval = 300 } +/// URL을 통해 이미지를 비동기적으로 로드하는 클래스 class ImageLoader { static let shared = ImageLoader() - private static let memoryCache = NSCache() + + /// 이미지 로더 설정 객체 + let configure = ImageLoaderConfigure() private init() {} + /// URL을 통해 이미지를 로드하고, 실패 시 기본 이미지를 반환하는 메서드 + /// - Parameters: + /// - stringURL: 이미지 URL 문자열 + /// - defaultImage: 로드 실패 시 반환할 기본 이미지 + /// - completion: 로드 완료 후 호출되는 클로저 + func loadImage(with stringURL: String?, defaultImage: UIImage?, completion: @escaping (UIImage?) -> Void) { + loadImage(with: stringURL) { result in + switch result { + case .success(let image): + completion(image) + case .failure: + completion(defaultImage) + } + } + } +} + +private extension ImageLoader { + + /// URL을 통해 이미지를 로드하는 내부 메서드 + /// - Parameters: + /// - stringURL: 이미지 URL 문자열 + /// - completion: 로드 완료 후 호출되는 클로저 func loadImage(with stringURL: String?, completion: @escaping (Result) -> Void) { - guard let stringURL = stringURL, - let url = URL(string: stringURL) else { + guard let stringURL = stringURL, let url = URL(string: stringURL) else { completion(.failure(ImageLoaderError.invalidURL)) return } - - let cacheKey = url.absoluteString as NSString - - if let cachedImage = fetchImageFromMemory(forKey: cacheKey) { + + // 메모리 캐시에서 이미지 조회 + if let cachedImage = MemoryStorage.shared.fetchImage(url: stringURL) { completion(.success(cachedImage)) return } - fetchImageFrom(url: url) { result in + // 네트워크에서 데이터 요청 + fetchDataFrom(url: url) { result in switch result { - case .success(let image): - if let image = image { - self.storeInMemoryCache(image: image, forKey: cacheKey) + case .success(let data): + if let data = data, let image = UIImage(data: data) { + MemoryStorage.shared.store(image: image, url: stringURL) + completion(.success(image)) + } else { + completion(.failure(ImageLoaderError.convertError(description: "Failed to convert data to UIImage"))) } - completion(.success(image)) - case .failure(let error): completion(.failure(error)) } } } -} - -private extension ImageLoader { - func fetchImageFrom(url: URL, completion: @escaping (Result) -> Void) { + + /// URL을 통해 데이터를 요청하는 메서드 + /// - Parameters: + /// - url: 요청할 URL 객체 + /// - completion: 요청 완료 후 호출되는 클로저 + func fetchDataFrom(url: URL, completion: @escaping (Result) -> Void) { let task = URLSession.shared.dataTask(with: url) { data, response, error in if let error = error { completion(.failure(ImageLoaderError.networkError(description: "Network Error: \(error.localizedDescription)"))) return } - - guard let data = data, let image = UIImage(data: data) else { - completion(.failure(ImageLoaderError.networkError(description: "Network Error: Invalid image data"))) - return - } - - completion(.success(image)) + completion(.success(data)) } task.resume() } - - func storeInMemoryCache(image: UIImage, forKey key: NSString) { - ImageLoader.memoryCache.setObject(image, forKey: key) - } - - func fetchImageFromMemory(forKey key: NSString) -> UIImage? { - return ImageLoader.memoryCache.object(forKey: key) - } } diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift new file mode 100644 index 00000000..b2203678 --- /dev/null +++ b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift @@ -0,0 +1,92 @@ +import UIKit + +/// 캐시할 이미지와 만료 시간을 저장하는 클래스 +class StorageData: NSObject { + let image: UIImage? /// 캐시된 이미지 + let expirationDate: Date /// 캐시 만료 시간 + + /// 초기화 메서드 + /// - Parameters: + /// - image: 저장할 이미지 + /// - expiration: 만료 시간 (초 단위) + init(image: UIImage?, expiration: TimeInterval) { + self.image = image + self.expirationDate = Date().addingTimeInterval(expiration) + } + + /// 캐시가 만료되었는지 확인하는 메서드 + /// - Returns: 만료 여부 (true: 만료됨, false: 유효함) + func isExpired() -> Bool { + return Date() > expirationDate + } +} + +/// 메모리 캐시를 관리하는 클래스 +class MemoryStorage { + + /// 싱글톤 인스턴스 + static let shared = MemoryStorage() + + /// 이미지 캐시 저장소 + private let cache = NSCache() + + /// 현재 캐시에 저장된 키 목록 + private var cachedKeys: Set = [] + + /// 초기화 (자동 캐시 정리 시작) + private init() { + startCacheCleanup() + } + + /// 이미지를 캐시에 저장하는 메서드 + /// - Parameters: + /// - image: 저장할 이미지 + /// - url: 이미지 URL 문자열 + func store(image: UIImage?, url: String) { + let cachedData = StorageData(image: image, expiration: ImageLoader.shared.configure.memoryCacheExpiration) + cache.setObject(cachedData, forKey: url as NSString) + cachedKeys.insert(url) + } + + /// 캐시에서 이미지를 가져오는 메서드 + /// - Parameter url: 이미지 URL 문자열 + /// - Returns: 캐시된 UIImage (없으면 nil) + func fetchImage(url: String) -> UIImage? { + if let cachedData = cache.object(forKey: url as NSString), !cachedData.isExpired() { + return cachedData.image + } else { + removeData(url: url) + return nil + } + } + + /// 특정 URL의 캐시 데이터를 제거하는 메서드 + /// - Parameter url: 제거할 이미지의 URL 문자열 + func removeData(url: String) { + cache.removeObject(forKey: url as NSString) + cachedKeys.remove(url) + } + + /// 모든 캐시 데이터를 삭제하는 메서드 + func clearCache() { + cache.removeAllObjects() + cachedKeys.removeAll() + } + + /// 주기적으로 만료된 캐시를 정리하는 메서드 + private func startCacheCleanup() { + DispatchQueue.global(qos: .background).async { [weak self] in + guard let self = self else { return } + + Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in + for key in self.cachedKeys { + let nsKey = key as NSString + if let cachedData = self.cache.object(forKey: nsKey), cachedData.isExpired() { + self.cache.removeObject(forKey: nsKey) + self.cachedKeys.remove(key) + } + } + } + } + } +} diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index 8040bddb..8b7b398c 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -17,15 +17,20 @@ extension UIImageView { } let imageURLString = Secrets.popPoolS3BaseURL.rawValue + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { - let imageURL = URL(string: cenvertimageURL) - self.kf.setImage(with: imageURL) { result in - switch result { - case .failure(let error): - Logger.log(message: "\(path) image Load Fail: \(error.localizedDescription)", category: .error) - default: - break + ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default")) { [weak self] image in + DispatchQueue.main.async { + self?.image = image } } +// let imageURL = URL(string: cenvertimageURL) +// self.kf.setImage(with: imageURL) { result in +// switch result { +// case .failure(let error): +// Logger.log(message: "\(path) image Load Fail: \(error.localizedDescription)", category: .error) +// default: +// break +// } +// } } } From 8e0e591eeb732c0a7b7b1e714db112828af2a468 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 1 Apr 2025 00:50:38 +0900 Subject: [PATCH 026/393] =?UTF-8?q?docs/#93:=20SwiftLint=20=EB=AC=B8?= =?UTF-8?q?=EB=B2=95=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/.swiftlint.yml | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Poppool/.swiftlint.yml b/Poppool/.swiftlint.yml index b1f8439d..0686d111 100644 --- a/Poppool/.swiftlint.yml +++ b/Poppool/.swiftlint.yml @@ -1,15 +1,21 @@ # 기본 활성화된 룰 중에 비활성화할 룰을 지정 disabled_rules: - + - redundant_optional_initialization # 기본(default) 룰이 아닌 룰들을 활성화 opt_in_rules: - - -# swiftlint 과정에 포함할 파일 경로 -included: + - sorted_imports + - direct_return + - file_header + - weak_delegate +# 기본 활성화된 룰 중에 조건을 변경할 룰 +file_length: 400 -# swiftlint를 적용하지 않게 설정할 파일 경로 -excluded: +function_body_length: + warning: 100 + error: 150 +cyclomatic_complexity: + warning: 15 + error: 30 From 395729f400b6f60af1083a09ebd9fbf8a10e702a Mon Sep 17 00:00:00 2001 From: JunYoung Date: Tue, 1 Apr 2025 19:27:24 +0900 Subject: [PATCH 027/393] =?UTF-8?q?feat/#92:=20DiskStorage=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 --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 + .../ImageLoader/DiskStorage.swift | 155 ++++++++++++++++++ .../ImageLoader/ImageLoader.swift | 14 +- .../ImageLoader/MemoryStorage.swift | 7 +- 4 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index c447b668..d4a99af2 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -353,6 +353,7 @@ 08CBEA3A2D3FABE100248007 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA392D3FABE100248007 /* ToastView.swift */; }; 08CBEA3C2D3FABED00248007 /* BookMarkToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA3B2D3FABED00248007 /* BookMarkToastView.swift */; }; 08CBEA3E2D3FF6A100248007 /* PopUpCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA3D2D3FF6A100248007 /* PopUpCardView.swift */; }; + 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */; }; 08DC61F32CF75037002A2F44 /* KeyChainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F22CF75037002A2F44 /* KeyChainService.swift */; }; 08DC61F52CF765B5002A2F44 /* UserDefaultService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */; }; 08DC61F82CF76843002A2F44 /* SignUpCompleteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F72CF76843002A2F44 /* SignUpCompleteView.swift */; }; @@ -845,6 +846,7 @@ 08CBEA392D3FABE100248007 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; 08CBEA3B2D3FABED00248007 /* BookMarkToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkToastView.swift; sourceTree = ""; }; 08CBEA3D2D3FF6A100248007 /* PopUpCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpCardView.swift; sourceTree = ""; }; + 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = ""; }; 08DC61F22CF75037002A2F44 /* KeyChainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyChainService.swift; sourceTree = ""; }; 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultService.swift; sourceTree = ""; }; 08DC61F72CF76843002A2F44 /* SignUpCompleteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCompleteView.swift; sourceTree = ""; }; @@ -2317,6 +2319,7 @@ children = ( 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */, 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */, + 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */, ); path = ImageLoader; sourceTree = ""; @@ -3485,6 +3488,7 @@ 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */, 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */, 083A258D2CF361F90099B58E /* ConventionCollectionViewCell.swift in Sources */, + 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */, 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */, 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */, 081898FB2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift in Sources */, diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift new file mode 100644 index 00000000..92c0f05c --- /dev/null +++ b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift @@ -0,0 +1,155 @@ +import UIKit +import CryptoKit + +/// 디스크에 이미지를 캐싱하는 클래스 +final class DiskStorage { + + /// 싱글톤 인스턴스 + static let shared = DiskStorage() + + /// 파일 관리 객체 + private let fileManager = FileManager.default + + /// 이미지 캐시 디렉터리 경로 + private let cacheDirectory: URL + + /// 초기화 메서드 (캐시 디렉터리 생성 및 자동 삭제 스케줄 시작) + private init() { + let urls = fileManager.urls(for: .cachesDirectory, in: .userDomainMask) + cacheDirectory = urls[0].appendingPathComponent("ImageCache") + + // 디렉터리가 존재하지 않으면 생성 + if !fileManager.fileExists(atPath: cacheDirectory.path) { + try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true, attributes: nil) + } + startCacheCleanup() + } + + /// URL을 안전한 파일명으로 변환하는 메서드 + /// - Parameter url: 원본 URL 문자열 + /// - Returns: 파일명으로 변환된 문자열 + private func cacheFileName(for url: String) -> String { + let data = Data(url.utf8) + let hashed = SHA256.hash(data: data) + return hashed.compactMap { String(format: "%02x", $0) }.joined() + } + + /// 이미지를 디스크에 저장하는 메서드 + /// - Parameters: + /// - image: 저장할 UIImage 객체 + /// - url: 해당 이미지의 원본 URL 문자열 + func store(image: UIImage, url: String) { + let fileName = cacheFileName(for: url) + let fileURL = cacheDirectory.appendingPathComponent(fileName) + let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") + + // 이미지 데이터를 JPEG 형식으로 변환하여 저장 + if let data = image.jpegData(compressionQuality: 0.8) { + do { + try data.write(to: fileURL) + } catch { + print("Error writing image data to disk: \(error)") + } + } + + // 만료 시간 기록 + let expirationDate = Date().addingTimeInterval(ImageLoader.shared.configure.diskCacheExpiration) + let metadata = ["expiration": expirationDate.timeIntervalSince1970] + + // 만료 정보를 JSON 형태로 저장 + if let metadataData = try? JSONSerialization.data(withJSONObject: metadata) { + do { + try metadataData.write(to: metadataURL) + } catch { + print("Error writing metadata: \(error)") + } + } + } + + /// 디스크에서 이미지를 불러오는 메서드 (만료된 경우 자동 삭제) + /// - Parameter url: 이미지의 원본 URL 문자열 + /// - Returns: UIImage 객체 (없거나 만료된 경우 nil) + func fetchImage(url: String) -> UIImage? { + let fileName = cacheFileName(for: url) + let fileURL = cacheDirectory.appendingPathComponent(fileName) + let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") + + // 만료 시간 확인 + if let metadataData = try? Data(contentsOf: metadataURL), + let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], + let expirationTime = metadata["expiration"] { + + // 만료 시간이 현재 시각을 초과하면 삭제 후 nil 반환 + if Date().timeIntervalSince1970 > expirationTime { + removeImage(url: url) + return nil + } + } + + // 이미지 파일이 존재하면 로드하여 반환 + if let data = try? Data(contentsOf: fileURL) { + return UIImage(data: data) + } + + return nil + } + + /// 특정 URL에 해당하는 이미지를 디스크에서 삭제하는 메서드 + /// - Parameter url: 삭제할 이미지의 원본 URL 문자열 + func removeImage(url: String) { + let fileName = cacheFileName(for: url) + let fileURL = cacheDirectory.appendingPathComponent(fileName) + let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") + + do { + try fileManager.removeItem(at: fileURL) // 이미지 파일 삭제 + try fileManager.removeItem(at: metadataURL) // 메타데이터 파일 삭제 + } catch { + print("Failed to remove image: \(error)") + } + } + + /// 모든 캐시 데이터를 삭제하는 메서드 + func clearCache() { + do { + try fileManager.removeItem(at: cacheDirectory) + try fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true, attributes: nil) + } catch { + print("Failed to clear cache: \(error)") + } + } + + /// 주기적으로 만료된 캐시를 삭제하는 메서드 + /// - 5분(300초)마다 실행되며, 만료된 이미지와 메타데이터를 정리함. + private func startCacheCleanup() { + DispatchQueue.global(qos: .background).async { [weak self] in + guard let self = self else { return } + + let cleanTimer = Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in + let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] + + for file in files { + if file.pathExtension == "metadata", + let metadataData = try? Data(contentsOf: file), + let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], + let expirationTime = metadata["expiration"] { + + // 만료 시간이 지나면 이미지와 메타데이터 삭제 + if Date().timeIntervalSince1970 > expirationTime { + let imageFileURL = file.deletingPathExtension() // 메타데이터와 동일한 이름의 이미지 파일 + do { + try self.fileManager.removeItem(at: imageFileURL) + try self.fileManager.removeItem(at: file) // 메타데이터 삭제 + } catch { + print("Failed to delete expired cache: \(error)") + } + } + } + } + } + // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 + RunLoop.current.add(cleanTimer, forMode: .common) + RunLoop.current.run() // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 + } + } +} diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift index 512beafd..90df18cc 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -10,10 +10,11 @@ enum ImageLoaderError: Error { /// - `memoryCacheExpiration`: 메모리 캐시 만료 시간 (기본값 300초) class ImageLoaderConfigure { var memoryCacheExpiration: TimeInterval = 300 + var diskCacheExpiration: TimeInterval = 86_400 } /// URL을 통해 이미지를 비동기적으로 로드하는 클래스 -class ImageLoader { +final class ImageLoader { static let shared = ImageLoader() @@ -50,19 +51,28 @@ private extension ImageLoader { completion(.failure(ImageLoaderError.invalidURL)) return } - + // 메모리 캐시에서 이미지 조회 if let cachedImage = MemoryStorage.shared.fetchImage(url: stringURL) { completion(.success(cachedImage)) return } + // 디스크 캐시 확인 + if let diskImage = DiskStorage.shared.fetchImage(url: stringURL) { + // 메모리 캐시에 저장 후 반환 + MemoryStorage.shared.store(image: diskImage, url: stringURL) + completion(.success(diskImage)) + return + } + // 네트워크에서 데이터 요청 fetchDataFrom(url: url) { result in switch result { case .success(let data): if let data = data, let image = UIImage(data: data) { MemoryStorage.shared.store(image: image, url: stringURL) + DiskStorage.shared.store(image: image, url: stringURL) completion(.success(image)) } else { completion(.failure(ImageLoaderError.convertError(description: "Failed to convert data to UIImage"))) diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift index b2203678..517bc9a7 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift @@ -22,7 +22,7 @@ class StorageData: NSObject { } /// 메모리 캐시를 관리하는 클래스 -class MemoryStorage { +final class MemoryStorage { /// 싱글톤 인스턴스 static let shared = MemoryStorage() @@ -78,7 +78,7 @@ class MemoryStorage { DispatchQueue.global(qos: .background).async { [weak self] in guard let self = self else { return } - Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in + let cleanTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in for key in self.cachedKeys { let nsKey = key as NSString if let cachedData = self.cache.object(forKey: nsKey), cachedData.isExpired() { @@ -87,6 +87,9 @@ class MemoryStorage { } } } + // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 + RunLoop.current.add(cleanTimer, forMode: .common) + RunLoop.current.run() // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 } } } From b529e2424633912701c8311d809729cee3cedd28 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Tue, 1 Apr 2025 20:16:48 +0900 Subject: [PATCH 028/393] =?UTF-8?q?feat/#92:=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=EB=A6=AC=EC=82=AC=EC=9D=B4=EC=A7=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageLoader/ImageLoader.swift | 55 ++++++++++++++++++- .../Presentation/Extension/UIImageView+.swift | 2 +- 2 files changed, 53 insertions(+), 4 deletions(-) diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift index 90df18cc..9e254f47 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -6,6 +6,26 @@ enum ImageLoaderError: Error { case convertError(description: String?) } +enum ImageSizeOption { + case low + case middle + case high + case origin + + var size: CGSize { + switch self { + case .low: + return CGSize(width: 100, height: 100) + case .middle: + return CGSize(width: 200, height: 200) + case .high: + return CGSize(width: 400, height: 400) + case .origin: + return CGSize(width: 1000, height: 1000) + } + } +} + /// 이미지 로더 설정 클래스 /// - `memoryCacheExpiration`: 메모리 캐시 만료 시간 (기본값 300초) class ImageLoaderConfigure { @@ -28,11 +48,16 @@ final class ImageLoader { /// - stringURL: 이미지 URL 문자열 /// - defaultImage: 로드 실패 시 반환할 기본 이미지 /// - completion: 로드 완료 후 호출되는 클로저 - func loadImage(with stringURL: String?, defaultImage: UIImage?, completion: @escaping (UIImage?) -> Void) { - loadImage(with: stringURL) { result in + func loadImage( + with stringURL: String?, + defaultImage: UIImage?, + imageQuality: ImageSizeOption = .origin, + completion: @escaping (UIImage?) -> Void + ) { + loadImage(with: stringURL) { [weak self] result in switch result { case .success(let image): - completion(image) + completion(self?.resizeImage(image, defaultImage: defaultImage, with: imageQuality)) case .failure: completion(defaultImage) } @@ -97,4 +122,28 @@ private extension ImageLoader { } task.resume() } + + func resizeImage(_ image: UIImage?, defaultImage: UIImage?, with sizeOption: ImageSizeOption) -> UIImage? { + guard let image else { return defaultImage } + + if sizeOption == .origin { return image } + + let targetSize = sizeOption.size + + // 비율 유지 리사이징 + let aspectRatio = image.size.width / image.size.height + var newSize = targetSize + + if aspectRatio > 1 { // 가로 이미지 + newSize.height = targetSize.width / aspectRatio + } else { // 세로 이미지 + newSize.width = targetSize.height * aspectRatio + } + + let renderer = UIGraphicsImageRenderer(size: newSize) + + return renderer.image { _ in + image.draw(in: CGRect(origin: .zero, size: newSize)) + } + } } diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index 8b7b398c..f2670759 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -17,7 +17,7 @@ extension UIImageView { } let imageURLString = Secrets.popPoolS3BaseURL.rawValue + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { - ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default")) { [weak self] image in + ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in DispatchQueue.main.async { self?.image = image } From ed0e2355aa5b3729e2e8e04a1d3b6afc56fd2804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 30 Mar 2025 18:23:05 +0900 Subject: [PATCH 029/393] =?UTF-8?q?[FIX]=20=ED=95=84=ED=84=B0=EB=B0=94?= =?UTF-8?q?=ED=85=80=EC=8B=9C=ED=8A=B8=EB=B7=B0=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EB=94=A4=EB=93=9C=EC=98=81=EC=97=AD=20=ED=83=AD=EC=8B=9C=20?= =?UTF-8?q?=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EB=8B=AB=ED=9E=88=EC=A7=80?= =?UTF-8?q?=EC=95=8A=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Map/Common/RegionDefinitions.swift | 2 - .../FullScreenMapViewController.swift | 56 ++++++------- .../MapGuideView/MapGuideViewController.swift | 82 ++++++++++++++----- .../Map/MapView/MapViewController.swift | 17 +--- 4 files changed, 86 insertions(+), 71 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift b/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift index 8de0e178..92e35ab1 100644 --- a/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift +++ b/Poppool/Poppool/Presentation/Map/Common/RegionDefinitions.swift @@ -27,7 +27,6 @@ enum RegionType { case province struct RegionDefinitions { - // 서울 클러스터 static let seoulClusters: [RegionCluster] = [ RegionCluster( name: "도봉/노원/강북/중랑", @@ -201,7 +200,6 @@ enum RegionType { ) ] - // 도 클러스터 static let provinceClusters: [RegionCluster] = [ RegionCluster( name: "충북", diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 083dfdc3..8eeaa084 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -105,24 +105,26 @@ class FullScreenMapViewController: MapViewController { let position = NMGLatLng(lat: store.latitude, lng: store.longitude) - // 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: position, zoomTo: 15.0) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - // 기존 마커가 있으면 재활용 if let existingMarker = initialMarker { // 기존 마커가 맵뷰에 설정되어 있지 않으면 설정 if existingMarker.mapView == nil { existingMarker.mapView = mainView.mapView } - // 마커 스타일 업데이트 - updateMarkerStyle(marker: existingMarker, selected: true, isCluster: false, count: 1) + // 명시적으로 TapMarker 스타일 적용 (selected 매개변수는 무시됨) + existingMarker.iconImage = NMFOverlayImage(name: "TapMarker") + existingMarker.width = 44 + existingMarker.height = 44 + existingMarker.anchor = CGPoint(x: 0.5, y: 1.0) + currentMarker = existingMarker } else { - // 기존 마커가 없는 경우에만 새로 생성 + // 새 마커 생성 시에도 TapMarker 적용 let marker = NMFMarker() marker.position = position marker.iconImage = NMFOverlayImage(name: "TapMarker") @@ -132,11 +134,11 @@ class FullScreenMapViewController: MapViewController { marker.userInfo = ["storeData": store] marker.mapView = mainView.mapView currentMarker = marker - - // 마커 스타일 업데이트 - updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: 1) } + // 마커 잠금 설정 + markerLocked = true + // 캐러셀 설정 currentCarouselStores = [store] carouselView.updateCards([store]) @@ -144,6 +146,7 @@ class FullScreenMapViewController: MapViewController { } + override func bind(reactor: MapReactor) { super.bind(reactor: reactor) @@ -171,54 +174,49 @@ class FullScreenMapViewController: MapViewController { } override func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { - if selected { - // 선택된 경우 항상 TapMarker + // 풀스크린 모드에서는 항상 TapMarker 스타일 적용 + if isFullScreenMode && markerLocked { marker.width = 44 marker.height = 44 marker.iconImage = NMFOverlayImage(name: "TapMarker") - } else { - // 선택되지 않은 경우 일반 마커 - marker.width = 32 - marker.height = 32 - marker.iconImage = NMFOverlayImage(name: "Marker") - } - marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.anchor = CGPoint(x: 0.5, y: 1.0) - if count > 1 { - marker.captionText = "\(count)" - } else { - marker.captionText = "" + if count > 1 { + marker.captionText = "\(count)" + } else { + marker.captionText = "" + } + return } + + super.updateMarkerStyle(marker: marker, selected: selected, isCluster: isCluster, count: count, regionName: regionName) } + + override func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true - markerLocked = true // 마커 상태 잠금 + markerLocked = true - // 이전 마커 선택 상태 해제 if let previousMarker = currentMarker, previousMarker != marker { fullScreenUpdateMarkerStyle(marker: previousMarker, selected: false) } - // 현재 마커를 TapMarker로 설정 marker.iconImage = NMFOverlayImage(name: "TapMarker") marker.width = 44 marker.height = 44 fullScreenUpdateMarkerStyle(marker: marker, selected: true) currentMarker = marker - // 캐러셀 업데이트 및 표시 currentCarouselStores = [store] carouselView.updateCards([store]) carouselView.isHidden = false mainView.setStoreCardHidden(false, animated: true) - // 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: 15.0) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - // 약간의 지연 후 플래그 리셋 DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in self?.isMovingToMarker = false } @@ -228,15 +226,12 @@ class FullScreenMapViewController: MapViewController { // 맵뷰 탭 처리 오버라이드 override func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { - // 풀스크린 모드에서는 맵 탭 시 아무 동작도 하지 않음 (캐러셀 유지) return } // 카메라 이동 시작 시 호출 override func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) { - // 풀스크린 모드에서는 캐러셀 유지 if isFullScreenMode && markerLocked { - // 상위 클래스의 기본 동작 방지 return } super.mapView(mapView, cameraWillChangeByReason: reason, animated: animated) @@ -244,7 +239,6 @@ class FullScreenMapViewController: MapViewController { // 카메라 이동 중 호출 override func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) { - // 마커가 잠겨있을 때는 캐러셀 유지 if isFullScreenMode && markerLocked { // 기존 동작을 방지하고 풀스크린 동작 수행 return diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index 78a5d81a..3776cebe 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -10,11 +10,12 @@ final class MapGuideViewController: UIViewController, View { var disposeBag = DisposeBag() private let popUpStoreId: Int64 private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 - + init(popUpStoreId: Int64) { self.popUpStoreId = popUpStoreId super.init(nibName: nil, bundle: nil) + modalPresentationStyle = .overFullScreen // 모달 스타일 설정 } required init?(coder: NSCoder) { @@ -23,7 +24,7 @@ final class MapGuideViewController: UIViewController, View { private let dimmingView: UIView = { let v = UIView() - v.backgroundColor = UIColor.gray.withAlphaComponent(0.3) + v.backgroundColor = UIColor.gray.withAlphaComponent(0.7) // 더 진한 딤드 효과 v.alpha = 0 return v }() @@ -101,9 +102,9 @@ final class MapGuideViewController: UIViewController, View { return btn }() - private let tmapButton: UIButton = { + private let appleButton: UIButton = { let btn = UIButton() - btn.setImage(UIImage(named: "TMap"), for: .normal) + btn.setImage(UIImage(named: "AppleMap"), for: .normal) btn.layer.cornerRadius = 24 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.g100.cgColor @@ -117,16 +118,33 @@ final class MapGuideViewController: UIViewController, View { override func viewDidLoad() { super.viewDidLoad() setupUI() + setupTapGesture() // 탭 제스처 설정 추가 presentModalCard() } + // 딤드 영역 탭 제스처 설정 + private func setupTapGesture() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOnDimmingView)) + dimmingView.addGestureRecognizer(tapGesture) + dimmingView.isUserInteractionEnabled = true // 중요: 상호작용 활성화 + } + + // 딤드 영역 탭 처리 + @objc private func handleTapOnDimmingView(_ sender: UITapGestureRecognizer) { + // 탭 위치가 modalCardView 영역이 아닌 경우에만 닫기 + let location = sender.location(in: view) + if !modalCardView.frame.contains(location) { + dismissModalCard() + } + } + func bind(reactor: MapGuideReactor) { reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) // 닫기 버튼 closeButton.rx.tap .subscribe(onNext: { [weak self] in - self?.dismiss(animated: true, completion: nil) + self?.dismissModalCard() }) .disposed(by: disposeBag) @@ -139,8 +157,8 @@ final class MapGuideViewController: UIViewController, View { .map { Reactor.Action.openMapApp("kakao") } .bind(to: reactor.action) .disposed(by: disposeBag) - tmapButton.rx.tap - .map { Reactor.Action.openMapApp("tmap") } + appleButton.rx.tap + .map { Reactor.Action.openMapApp("apple") } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -157,11 +175,21 @@ final class MapGuideViewController: UIViewController, View { if let selectedStore = self.currentCarouselStores.first { reactor.action.onNext(.didSelectItem(selectedStore)) - // 기존 코드 그대로 사용 - let fullScreenMapVC = FullScreenMapViewController(store: selectedStore) - fullScreenMapVC.reactor = reactor - - let nav = UINavigationController(rootViewController: fullScreenMapVC) + // 현재 맵에 표시된 마커 생성 또는 가져오기 + let marker = NMFMarker() + marker.position = NMGLatLng(lat: selectedStore.latitude, lng: selectedStore.longitude) + // 중요: 명시적으로 TapMarker 설정 + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.userInfo = ["storeData": selectedStore] + + // 풀스크린 맵 뷰 컨트롤러에 선택된 마커 정보 전달 + let fullScreenMapViewController = FullScreenMapViewController(store: selectedStore, existingMarker: marker) + fullScreenMapViewController.reactor = reactor + + let nav = UINavigationController(rootViewController: fullScreenMapViewController) nav.modalPresentationStyle = .fullScreen self.present(nav, animated: true) } else { @@ -174,10 +202,19 @@ final class MapGuideViewController: UIViewController, View { .take(1) .observe(on: MainScheduler.instance) .subscribe(onNext: { store in - let fullScreenMapVC = FullScreenMapViewController(store: store) - fullScreenMapVC.reactor = reactor - - let nav = UINavigationController(rootViewController: fullScreenMapVC) + // 여기서도 동일하게 마커 생성 및 설정 + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.userInfo = ["storeData": store] + + let fullScreenMapViewController = FullScreenMapViewController(store: store, existingMarker: marker) + fullScreenMapViewController.reactor = reactor + + let nav = UINavigationController(rootViewController: fullScreenMapViewController) nav.modalPresentationStyle = .fullScreen self.present(nav, animated: true) }) @@ -220,7 +257,7 @@ final class MapGuideViewController: UIViewController, View { // MARK: - UI Setup private func setupUI() { - view.backgroundColor = .white + view.backgroundColor = .clear // 배경색을 clear로 변경하여 항상 딤드 뷰가 보이도록 함 view.addSubview(dimmingView) dimmingView.snp.makeConstraints { $0.edges.equalToSuperview() } @@ -256,7 +293,7 @@ final class MapGuideViewController: UIViewController, View { mapView.snp.makeConstraints { make in make.top.equalTo(topContainer.snp.bottom).offset(20) make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(320) + make.height.equalTo(240) // 약간 줄임 } modalCardView.addSubview(expandButton) @@ -267,7 +304,7 @@ final class MapGuideViewController: UIViewController, View { } let bottomContainer = UIView() - modalCardView.addSubview(bottomContainer) + modalCardView.addSubview(bottomContainer) bottomContainer.snp.makeConstraints { make in make.top.equalTo(mapView.snp.bottom).offset(20) make.leading.trailing.equalToSuperview().inset(20) @@ -281,7 +318,7 @@ final class MapGuideViewController: UIViewController, View { make.centerY.equalToSuperview() } - let appStack = UIStackView(arrangedSubviews: [naverButton, kakaoButton, tmapButton]) + let appStack = UIStackView(arrangedSubviews: [naverButton, kakaoButton, appleButton]) appStack.axis = .horizontal appStack.alignment = .center appStack.spacing = 16 @@ -291,7 +328,7 @@ final class MapGuideViewController: UIViewController, View { appStack.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() - [naverButton, kakaoButton, tmapButton].forEach { button in + [naverButton, kakaoButton, appleButton].forEach { button in button.snp.makeConstraints { make in make.size.equalTo(CGSize(width: 48, height: 48)) } @@ -300,7 +337,9 @@ final class MapGuideViewController: UIViewController, View { } private func presentModalCard() { + // 백그라운드가 보이도록 처음부터 alpha를 1로 설정 self.dimmingView.alpha = 1 + UIView.animate( withDuration: 0.3, delay: 0, @@ -335,7 +374,6 @@ final class MapGuideViewController: UIViewController, View { cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mapView.moveCamera(cameraUpdate) - } private func dismissModalCard() { diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index c08ca70d..ac292485 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -175,7 +175,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 지도 이동 완료 감지 mainView.mapView.addCameraDelegate(delegate: self) - // idleAtPosition 대체 - 지도 이동 완료 시 idleSubject .observe(on: MainScheduler.instance) .subscribe(onNext: { [weak self] in @@ -183,7 +182,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let marker = self.currentMarker, let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { - // 툴팁이 없으면 생성, 있으면 위치 업데이트 if self.currentTooltipView == nil { self.configureTooltip(for: marker, stores: storeArray) } else { @@ -208,17 +206,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let tooltipView = MarkerTooltipView() tooltipView.configure(with: stores) - // 선택된 상태로 표시 - 첫 번째 정보를 기본 선택 상태로 만듦 tooltipView.selectStore(at: 0) - // onStoreSelected 클로저 설정 tooltipView.onStoreSelected = { [weak self] index in guard let self = self, index < stores.count else { return } self.currentCarouselStores = stores self.carouselView.updateCards(stores) self.carouselView.scrollToCard(index: index) - // 선택된 상태로 업데이트 self.updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: stores.count) tooltipView.selectStore(at: index) @@ -229,9 +224,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM """, category: .debug) } - // 툴팁 위치 설정 (마커 위치 기준으로 계산) let markerPoint = self.mainView.mapView.projection.point(from: marker.position) - let markerHeight: CGFloat = 32 // 마커 이미지 높이 추정값 + let markerHeight: CGFloat = 32 tooltipView.frame = CGRect( x: markerPoint.x, @@ -1082,34 +1076,25 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM present(alert, animated: true, completion: nil) } - // 캐러셀 영역을 제외한 실제 가시 영역 계산 함수 private func getEffectiveViewport() -> NMGLatLngBounds { - // 기본 가시 영역 가져오기 let bounds = getVisibleBounds() - // 캐러셀이 보이지 않으면 전체 영역 반환 if carouselView.isHidden { return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast) } - // 캐러셀 상단 Y 좌표 let carouselTopY = carouselView.frame.minY - - // 화면 좌표계에서 캐러셀 상단 라인을 생성 (좌우 전체 폭) let leftPoint = CGPoint(x: 0, y: carouselTopY) let rightPoint = CGPoint(x: view.frame.width, y: carouselTopY) - // 화면 좌표를 지도 좌표로 변환 let leftCoordinate = mainView.mapView.projection.latlng(from: leftPoint) let rightCoordinate = mainView.mapView.projection.latlng(from: rightPoint) - // 캐러셀 영역을 제외한 경계 계산 let adjustedSouthWest = NMGLatLng( lat: max(leftCoordinate.lat, rightCoordinate.lat), lng: bounds.southWest.lng ) - // 조정된 경계로 새 영역 생성 return NMGLatLngBounds( southWest: adjustedSouthWest, northEast: bounds.northEast From 6498cf0fb66cffadf37067993adf759fa3e5b3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 2 Apr 2025 02:40:41 +0900 Subject: [PATCH 030/393] =?UTF-8?q?fix=20#95:=20=EC=9E=94=EC=A1=B4?= =?UTF-8?q?=ED=95=B4=EC=9E=88=EB=8D=98=20CoreLocation=20=EC=B0=B8=EC=A1=B0?= =?UTF-8?q?=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Admin/Data/MapDomain/MapPopUpStore.swift | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift index ad5875c8..02384864 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift @@ -1,5 +1,4 @@ import Foundation -import CoreLocation import NMapsMap struct MapPopUpStore: Equatable { @@ -16,9 +15,6 @@ struct MapPopUpStore: Equatable { let markerSnippet: String let mainImageUrl: String? - var coordinate: CLLocationCoordinate2D { - CLLocationCoordinate2D(latitude: latitude, longitude: longitude) - } var nmgCoordinate: NMGLatLng { NMGLatLng(lat: latitude, lng: longitude) From c7929cc4b310ae41e67a1cec842114379e6ca9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 2 Apr 2025 15:53:32 +0900 Subject: [PATCH 031/393] =?UTF-8?q?fix/#95:=20=EB=A7=B5=EA=B0=80=EC=9D=B4?= =?UTF-8?q?=EB=93=9C=EB=B7=B0=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC=20?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FilterBottomSheetView.swift | 72 ++- .../FilterBottomSheetViewController.swift | 2 +- .../MapGuideView/MapGuideViewController.swift | 512 ++++++++++-------- .../Presentation/Map/MapView/MapMarker.swift | 2 - 4 files changed, 317 insertions(+), 271 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index c233ea4b..d465e412 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -200,7 +200,7 @@ final class FilterBottomSheetView: UIView { func setupLocationScrollView(locations: [Location], buttonAction: @escaping (Int, UIButton) -> Void) { locationContentView.subviews.forEach { $0.removeFromSuperview() } - locationScrollView.delegate = self + locationScrollView.delegate = self as? UIScrollViewDelegate var lastButton: UIButton? @@ -208,10 +208,14 @@ final class FilterBottomSheetView: UIView { let button = createStyledButton(title: location.main) button.tag = index - button.addAction(UIAction { _ in + button.addTarget(self, action: #selector(locationButtonTapped(_:)), for: .touchUpInside) + + // actionHandler 클로저 저장 + button.layer.setValue(index, forKey: "buttonIndex") + objc_setAssociatedObject(button, &AssociatedKeys.actionHandler, { [weak self] in buttonAction(index, button) - self.updateMainLocationSelection(index) - }, for: .touchUpInside) + self?.updateMainLocationSelection(index) + }, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) locationContentView.addSubview(button) @@ -234,6 +238,16 @@ final class FilterBottomSheetView: UIView { } } + private struct AssociatedKeys { + static var actionHandler = "actionHandler" + } + + @objc private func locationButtonTapped(_ sender: UIButton) { + if let actionHandler = objc_getAssociatedObject(sender, &AssociatedKeys.actionHandler) as? () -> Void { + actionHandler() + } + } + func updateCategoryButtonSelection(_ category: String) { categoryCollectionView.subviews.forEach { subview in if let stackView = subview as? UIStackView { @@ -256,6 +270,19 @@ final class FilterBottomSheetView: UIView { } } + func updateContentVisibility(isCategorySelected: Bool) { + self.locationScrollView.isHidden = isCategorySelected + self.balloonBackgroundView.isHidden = isCategorySelected + self.categoryCollectionView.isHidden = !isCategorySelected + + self.locationScrollView.alpha = isCategorySelected ? 0 : 1 + self.balloonBackgroundView.alpha = isCategorySelected ? 0 : 1 + self.categoryCollectionView.alpha = isCategorySelected ? 1 : 0 + + let newHeight = isCategorySelected ? 170 : self.balloonBackgroundView.calculateHeight() + self.balloonHeightConstraint?.update(offset: newHeight) + } + private func createCategoryButton(title: String, isSelected: Bool) -> UIButton { let button = UIButton(type: .system) button.setTitle(title, for: .normal) @@ -278,21 +305,6 @@ final class FilterBottomSheetView: UIView { return button } - func updateContentVisibility(isCategorySelected: Bool) { - locationScrollView.isHidden = isCategorySelected - balloonBackgroundView.isHidden = isCategorySelected - categoryCollectionView.isHidden = !isCategorySelected - - locationScrollView.alpha = isCategorySelected ? 0 : 1 - balloonBackgroundView.alpha = isCategorySelected ? 0 : 1 - categoryCollectionView.alpha = isCategorySelected ? 1 : 0 - - let newHeight = isCategorySelected ? 170 : self.balloonBackgroundView.calculateHeight() - balloonHeightConstraint?.update(offset: newHeight) - - self.layoutIfNeeded() - } - private func createStyledButton(title: String, isSelected: Bool = false) -> PPButton { let button = PPButton( style: .secondary, @@ -340,7 +352,7 @@ final class FilterBottomSheetView: UIView { balloonHeightConstraint?.update(offset: isHidden ? 0 : dynamicHeight) self.layoutIfNeeded() } - + func updateBalloonPosition(for button: UIButton) { DispatchQueue.main.async { guard let window = button.window else { return } @@ -359,14 +371,25 @@ final class FilterBottomSheetView: UIView { self.balloonBackgroundView.setNeedsDisplay() } } + private func updateBalloonPositionAccurately(for button: PPButton) { let buttonFrameInBalloon = button.convert(button.bounds, to: balloonBackgroundView) let arrowPosition = buttonFrameInBalloon.midX / balloonBackgroundView.bounds.width balloonBackgroundView.arrowPosition = arrowPosition balloonBackgroundView.setNeedsDisplay() } + + private func updateSelectedButtonPosition() { + guard let selectedButton = locationContentView.subviews.first(where: { view in + guard let button = view as? PPButton else { return false } + return button.backgroundColor == .blu500 + }) as? PPButton else { return } + + updateBalloonPosition(for: selectedButton) + } } +// MARK: - Extensions extension FilterBottomSheetView { func update(locationText: String?, categoryText: String?) { var filters: [String] = [] @@ -396,13 +419,4 @@ extension FilterBottomSheetView: UIScrollViewDelegate { updateSelectedButtonPosition() } } - - private func updateSelectedButtonPosition() { - guard let selectedButton = locationContentView.subviews.first(where: { view in - guard let button = view as? PPButton else { return false } - return button.backgroundColor == .blu500 - }) as? PPButton else { return } - - updateBalloonPosition(for: selectedButton) - } } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift index 828141f5..d0c196d4 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift @@ -44,7 +44,7 @@ final class FilterBottomSheetViewController: UIViewController, View { setupLayout() setupGestures() setupCollectionView() - containerView.isUserInteractionEnabled = true + containerView.filterChipsView.onRemoveChip = { [weak self] removedOption in guard let self = self, let reactor = self.reactor else { return } diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index 3776cebe..c7b71cb4 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -6,310 +6,222 @@ import CoreLocation import NMapsMap final class MapGuideViewController: UIViewController, View { + + // MARK: - Constants + private enum Constant { + // Modal Card + static let modalCardHeight: CGFloat = 408 + static let modalCardInitialBottomOffset: CGFloat = 408 + static let modalCardAnimationDuration: TimeInterval = 0.3 + static let modalCardAnimationDamping: CGFloat = 0.8 + static let modalCardAnimationInitialVelocity: CGFloat = 0.5 + + // Top Container + static let topContainerTopOffset: CGFloat = 20 + static let topContainerHeight: CGFloat = 44 + + // Title Label + static let titleLabelLeadingOffset: CGFloat = 20 + + // Close Button + static let closeButtonTrailingInset: CGFloat = 16 + static let closeButtonSize: CGFloat = 24 + + // MapView + static let mapViewTopOffset: CGFloat = 20 + static let mapViewHorizontalInset: CGFloat = 20 + static let mapViewHeight: CGFloat = 240 + static let mapZoomLevel: Double = 15.0 + + // Expand Button + static let expandButtonBottomOffset: CGFloat = -10 + static let expandButtonTrailingOffset: CGFloat = -10 + static let expandButtonSize: CGFloat = 32 + + // Bottom Container + static let bottomContainerTopOffset: CGFloat = 20 + static let bottomContainerHorizontalInset: CGFloat = 20 + static let bottomContainerHeight: CGFloat = 44 + static let bottomContainerBottomInset: CGFloat = 60 + + // Throttle + static let expandButtonThrottleMilliseconds: Int = 300 + } + // MARK: - Properties var disposeBag = DisposeBag() private let popUpStoreId: Int64 private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 - - init(popUpStoreId: Int64) { - self.popUpStoreId = popUpStoreId - super.init(nibName: nil, bundle: nil) - modalPresentationStyle = .overFullScreen // 모달 스타일 설정 - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - + // MARK: - UI Components private let dimmingView: UIView = { - let v = UIView() - v.backgroundColor = UIColor.gray.withAlphaComponent(0.7) // 더 진한 딤드 효과 - v.alpha = 0 - return v + let viewInstance = UIView() + viewInstance.backgroundColor = UIColor.gray.withAlphaComponent(0.7) + viewInstance.alpha = 0 + return viewInstance }() private let modalCardView: UIView = { - let v = UIView() - v.backgroundColor = .white - v.layer.cornerRadius = 16 - v.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - v.layer.shadowColor = UIColor.black.cgColor - v.layer.shadowOpacity = 0.1 - v.layer.shadowOffset = .zero - v.layer.shadowRadius = 8 - return v + let viewInstance = UIView() + viewInstance.backgroundColor = .white + viewInstance.layer.cornerRadius = 16 + viewInstance.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + viewInstance.layer.shadowColor = UIColor.black.cgColor + viewInstance.layer.shadowOpacity = 0.1 + viewInstance.layer.shadowOffset = .zero + viewInstance.layer.shadowRadius = 8 + return viewInstance }() private let titleLabel: UILabel = { - let lb = UILabel() - lb.text = "찾아가는 길" - lb.font = UIFont.boldSystemFont(ofSize: 17) - lb.textColor = .black - return lb + let label = UILabel() + label.text = "찾아가는 길" + label.font = UIFont.boldSystemFont(ofSize: 17) + label.textColor = .black + return label }() private let closeButton: UIButton = { - let btn = UIButton(type: .system) + let button = UIButton(type: .system) let image = UIImage(named: "icon_xmark")?.withRenderingMode(.alwaysOriginal) - btn.setImage(image, for: .normal) - return btn + button.setImage(image, for: .normal) + return button }() private let mapView: NMFMapView = { - let map = NMFMapView() - map.layer.borderWidth = 1 - map.layer.borderColor = UIColor.g100.cgColor - map.layer.cornerRadius = 12 - return map + let mapViewInstance = NMFMapView() + mapViewInstance.layer.borderWidth = 1 + mapViewInstance.layer.borderColor = UIColor.g100.cgColor + mapViewInstance.layer.cornerRadius = 12 + return mapViewInstance }() private let expandButton: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "Expandable"), for: .normal) - btn.backgroundColor = UIColor.white - btn.layer.cornerRadius = 16 - btn.clipsToBounds = true - return btn + let button = UIButton() + button.setImage(UIImage(named: "Expandable"), for: .normal) + button.backgroundColor = UIColor.white + button.layer.cornerRadius = 16 + button.clipsToBounds = true + return button }() private let promptLabel: UILabel = { - let lb = UILabel() - lb.text = "지도 앱으로\n바로 찾아볼까요?" - lb.font = UIFont.systemFont(ofSize: 15, weight: .medium) - lb.textColor = .darkGray - lb.numberOfLines = 2 - return lb + let label = UILabel() + label.text = "지도 앱으로\n바로 찾아볼까요?" + label.font = UIFont.systemFont(ofSize: 15, weight: .medium) + label.textColor = .darkGray + label.numberOfLines = 2 + return label }() private let naverButton: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "naver"), for: .normal) - btn.layer.cornerRadius = 24 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.g100.cgColor - btn.clipsToBounds = true - return btn + let button = UIButton() + button.setImage(UIImage(named: "naver"), for: .normal) + button.layer.cornerRadius = 24 + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.g100.cgColor + button.clipsToBounds = true + return button }() private let kakaoButton: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "kakao"), for: .normal) - btn.layer.cornerRadius = 24 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.g100.cgColor - btn.clipsToBounds = true - return btn + let button = UIButton() + button.setImage(UIImage(named: "kakao"), for: .normal) + button.layer.cornerRadius = 24 + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.g100.cgColor + button.clipsToBounds = true + return button }() private let appleButton: UIButton = { - let btn = UIButton() - btn.setImage(UIImage(named: "AppleMap"), for: .normal) - btn.layer.cornerRadius = 24 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.g100.cgColor - btn.clipsToBounds = true - return btn + let button = UIButton() + button.setImage(UIImage(named: "AppleMap"), for: .normal) + button.layer.cornerRadius = 24 + button.layer.borderWidth = 1 + button.layer.borderColor = UIColor.g100.cgColor + button.clipsToBounds = true + return button }() private var modalCardBottomConstraint: Constraint? + // MARK: - Initializer + init(popUpStoreId: Int64) { + self.popUpStoreId = popUpStoreId + super.init(nibName: nil, bundle: nil) + modalPresentationStyle = .overFullScreen + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() setupUI() - setupTapGesture() // 탭 제스처 설정 추가 + setupTapGesture() presentModalCard() } - // 딤드 영역 탭 제스처 설정 - private func setupTapGesture() { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOnDimmingView)) - dimmingView.addGestureRecognizer(tapGesture) - dimmingView.isUserInteractionEnabled = true // 중요: 상호작용 활성화 - } - - // 딤드 영역 탭 처리 - @objc private func handleTapOnDimmingView(_ sender: UITapGestureRecognizer) { - // 탭 위치가 modalCardView 영역이 아닌 경우에만 닫기 - let location = sender.location(in: view) - if !modalCardView.frame.contains(location) { - dismissModalCard() - } - } - - func bind(reactor: MapGuideReactor) { - reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) - - // 닫기 버튼 - closeButton.rx.tap - .subscribe(onNext: { [weak self] in - self?.dismissModalCard() - }) - .disposed(by: disposeBag) - - // 지도 앱 열기 - naverButton.rx.tap - .map { Reactor.Action.openMapApp("naver") } - .bind(to: reactor.action) - .disposed(by: disposeBag) - kakaoButton.rx.tap - .map { Reactor.Action.openMapApp("kakao") } - .bind(to: reactor.action) - .disposed(by: disposeBag) - appleButton.rx.tap - .map { Reactor.Action.openMapApp("apple") } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - expandButton.rx.tap - .throttle(.milliseconds(300), scheduler: MainScheduler.instance) - .subscribe(onNext: { [weak self] in - guard let self = self else { return } - - let provider = ProviderImpl() - let useCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) - let directionRepository = DefaultMapDirectionRepository(provider: provider) - let reactor = MapReactor(useCase: useCase, directionRepository: directionRepository) - - if let selectedStore = self.currentCarouselStores.first { - reactor.action.onNext(.didSelectItem(selectedStore)) - - // 현재 맵에 표시된 마커 생성 또는 가져오기 - let marker = NMFMarker() - marker.position = NMGLatLng(lat: selectedStore.latitude, lng: selectedStore.longitude) - // 중요: 명시적으로 TapMarker 설정 - marker.iconImage = NMFOverlayImage(name: "TapMarker") - marker.width = 44 - marker.height = 44 - marker.anchor = CGPoint(x: 0.5, y: 1.0) - marker.userInfo = ["storeData": selectedStore] - - // 풀스크린 맵 뷰 컨트롤러에 선택된 마커 정보 전달 - let fullScreenMapViewController = FullScreenMapViewController(store: selectedStore, existingMarker: marker) - fullScreenMapViewController.reactor = reactor - - let nav = UINavigationController(rootViewController: fullScreenMapViewController) - nav.modalPresentationStyle = .fullScreen - self.present(nav, animated: true) - } else { - reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) - - reactor.state - .map { $0.searchResult } - .distinctUntilChanged() - .compactMap { $0 } - .take(1) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { store in - // 여기서도 동일하게 마커 생성 및 설정 - let marker = NMFMarker() - marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) - marker.iconImage = NMFOverlayImage(name: "TapMarker") - marker.width = 44 - marker.height = 44 - marker.anchor = CGPoint(x: 0.5, y: 1.0) - marker.userInfo = ["storeData": store] - - let fullScreenMapViewController = FullScreenMapViewController(store: store, existingMarker: marker) - fullScreenMapViewController.reactor = reactor - - let nav = UINavigationController(rootViewController: fullScreenMapViewController) - nav.modalPresentationStyle = .fullScreen - self.present(nav, animated: true) - }) - .disposed(by: self.disposeBag) - } - }) - .disposed(by: disposeBag) - - - - // 목적지 좌표로 마커 및 카메라 설정 - reactor.state - .map { $0.destinationCoordinate } - .compactMap { $0 } - .subscribe(onNext: { [weak self] coordinate in - self?.setupMarker(at: coordinate) - }) - .disposed(by: disposeBag) - - // searchResult로 currentCarouselStores 업데이트 - reactor.state - .map { $0.searchResult } - .distinctUntilChanged() - .compactMap { $0 } - .subscribe(onNext: { [weak self] store in - self?.currentCarouselStores = [store] - }) - .disposed(by: disposeBag) - - // Dismiss 처리 - reactor.state - .map { $0.shouldDismiss } - .distinctUntilChanged() - .filter { $0 } - .subscribe(onNext: { [weak self] _ in - self?.dismissModalCard() - }) - .disposed(by: disposeBag) - } - // MARK: - UI Setup private func setupUI() { - view.backgroundColor = .clear // 배경색을 clear로 변경하여 항상 딤드 뷰가 보이도록 함 + view.backgroundColor = .clear view.addSubview(dimmingView) - dimmingView.snp.makeConstraints { $0.edges.equalToSuperview() } + dimmingView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } view.addSubview(modalCardView) modalCardView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() - make.height.equalTo(408) - self.modalCardBottomConstraint = make.bottom.equalToSuperview().offset(408).constraint + make.height.equalTo(Constant.modalCardHeight) + self.modalCardBottomConstraint = make.bottom.equalToSuperview().offset(Constant.modalCardInitialBottomOffset).constraint } let topContainer = UIView() modalCardView.addSubview(topContainer) topContainer.snp.makeConstraints { make in - make.top.equalToSuperview().offset(20) + make.top.equalToSuperview().offset(Constant.topContainerTopOffset) make.leading.trailing.equalToSuperview() - make.height.equalTo(44) + make.height.equalTo(Constant.topContainerHeight) } topContainer.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.leading.equalToSuperview().offset(20) + make.leading.equalToSuperview().offset(Constant.titleLabelLeadingOffset) } topContainer.addSubview(closeButton) closeButton.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(16) - make.width.height.equalTo(24) + make.trailing.equalToSuperview().inset(Constant.closeButtonTrailingInset) + make.width.height.equalTo(Constant.closeButtonSize) } modalCardView.addSubview(mapView) mapView.snp.makeConstraints { make in - make.top.equalTo(topContainer.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(240) // 약간 줄임 + make.top.equalTo(topContainer.snp.bottom).offset(Constant.mapViewTopOffset) + make.leading.trailing.equalToSuperview().inset(Constant.mapViewHorizontalInset) + make.height.equalTo(Constant.mapViewHeight) } modalCardView.addSubview(expandButton) expandButton.snp.makeConstraints { make in - make.bottom.equalTo(mapView.snp.bottom).offset(-10) - make.trailing.equalTo(mapView.snp.trailing).offset(-10) - make.width.height.equalTo(32) + make.bottom.equalTo(mapView.snp.bottom).offset(Constant.expandButtonBottomOffset) + make.trailing.equalTo(mapView.snp.trailing).offset(Constant.expandButtonTrailingOffset) + make.width.height.equalTo(Constant.expandButtonSize) } let bottomContainer = UIView() modalCardView.addSubview(bottomContainer) bottomContainer.snp.makeConstraints { make in - make.top.equalTo(mapView.snp.bottom).offset(20) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(44) - make.bottom.equalTo(modalCardView.snp.bottom).inset(60) + make.top.equalTo(mapView.snp.bottom).offset(Constant.bottomContainerTopOffset) + make.leading.trailing.equalToSuperview().inset(Constant.bottomContainerHorizontalInset) + make.height.equalTo(Constant.bottomContainerHeight) + make.bottom.equalTo(modalCardView.snp.bottom).inset(Constant.bottomContainerBottomInset) } bottomContainer.addSubview(promptLabel) @@ -336,15 +248,19 @@ final class MapGuideViewController: UIViewController, View { } } + private func setupTapGesture() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOnDimmingView)) + dimmingView.addGestureRecognizer(tapGesture) + dimmingView.isUserInteractionEnabled = true + } + private func presentModalCard() { - // 백그라운드가 보이도록 처음부터 alpha를 1로 설정 self.dimmingView.alpha = 1 - UIView.animate( - withDuration: 0.3, + withDuration: Constant.modalCardAnimationDuration, delay: 0, - usingSpringWithDamping: 0.8, - initialSpringVelocity: 0.5, + usingSpringWithDamping: Constant.modalCardAnimationDamping, + initialSpringVelocity: Constant.modalCardAnimationInitialVelocity, options: .curveEaseOut ) { self.modalCardBottomConstraint?.update(offset: 0) @@ -353,36 +269,154 @@ final class MapGuideViewController: UIViewController, View { } private func setupMarker(at coordinate: CLLocationCoordinate2D) { - mapView.subviews.forEach { if $0 is NMFMarker { $0.removeFromSuperview() } } + // 기존 마커 제거 + self.mapView.subviews.forEach { subview in + if subview is NMFMarker { + subview.removeFromSuperview() + } + } - // 새 마커 생성 및 설정 let marker = NMFMarker() marker.position = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) marker.iconImage = NMFOverlayImage(name: "TapMarker") marker.width = 44 marker.height = 44 marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.mapView = self.mapView - // 먼저 마커를 지도에 추가 - marker.mapView = mapView - - // 그 다음 카메라 위치 설정 let cameraUpdate = NMFCameraUpdate( scrollTo: NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude), - zoomTo: 15.0 + zoomTo: Constant.mapZoomLevel ) cameraUpdate.animation = .easeIn - cameraUpdate.animationDuration = 0.3 - mapView.moveCamera(cameraUpdate) + cameraUpdate.animationDuration = Constant.modalCardAnimationDuration + self.mapView.moveCamera(cameraUpdate) } private func dismissModalCard() { - UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn) { + UIView.animate(withDuration: Constant.modalCardAnimationDuration, delay: 0, options: .curveEaseIn) { self.dimmingView.alpha = 0 - self.modalCardBottomConstraint?.update(offset: 408) + self.modalCardBottomConstraint?.update(offset: Constant.modalCardInitialBottomOffset) self.view.layoutIfNeeded() } completion: { _ in self.dismiss(animated: false) } } + + @objc private func handleTapOnDimmingView(_ sender: UITapGestureRecognizer) { + let location = sender.location(in: self.view) + if !modalCardView.frame.contains(location) { + dismissModalCard() + } + } +} + +// MARK: - ReactorKit Binding +extension MapGuideViewController { + func bind(reactor: MapGuideReactor) { + reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) + + closeButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.dismissModalCard() + }) + .disposed(by: self.disposeBag) + + naverButton.rx.tap + .map { Reactor.Action.openMapApp("naver") } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + kakaoButton.rx.tap + .map { Reactor.Action.openMapApp("kakao") } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + appleButton.rx.tap + .map { Reactor.Action.openMapApp("apple") } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + expandButton.rx.tap + .throttle(.milliseconds(Constant.expandButtonThrottleMilliseconds), scheduler: MainScheduler.instance) + .subscribe(onNext: { [weak self] in + guard let self = self else { return } + let provider = ProviderImpl() + let useCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) + let directionRepository = DefaultMapDirectionRepository(provider: provider) + let reactorInstance = MapReactor(useCase: useCase, directionRepository: directionRepository) + + if let selectedStore = self.currentCarouselStores.first { + reactorInstance.action.onNext(.didSelectItem(selectedStore)) + + let marker = NMFMarker() + marker.position = NMGLatLng(lat: selectedStore.latitude, lng: selectedStore.longitude) + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.userInfo = ["storeData": selectedStore] + + let fullScreenMapViewController = FullScreenMapViewController(store: selectedStore, existingMarker: marker) + fullScreenMapViewController.reactor = reactorInstance + + let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) + navigationController.modalPresentationStyle = .fullScreen + self.present(navigationController, animated: true) + } else { + reactorInstance.action.onNext(.viewDidLoad(self.popUpStoreId)) + + reactorInstance.state + .map { $0.searchResult } + .distinctUntilChanged() + .compactMap { $0 } + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { store in + let marker = NMFMarker() + marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + marker.iconImage = NMFOverlayImage(name: "TapMarker") + marker.width = 44 + marker.height = 44 + marker.anchor = CGPoint(x: 0.5, y: 1.0) + marker.userInfo = ["storeData": store] + + let fullScreenMapViewController = FullScreenMapViewController(store: store, existingMarker: marker) + fullScreenMapViewController.reactor = reactorInstance + + let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) + navigationController.modalPresentationStyle = .fullScreen + self.present(navigationController, animated: true) + }) + .disposed(by: self.disposeBag) + } + }) + .disposed(by: self.disposeBag) + + reactor.state + .map { $0.destinationCoordinate } + .compactMap { $0 } + .subscribe(onNext: { [weak self] coordinate in + self?.setupMarker(at: coordinate) + }) + .disposed(by: self.disposeBag) + + reactor.state + .map { $0.searchResult } + .distinctUntilChanged() + .compactMap { $0 } + .subscribe(onNext: { [weak self] store in + self?.currentCarouselStores = [store] + }) + .disposed(by: self.disposeBag) + + reactor.state + .map { $0.shouldDismiss } + .distinctUntilChanged() + .filter { $0 } + .subscribe(onNext: { [weak self] _ in + self?.dismissModalCard() + }) + .disposed(by: self.disposeBag) + } } diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift index f15cc71d..9ddfb0e1 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift @@ -221,9 +221,7 @@ extension MapMarker { return markerImageView } - /// 네이버맵용으로 뷰를 UIImage로 렌더링하는 함수 func asImage() -> UIImage? { - // 필요한 경우 프레임을 강제로 업데이트합니다. self.layoutIfNeeded() UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } From 03eb7102e05b489fc13fc2f4be8d958521272697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 2 Apr 2025 15:53:53 +0900 Subject: [PATCH 032/393] =?UTF-8?q?feat/#95=20NMFMapViewDelegateProxy=20?= =?UTF-8?q?=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Map/Common/NMFMapViewDelegateProxy.swift | 40 ++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift index d5ef360c..dadff963 100644 --- a/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift +++ b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift @@ -2,53 +2,89 @@ import NMapsMap import RxSwift import RxCocoa +/// NMFMapViewDelegateProxy는 NMFMapView의 delegate 이벤트를 RxSwift Observable로 변환하는 역할 class NMFMapViewDelegateProxy: DelegateProxy, DelegateProxyType, NMFMapViewDelegate { + // MARK: - Properties + + /// 연결된 NMFMapView 인스턴스 (약한 참조) public weak private(set) var mapView: NMFMapView? - // Rx 이벤트를 위한 subject 추가 + /// 카메라 위치 변경 이벤트를 전달하기 위한 Rx Subject let didChangePositionSubject = PublishSubject() + + /// 맵이 idle 상태가 되었을 때 이벤트를 전달하기 위한 Rx Subject let idleAtPositionSubject = PublishSubject() + // MARK: - Initializer + + /// NMFMapViewDelegateProxy 초기화 메서드 + /// - Parameter mapView: 이벤트를 받아올 NMFMapView 인스턴스 init(mapView: NMFMapView) { self.mapView = mapView super.init(parentObject: mapView, delegateProxy: NMFMapViewDelegateProxy.self) } + // MARK: - DelegateProxyType Implementation + + /// Rx에서 사용하기 위한 구현 등록 static func registerKnownImplementations() { self.register { NMFMapViewDelegateProxy(mapView: $0) } } + /// 지정된 NMFMapView의 현재 delegate를 반환 + /// - Parameter object: NMFMapView 인스턴스 + /// - Returns: 해당 mapView의 delegate static func currentDelegate(for object: NMFMapView) -> NMFMapViewDelegate? { return object.delegate } + /// 지정된 NMFMapView에 delegate를 설정 + /// - Parameters: + /// - delegate: 설정할 delegate + /// - object: NMFMapView 인스턴스 static func setCurrentDelegate(_ delegate: NMFMapViewDelegate?, to object: NMFMapView) { object.delegate = delegate } - // 네이버맵의 Delegate 메서드를 Rx로 전달 + // MARK: - NMFMapViewDelegate Methods + + /// 카메라 위치가 변경될 때 호출되는 메서드. + /// - Parameters: + /// - mapView: 이벤트가 발생한 NMFMapView + /// - reason: 카메라 변경 사유 + /// - animated: 애니메이션 여부 func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) { didChangePositionSubject.onNext(()) + // 기존 delegate로 이벤트 전달 (옵셔널 체이닝) _forwardToDelegate?.mapView?(mapView, cameraDidChangeByReason: reason, animated: animated) } + /// 맵뷰가 idle 상태가 되었을 때 호출되는 메서드. + /// Rx Subject를 통해 idle 이벤트를 전달하고, 기존 delegate에게 까지 + /// - Parameter mapView: idle 상태가 된 NMFMapView func mapViewIdle(_ mapView: NMFMapView) { idleAtPositionSubject.onNext(()) + // 기존 delegate로 idle 이벤트 전달 forwardToDelegate()?.mapViewIdle?(mapView) } } +/// NMFMapView의 Reactive 확장 extension Reactive where Base: NMFMapView { + + /// NMFMapViewDelegateProxy를 반환하여 delegate 이벤트를 처리할 수 있도록 var delegate: DelegateProxy { return NMFMapViewDelegateProxy.proxy(for: base) } + /// mapView의 카메라 위치 변경 이벤트를 Observable로 var didChangePosition: Observable { let proxy = NMFMapViewDelegateProxy.proxy(for: base) return proxy.didChangePositionSubject.asObservable() } + /// mapView가 idle 상태가 되었을 때의 이벤트를 Observable로 var idleAtPosition: Observable { let proxy = NMFMapViewDelegateProxy.proxy(for: base) return proxy.idleAtPositionSubject.asObservable() From a4d4d941b9d87960f1234bf96d7a4c70bf5a449a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 2 Apr 2025 15:54:23 +0900 Subject: [PATCH 033/393] =?UTF-8?q?fix/#95=20:=20=EB=A7=B5=EA=B0=80?= =?UTF-8?q?=EC=9D=B4=EB=93=9C=EB=B7=B0=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC?= =?UTF-8?q?=20=EC=B6=95=EC=95=BD=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Map/Common/ClusteringModels.swift | 24 +- .../MapGuideView/MapGuideViewController.swift | 502 +++++++++--------- 2 files changed, 247 insertions(+), 279 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift index 0f218e3c..b0f80b7d 100644 --- a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift +++ b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift @@ -7,22 +7,18 @@ enum MapZoomLevel { case detailed static func getLevel(from zoom: Float) -> MapZoomLevel { - let level: MapZoomLevel - switch zoom { - case ..<7: - level = .country - case 7..<10: - level = .city - case 10..<11: - level = .district - default: - level = .detailed - } - Logger.log(message: "줌 레벨 계산: \(zoom) -> \(level)", category: .debug) - return level + switch zoom { + case ..<7: + return .country + case 7..<10: + return .city + case 10..<11: + return .district + default: + return .detailed } } - +} struct RegionCluster { let name: String diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index c7b71cb4..4810452c 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -6,57 +6,16 @@ import CoreLocation import NMapsMap final class MapGuideViewController: UIViewController, View { - - // MARK: - Constants - private enum Constant { - // Modal Card - static let modalCardHeight: CGFloat = 408 - static let modalCardInitialBottomOffset: CGFloat = 408 - static let modalCardAnimationDuration: TimeInterval = 0.3 - static let modalCardAnimationDamping: CGFloat = 0.8 - static let modalCardAnimationInitialVelocity: CGFloat = 0.5 - - // Top Container - static let topContainerTopOffset: CGFloat = 20 - static let topContainerHeight: CGFloat = 44 - - // Title Label - static let titleLabelLeadingOffset: CGFloat = 20 - - // Close Button - static let closeButtonTrailingInset: CGFloat = 16 - static let closeButtonSize: CGFloat = 24 - - // MapView - static let mapViewTopOffset: CGFloat = 20 - static let mapViewHorizontalInset: CGFloat = 20 - static let mapViewHeight: CGFloat = 240 - static let mapZoomLevel: Double = 15.0 - - // Expand Button - static let expandButtonBottomOffset: CGFloat = -10 - static let expandButtonTrailingOffset: CGFloat = -10 - static let expandButtonSize: CGFloat = 32 - - // Bottom Container - static let bottomContainerTopOffset: CGFloat = 20 - static let bottomContainerHorizontalInset: CGFloat = 20 - static let bottomContainerHeight: CGFloat = 44 - static let bottomContainerBottomInset: CGFloat = 60 - - // Throttle - static let expandButtonThrottleMilliseconds: Int = 300 - } - // MARK: - Properties var disposeBag = DisposeBag() - private let popUpStoreId: Int64 - private var currentCarouselStores: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 + private let popupStoreIdentifier: Int64 + private var currentCarouselStoreList: [MapPopUpStore] = [] // 현재 선택된 스토어 목록 // MARK: - UI Components + private let dimmingView: UIView = { let viewInstance = UIView() - viewInstance.backgroundColor = UIColor.gray.withAlphaComponent(0.7) + viewInstance.backgroundColor = UIColor.gray.withAlphaComponent(0.7) viewInstance.alpha = 0 return viewInstance }() @@ -74,18 +33,18 @@ final class MapGuideViewController: UIViewController, View { }() private let titleLabel: UILabel = { - let label = UILabel() - label.text = "찾아가는 길" - label.font = UIFont.boldSystemFont(ofSize: 17) - label.textColor = .black - return label + let labelInstance = UILabel() + labelInstance.text = "찾아가는 길" + labelInstance.font = UIFont.boldSystemFont(ofSize: 17) + labelInstance.textColor = .black + return labelInstance }() private let closeButton: UIButton = { - let button = UIButton(type: .system) + let buttonInstance = UIButton(type: .system) let image = UIImage(named: "icon_xmark")?.withRenderingMode(.alwaysOriginal) - button.setImage(image, for: .normal) - return button + buttonInstance.setImage(image, for: .normal) + return buttonInstance }() private let mapView: NMFMapView = { @@ -97,60 +56,60 @@ final class MapGuideViewController: UIViewController, View { }() private let expandButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "Expandable"), for: .normal) - button.backgroundColor = UIColor.white - button.layer.cornerRadius = 16 - button.clipsToBounds = true - return button + let buttonInstance = UIButton() + buttonInstance.setImage(UIImage(named: "Expandable"), for: .normal) + buttonInstance.backgroundColor = UIColor.white + buttonInstance.layer.cornerRadius = 16 + buttonInstance.clipsToBounds = true + return buttonInstance }() private let promptLabel: UILabel = { - let label = UILabel() - label.text = "지도 앱으로\n바로 찾아볼까요?" - label.font = UIFont.systemFont(ofSize: 15, weight: .medium) - label.textColor = .darkGray - label.numberOfLines = 2 - return label + let labelInstance = UILabel() + labelInstance.text = "지도 앱으로\n바로 찾아볼까요?" + labelInstance.font = UIFont.systemFont(ofSize: 15, weight: .medium) + labelInstance.textColor = .darkGray + labelInstance.numberOfLines = 2 + return labelInstance }() private let naverButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "naver"), for: .normal) - button.layer.cornerRadius = 24 - button.layer.borderWidth = 1 - button.layer.borderColor = UIColor.g100.cgColor - button.clipsToBounds = true - return button + let buttonInstance = UIButton() + buttonInstance.setImage(UIImage(named: "naver"), for: .normal) + buttonInstance.layer.cornerRadius = 24 + buttonInstance.layer.borderWidth = 1 + buttonInstance.layer.borderColor = UIColor.g100.cgColor + buttonInstance.clipsToBounds = true + return buttonInstance }() private let kakaoButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "kakao"), for: .normal) - button.layer.cornerRadius = 24 - button.layer.borderWidth = 1 - button.layer.borderColor = UIColor.g100.cgColor - button.clipsToBounds = true - return button + let buttonInstance = UIButton() + buttonInstance.setImage(UIImage(named: "kakao"), for: .normal) + buttonInstance.layer.cornerRadius = 24 + buttonInstance.layer.borderWidth = 1 + buttonInstance.layer.borderColor = UIColor.g100.cgColor + buttonInstance.clipsToBounds = true + return buttonInstance }() private let appleButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "AppleMap"), for: .normal) - button.layer.cornerRadius = 24 - button.layer.borderWidth = 1 - button.layer.borderColor = UIColor.g100.cgColor - button.clipsToBounds = true - return button + let buttonInstance = UIButton() + buttonInstance.setImage(UIImage(named: "AppleMap"), for: .normal) + buttonInstance.layer.cornerRadius = 24 + buttonInstance.layer.borderWidth = 1 + buttonInstance.layer.borderColor = UIColor.g100.cgColor + buttonInstance.clipsToBounds = true + return buttonInstance }() private var modalCardBottomConstraint: Constraint? // MARK: - Initializer init(popUpStoreId: Int64) { - self.popUpStoreId = popUpStoreId + self.popupStoreIdentifier = popUpStoreId super.init(nibName: nil, bundle: nil) - modalPresentationStyle = .overFullScreen + modalPresentationStyle = .overFullScreen // 모달 스타일 설정 } required init?(coder: NSCoder) { @@ -161,13 +120,145 @@ final class MapGuideViewController: UIViewController, View { override func viewDidLoad() { super.viewDidLoad() setupUI() - setupTapGesture() + setupTapGesture() // 탭 제스처 설정 추가 presentModalCard() } + // MARK: - Gesture Setup + /// 딤드 영역 탭 제스처 설정 + private func setupTapGesture() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOnDimmingView)) + dimmingView.addGestureRecognizer(tapGesture) + dimmingView.isUserInteractionEnabled = true // 중요: 상호작용 활성화 + } + + /// 딤드 영역 탭 처리: 탭 위치가 모달 카드 영역이 아닌 경우에만 닫기 + @objc private func handleTapOnDimmingView(_ sender: UITapGestureRecognizer) { + let tapLocation = sender.location(in: view) + if !modalCardView.frame.contains(tapLocation) { + dismissModalCard() + } + } + + // MARK: - ReactorKit Binding + func bind(reactor: MapGuideReactor) { + reactor.action.onNext(.viewDidLoad(self.popupStoreIdentifier)) + + // 닫기 버튼 + closeButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.dismissModalCard() + }) + .disposed(by: disposeBag) + + // 지도 앱 열기 + naverButton.rx.tap + .map { Reactor.Action.openMapApp("naver") } + .bind(to: reactor.action) + .disposed(by: disposeBag) + kakaoButton.rx.tap + .map { Reactor.Action.openMapApp("kakao") } + .bind(to: reactor.action) + .disposed(by: disposeBag) + appleButton.rx.tap + .map { Reactor.Action.openMapApp("apple") } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + // 확장 버튼 탭 처리 및 지도 풀스크린 전환 + expandButton.rx.tap + .throttle(.milliseconds(300), scheduler: MainScheduler.instance) + .subscribe(onNext: { [weak self] in + guard let strongSelf = self else { return } + + let providerInstance = ProviderImpl() + let repositoryInstance = DefaultMapRepository(provider: providerInstance) + let useCaseInstance = DefaultMapUseCase(repository: repositoryInstance) + let directionRepositoryInstance = DefaultMapDirectionRepository(provider: providerInstance) + let mapReactorInstance = MapReactor(useCase: useCaseInstance, directionRepository: directionRepositoryInstance) + + if let selectedStore = strongSelf.currentCarouselStoreList.first { + mapReactorInstance.action.onNext(.didSelectItem(selectedStore)) + + // 현재 지도에 표시된 마커 생성 또는 가져오기 + let markerInstance = NMFMarker() + markerInstance.position = NMGLatLng(lat: selectedStore.latitude, lng: selectedStore.longitude) + markerInstance.iconImage = NMFOverlayImage(name: "TapMarker") + markerInstance.width = 44 + markerInstance.height = 44 + markerInstance.anchor = CGPoint(x: 0.5, y: 1.0) + markerInstance.userInfo = ["storeData": selectedStore] + + // 풀스크린 지도 뷰 컨트롤러에 선택된 마커 정보 전달 + let fullScreenMapViewController = FullScreenMapViewController(store: selectedStore, existingMarker: markerInstance) + fullScreenMapViewController.reactor = mapReactorInstance + + let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) + navigationController.modalPresentationStyle = .fullScreen + strongSelf.present(navigationController, animated: true) + } else { + mapReactorInstance.action.onNext(.viewDidLoad(strongSelf.popupStoreIdentifier)) + + mapReactorInstance.state + .map { $0.searchResult } + .distinctUntilChanged() + .compactMap { $0 } + .take(1) + .observe(on: MainScheduler.instance) + .subscribe(onNext: { store in + let markerInstance = NMFMarker() + markerInstance.position = NMGLatLng(lat: store.latitude, lng: store.longitude) + markerInstance.iconImage = NMFOverlayImage(name: "TapMarker") + markerInstance.width = 44 + markerInstance.height = 44 + markerInstance.anchor = CGPoint(x: 0.5, y: 1.0) + markerInstance.userInfo = ["storeData": store] + + let fullScreenMapViewController = FullScreenMapViewController(store: store, existingMarker: markerInstance) + fullScreenMapViewController.reactor = mapReactorInstance + + let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) + navigationController.modalPresentationStyle = .fullScreen + strongSelf.present(navigationController, animated: true) + }) + .disposed(by: strongSelf.disposeBag) + } + }) + .disposed(by: disposeBag) + + // 목적지 좌표에 따른 마커 및 카메라 설정 + reactor.state + .map { $0.destinationCoordinate } + .compactMap { $0 } + .subscribe(onNext: { [weak self] coordinate in + self?.setupMarker(at: coordinate) + }) + .disposed(by: disposeBag) + + // searchResult로 현재 캐러셀 스토어 목록 업데이트 + reactor.state + .map { $0.searchResult } + .distinctUntilChanged() + .compactMap { $0 } + .subscribe(onNext: { [weak self] store in + self?.currentCarouselStoreList = [store] + }) + .disposed(by: disposeBag) + + // Dismiss 처리 + reactor.state + .map { $0.shouldDismiss } + .distinctUntilChanged() + .filter { $0 } + .subscribe(onNext: { [weak self] _ in + self?.dismissModalCard() + }) + .disposed(by: disposeBag) + } + // MARK: - UI Setup private func setupUI() { - view.backgroundColor = .clear + view.backgroundColor = .clear // 배경색을 clear로 설정하여 항상 딤드 뷰가 보이도록 함 view.addSubview(dimmingView) dimmingView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -176,91 +267,86 @@ final class MapGuideViewController: UIViewController, View { view.addSubview(modalCardView) modalCardView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() - make.height.equalTo(Constant.modalCardHeight) - self.modalCardBottomConstraint = make.bottom.equalToSuperview().offset(Constant.modalCardInitialBottomOffset).constraint + make.height.equalTo(408) + self.modalCardBottomConstraint = make.bottom.equalToSuperview().offset(408).constraint } - let topContainer = UIView() - modalCardView.addSubview(topContainer) - topContainer.snp.makeConstraints { make in - make.top.equalToSuperview().offset(Constant.topContainerTopOffset) + let topContainerView = UIView() + modalCardView.addSubview(topContainerView) + topContainerView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(20) make.leading.trailing.equalToSuperview() - make.height.equalTo(Constant.topContainerHeight) + make.height.equalTo(44) } - topContainer.addSubview(titleLabel) + topContainerView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.leading.equalToSuperview().offset(Constant.titleLabelLeadingOffset) + make.leading.equalToSuperview().offset(20) } - topContainer.addSubview(closeButton) + topContainerView.addSubview(closeButton) closeButton.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.trailing.equalToSuperview().inset(Constant.closeButtonTrailingInset) - make.width.height.equalTo(Constant.closeButtonSize) + make.trailing.equalToSuperview().inset(16) + make.width.height.equalTo(24) } modalCardView.addSubview(mapView) mapView.snp.makeConstraints { make in - make.top.equalTo(topContainer.snp.bottom).offset(Constant.mapViewTopOffset) - make.leading.trailing.equalToSuperview().inset(Constant.mapViewHorizontalInset) - make.height.equalTo(Constant.mapViewHeight) + make.top.equalTo(topContainerView.snp.bottom).offset(20) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(240) // 높이 약간 조정 } modalCardView.addSubview(expandButton) expandButton.snp.makeConstraints { make in - make.bottom.equalTo(mapView.snp.bottom).offset(Constant.expandButtonBottomOffset) - make.trailing.equalTo(mapView.snp.trailing).offset(Constant.expandButtonTrailingOffset) - make.width.height.equalTo(Constant.expandButtonSize) + make.bottom.equalTo(mapView.snp.bottom).offset(-10) + make.trailing.equalTo(mapView.snp.trailing).offset(-10) + make.width.height.equalTo(32) } - let bottomContainer = UIView() - modalCardView.addSubview(bottomContainer) - bottomContainer.snp.makeConstraints { make in - make.top.equalTo(mapView.snp.bottom).offset(Constant.bottomContainerTopOffset) - make.leading.trailing.equalToSuperview().inset(Constant.bottomContainerHorizontalInset) - make.height.equalTo(Constant.bottomContainerHeight) - make.bottom.equalTo(modalCardView.snp.bottom).inset(Constant.bottomContainerBottomInset) + let bottomContainerView = UIView() + modalCardView.addSubview(bottomContainerView) + bottomContainerView.snp.makeConstraints { make in + make.top.equalTo(mapView.snp.bottom).offset(20) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(44) + make.bottom.equalTo(modalCardView.snp.bottom).inset(60) } - bottomContainer.addSubview(promptLabel) + bottomContainerView.addSubview(promptLabel) promptLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() } - let appStack = UIStackView(arrangedSubviews: [naverButton, kakaoButton, appleButton]) - appStack.axis = .horizontal - appStack.alignment = .center - appStack.spacing = 16 - appStack.distribution = .fillEqually + let applicationStackView = UIStackView(arrangedSubviews: [naverButton, kakaoButton, appleButton]) + applicationStackView.axis = .horizontal + applicationStackView.alignment = .center + applicationStackView.spacing = 16 + applicationStackView.distribution = .fillEqually - bottomContainer.addSubview(appStack) - appStack.snp.makeConstraints { make in + bottomContainerView.addSubview(applicationStackView) + applicationStackView.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() [naverButton, kakaoButton, appleButton].forEach { button in - button.snp.makeConstraints { make in - make.size.equalTo(CGSize(width: 48, height: 48)) + button.snp.makeConstraints { constraint in + constraint.size.equalTo(CGSize(width: 48, height: 48)) } } } } - private func setupTapGesture() { - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapOnDimmingView)) - dimmingView.addGestureRecognizer(tapGesture) - dimmingView.isUserInteractionEnabled = true - } - private func presentModalCard() { self.dimmingView.alpha = 1 + UIView.animate( - withDuration: Constant.modalCardAnimationDuration, + withDuration: 0.3, delay: 0, - usingSpringWithDamping: Constant.modalCardAnimationDamping, - initialSpringVelocity: Constant.modalCardAnimationInitialVelocity, + usingSpringWithDamping: 0.8, + initialSpringVelocity: 0.5, options: .curveEaseOut ) { self.modalCardBottomConstraint?.update(offset: 0) @@ -269,154 +355,40 @@ final class MapGuideViewController: UIViewController, View { } private func setupMarker(at coordinate: CLLocationCoordinate2D) { - // 기존 마커 제거 - self.mapView.subviews.forEach { subview in + mapView.subviews.forEach { subview in if subview is NMFMarker { subview.removeFromSuperview() } } - let marker = NMFMarker() - marker.position = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) - marker.iconImage = NMFOverlayImage(name: "TapMarker") - marker.width = 44 - marker.height = 44 - marker.anchor = CGPoint(x: 0.5, y: 1.0) - marker.mapView = self.mapView + // 새 마커 생성 및 설정 + let markerInstance = NMFMarker() + markerInstance.position = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) + markerInstance.iconImage = NMFOverlayImage(name: "TapMarker") + markerInstance.width = 44 + markerInstance.height = 44 + markerInstance.anchor = CGPoint(x: 0.5, y: 1.0) + // 먼저 마커를 지도에 추가 + markerInstance.mapView = mapView + + // 카메라 위치 업데이트 let cameraUpdate = NMFCameraUpdate( scrollTo: NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude), - zoomTo: Constant.mapZoomLevel + zoomTo: 15.0 ) cameraUpdate.animation = .easeIn - cameraUpdate.animationDuration = Constant.modalCardAnimationDuration - self.mapView.moveCamera(cameraUpdate) + cameraUpdate.animationDuration = 0.3 + mapView.moveCamera(cameraUpdate) } private func dismissModalCard() { - UIView.animate(withDuration: Constant.modalCardAnimationDuration, delay: 0, options: .curveEaseIn) { + UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn) { self.dimmingView.alpha = 0 - self.modalCardBottomConstraint?.update(offset: Constant.modalCardInitialBottomOffset) + self.modalCardBottomConstraint?.update(offset: 408) self.view.layoutIfNeeded() } completion: { _ in self.dismiss(animated: false) } } - - @objc private func handleTapOnDimmingView(_ sender: UITapGestureRecognizer) { - let location = sender.location(in: self.view) - if !modalCardView.frame.contains(location) { - dismissModalCard() - } - } -} - -// MARK: - ReactorKit Binding -extension MapGuideViewController { - func bind(reactor: MapGuideReactor) { - reactor.action.onNext(.viewDidLoad(self.popUpStoreId)) - - closeButton.rx.tap - .subscribe(onNext: { [weak self] in - self?.dismissModalCard() - }) - .disposed(by: self.disposeBag) - - naverButton.rx.tap - .map { Reactor.Action.openMapApp("naver") } - .bind(to: reactor.action) - .disposed(by: self.disposeBag) - - kakaoButton.rx.tap - .map { Reactor.Action.openMapApp("kakao") } - .bind(to: reactor.action) - .disposed(by: self.disposeBag) - - appleButton.rx.tap - .map { Reactor.Action.openMapApp("apple") } - .bind(to: reactor.action) - .disposed(by: self.disposeBag) - - expandButton.rx.tap - .throttle(.milliseconds(Constant.expandButtonThrottleMilliseconds), scheduler: MainScheduler.instance) - .subscribe(onNext: { [weak self] in - guard let self = self else { return } - let provider = ProviderImpl() - let useCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) - let directionRepository = DefaultMapDirectionRepository(provider: provider) - let reactorInstance = MapReactor(useCase: useCase, directionRepository: directionRepository) - - if let selectedStore = self.currentCarouselStores.first { - reactorInstance.action.onNext(.didSelectItem(selectedStore)) - - let marker = NMFMarker() - marker.position = NMGLatLng(lat: selectedStore.latitude, lng: selectedStore.longitude) - marker.iconImage = NMFOverlayImage(name: "TapMarker") - marker.width = 44 - marker.height = 44 - marker.anchor = CGPoint(x: 0.5, y: 1.0) - marker.userInfo = ["storeData": selectedStore] - - let fullScreenMapViewController = FullScreenMapViewController(store: selectedStore, existingMarker: marker) - fullScreenMapViewController.reactor = reactorInstance - - let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) - navigationController.modalPresentationStyle = .fullScreen - self.present(navigationController, animated: true) - } else { - reactorInstance.action.onNext(.viewDidLoad(self.popUpStoreId)) - - reactorInstance.state - .map { $0.searchResult } - .distinctUntilChanged() - .compactMap { $0 } - .take(1) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { store in - let marker = NMFMarker() - marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) - marker.iconImage = NMFOverlayImage(name: "TapMarker") - marker.width = 44 - marker.height = 44 - marker.anchor = CGPoint(x: 0.5, y: 1.0) - marker.userInfo = ["storeData": store] - - let fullScreenMapViewController = FullScreenMapViewController(store: store, existingMarker: marker) - fullScreenMapViewController.reactor = reactorInstance - - let navigationController = UINavigationController(rootViewController: fullScreenMapViewController) - navigationController.modalPresentationStyle = .fullScreen - self.present(navigationController, animated: true) - }) - .disposed(by: self.disposeBag) - } - }) - .disposed(by: self.disposeBag) - - reactor.state - .map { $0.destinationCoordinate } - .compactMap { $0 } - .subscribe(onNext: { [weak self] coordinate in - self?.setupMarker(at: coordinate) - }) - .disposed(by: self.disposeBag) - - reactor.state - .map { $0.searchResult } - .distinctUntilChanged() - .compactMap { $0 } - .subscribe(onNext: { [weak self] store in - self?.currentCarouselStores = [store] - }) - .disposed(by: self.disposeBag) - - reactor.state - .map { $0.shouldDismiss } - .distinctUntilChanged() - .filter { $0 } - .subscribe(onNext: { [weak self] _ in - self?.dismissModalCard() - }) - .disposed(by: self.disposeBag) - } } From ab6df32dd9ee00fc19407f6f649b34d03f5ecd1b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 16:24:05 +0900 Subject: [PATCH 034/393] =?UTF-8?q?feat/#93:=20Swiftlint=20=EC=9E=90?= =?UTF-8?q?=EB=8F=99=20=EC=88=98=EC=A0=95=20=EA=B9=83=ED=97=88=EB=B8=8C=20?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/Swiftlint autocorrect.yml | 51 +++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .github/workflows/Swiftlint autocorrect.yml diff --git a/.github/workflows/Swiftlint autocorrect.yml b/.github/workflows/Swiftlint autocorrect.yml new file mode 100644 index 00000000..11cc6761 --- /dev/null +++ b/.github/workflows/Swiftlint autocorrect.yml @@ -0,0 +1,51 @@ +name: SwiftLint Autocorrect + +on: + push: + branches: ['dev/**'] + +jobs: + autocorrect: + name: SwiftLint Autocorrect + runs-on: macos-15 + if: github.actor != 'github-actions[bot]' # 커밋 무한 루프 방지를 위해 봇 커밋은 무시 + + steps: + - name: Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Xcode # Xcode 16.2 선택 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: Install SwiftLint # SwiftLint 설치 + run: brew install swiftlint + + - name: Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 + run: swiftlint autocorrect + + - name: Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 + run: | + LAST_COMMIT_MSG=$(git log -1 --pretty=%B) + if [[ "$LAST_COMMIT_MSG" == style/*Apply\ SwiftLint\ autocorrect* ]]; then + echo "Skipping: triggered by autocorrect commit." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + if [ -n "$(git status --porcelain)" ]; then + BRANCH_NAME="${GITHUB_REF_NAME}" + if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then + ISSUE_NUMBER="${BASH_REMATCH[1]}" + else + ISSUE_NUMBER="#??" + fi + git add . + git commit -m "style/${ISSUE_NUMBER}-Apply SwiftLint autocorrect" + git push + else + echo "No changes to commit" + fi From 3fd8a70d6b515dee0ee663abd8e4799dcc62511d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 16:26:06 +0900 Subject: [PATCH 035/393] =?UTF-8?q?remove/#93:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=83=80=EA=B2=9F=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 244 ------------------ Poppool/PoppoolTests/PoppoolTests.swift | 36 --- Poppool/PoppoolUITests/PoppoolUITests.swift | 41 --- .../PoppoolUITestsLaunchTests.swift | 32 --- 4 files changed, 353 deletions(-) delete mode 100644 Poppool/PoppoolTests/PoppoolTests.swift delete mode 100644 Poppool/PoppoolUITests/PoppoolUITests.swift delete mode 100644 Poppool/PoppoolUITests/PoppoolUITestsLaunchTests.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 0026d3f0..43178a19 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -410,7 +410,6 @@ 4E78706F2D37CB2200465FC9 /* PopUpStoreRegisterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12802D2BE0A6006744D6 /* PopUpStoreRegisterViewController.swift */; }; 4E8AA29D2D59A2340029DF75 /* MarkerTooltipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8AA29C2D59A2340029DF75 /* MarkerTooltipView.swift */; }; 4E9790C52D40E13500210499 /* MapGuideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9790C42D40E13500210499 /* MapGuideViewController.swift */; }; - 4E9A465E2D50B2DB0010578A /* AdminStoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B262D2B9C7C00ADFB21 /* AdminStoreCell.swift */; }; 4E9A46602D55D1270010578A /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9A465F2D55D1270010578A /* MapUtilities.swift */; }; 4E9C12782D2BC7A0006744D6 /* AdminBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12772D2BC7A0006744D6 /* AdminBottomSheetView.swift */; }; 4E9C127A2D2BC811006744D6 /* AdminBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12792D2BC811006744D6 /* AdminBottomSheetViewController.swift */; }; @@ -423,7 +422,6 @@ 4EAB809D2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809C2D3F78AA0041AF30 /* GMSMapViewDelegateProxy.swift */; }; 4EAB809F2D3F8EF50041AF30 /* ViewportBounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */; }; 4EDDEFB42D2D285900CFAFA5 /* DateTimePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */; }; - 4EDE57012D5E6A5F0014D924 /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */; }; 4EDE57032D5E70650014D924 /* LocationPermissionBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */; }; 4EE5A3D32D40E4A600A2469A /* MapGuideReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */; }; 4EEA1D8F2D352012003E7DE9 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */; }; @@ -460,9 +458,6 @@ BDCA41C52CF35AC0005EECF6 /* TestViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41C42CF35AC0005EECF6 /* TestViewController.swift */; }; BDCA41CA2CF35AC1005EECF6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BDCA41C92CF35AC1005EECF6 /* Assets.xcassets */; }; BDCA41CD2CF35AC1005EECF6 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = BDCA41CC2CF35AC1005EECF6 /* Base */; }; - BDCA41D82CF35AC1005EECF6 /* PoppoolTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41D72CF35AC1005EECF6 /* PoppoolTests.swift */; }; - BDCA41E22CF35AC1005EECF6 /* PoppoolUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41E12CF35AC1005EECF6 /* PoppoolUITests.swift */; }; - BDCA41E42CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41E32CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift */; }; BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F12CF35D0D005EECF6 /* SnapKit */; }; BDCA41F52CF35D33005EECF6 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F42CF35D33005EECF6 /* Kingfisher */; }; BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F72CF35D9A005EECF6 /* RxSwift */; }; @@ -475,23 +470,6 @@ BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA420F2CF35FF5005EECF6 /* Lottie */; }; /* End PBXBuildFile section */ -/* Begin PBXContainerItemProxy section */ - BDCA41D42CF35AC1005EECF6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BDCA41B52CF35AC0005EECF6 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BDCA41BC2CF35AC0005EECF6; - remoteInfo = Poppool; - }; - BDCA41DE2CF35AC1005EECF6 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = BDCA41B52CF35AC0005EECF6 /* Project object */; - proxyType = 1; - remoteGlobalIDString = BDCA41BC2CF35AC0005EECF6; - remoteInfo = Poppool; - }; -/* End PBXContainerItemProxy section */ - /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSection.swift; sourceTree = ""; }; @@ -941,11 +919,6 @@ BDCA41C92CF35AC1005EECF6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; BDCA41CC2CF35AC1005EECF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; BDCA41CE2CF35AC1005EECF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - BDCA41D32CF35AC1005EECF6 /* PoppoolTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PoppoolTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - BDCA41D72CF35AC1005EECF6 /* PoppoolTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoppoolTests.swift; sourceTree = ""; }; - BDCA41DD2CF35AC1005EECF6 /* PoppoolUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PoppoolUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - BDCA41E12CF35AC1005EECF6 /* PoppoolUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoppoolUITests.swift; sourceTree = ""; }; - BDCA41E32CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PoppoolUITestsLaunchTests.swift; sourceTree = ""; }; BDE30CE02CF87A9700C21E08 /* Poppool.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Poppool.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ @@ -974,20 +947,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BDCA41D02CF35AC1005EECF6 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BDCA41DA2CF35AC1005EECF6 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -2979,8 +2938,6 @@ children = ( 05229DD02D99519200D88E73 /* .swiftlint.yml */, BDCA41BF2CF35AC0005EECF6 /* Poppool */, - BDCA41D62CF35AC1005EECF6 /* PoppoolTests */, - BDCA41E02CF35AC1005EECF6 /* PoppoolUITests */, BDCA41BE2CF35AC0005EECF6 /* Products */, ); sourceTree = ""; @@ -2989,8 +2946,6 @@ isa = PBXGroup; children = ( BDCA41BD2CF35AC0005EECF6 /* Poppool.app */, - BDCA41D32CF35AC1005EECF6 /* PoppoolTests.xctest */, - BDCA41DD2CF35AC1005EECF6 /* PoppoolUITests.xctest */, ); name = Products; sourceTree = ""; @@ -3009,23 +2964,6 @@ path = Poppool; sourceTree = ""; }; - BDCA41D62CF35AC1005EECF6 /* PoppoolTests */ = { - isa = PBXGroup; - children = ( - BDCA41D72CF35AC1005EECF6 /* PoppoolTests.swift */, - ); - path = PoppoolTests; - sourceTree = ""; - }; - BDCA41E02CF35AC1005EECF6 /* PoppoolUITests */ = { - isa = PBXGroup; - children = ( - BDCA41E12CF35AC1005EECF6 /* PoppoolUITests.swift */, - BDCA41E32CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift */, - ); - path = PoppoolUITests; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -3066,42 +3004,6 @@ productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; productType = "com.apple.product-type.application"; }; - BDCA41D22CF35AC1005EECF6 /* PoppoolTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BDCA41EA2CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "PoppoolTests" */; - buildPhases = ( - BDCA41CF2CF35AC1005EECF6 /* Sources */, - BDCA41D02CF35AC1005EECF6 /* Frameworks */, - BDCA41D12CF35AC1005EECF6 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - BDCA41D52CF35AC1005EECF6 /* PBXTargetDependency */, - ); - name = PoppoolTests; - productName = PoppoolTests; - productReference = BDCA41D32CF35AC1005EECF6 /* PoppoolTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - BDCA41DC2CF35AC1005EECF6 /* PoppoolUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = BDCA41ED2CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "PoppoolUITests" */; - buildPhases = ( - BDCA41D92CF35AC1005EECF6 /* Sources */, - BDCA41DA2CF35AC1005EECF6 /* Frameworks */, - BDCA41DB2CF35AC1005EECF6 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - BDCA41DF2CF35AC1005EECF6 /* PBXTargetDependency */, - ); - name = PoppoolUITests; - productName = PoppoolUITests; - productReference = BDCA41DD2CF35AC1005EECF6 /* PoppoolUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -3115,14 +3017,6 @@ BDCA41BC2CF35AC0005EECF6 = { CreatedOnToolsVersion = 15.4; }; - BDCA41D22CF35AC1005EECF6 = { - CreatedOnToolsVersion = 15.4; - TestTargetID = BDCA41BC2CF35AC0005EECF6; - }; - BDCA41DC2CF35AC1005EECF6 = { - CreatedOnToolsVersion = 15.4; - TestTargetID = BDCA41BC2CF35AC0005EECF6; - }; }; }; buildConfigurationList = BDCA41B82CF35AC0005EECF6 /* Build configuration list for PBXProject "Poppool" */; @@ -3158,8 +3052,6 @@ projectRoot = ""; targets = ( BDCA41BC2CF35AC0005EECF6 /* Poppool */, - BDCA41D22CF35AC1005EECF6 /* PoppoolTests */, - BDCA41DC2CF35AC1005EECF6 /* PoppoolUITests */, ); }; /* End PBXProject section */ @@ -3185,20 +3077,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BDCA41D12CF35AC1005EECF6 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BDCA41DB2CF35AC1005EECF6 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -3662,40 +3540,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - BDCA41CF2CF35AC1005EECF6 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BDCA41D82CF35AC1005EECF6 /* PoppoolTests.swift in Sources */, - 4EDE57012D5E6A5F0014D924 /* MapViewController.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - BDCA41D92CF35AC1005EECF6 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BDCA41E42CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift in Sources */, - 4E9A465E2D50B2DB0010578A /* AdminStoreCell.swift in Sources */, - BDCA41E22CF35AC1005EECF6 /* PoppoolUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - BDCA41D52CF35AC1005EECF6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; - targetProxy = BDCA41D42CF35AC1005EECF6 /* PBXContainerItemProxy */; - }; - BDCA41DF2CF35AC1005EECF6 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; - targetProxy = BDCA41DE2CF35AC1005EECF6 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin PBXVariantGroup section */ BDCA41CB2CF35AC1005EECF6 /* LaunchScreen.storyboard */ = { isa = PBXVariantGroup; @@ -3916,78 +3762,6 @@ }; name = Release; }; - BDCA41EB2CF35AC2005EECF6 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2U86LHQK8Q; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.poppool.PoppoolTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Poppool.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Poppool"; - }; - name = Debug; - }; - BDCA41EC2CF35AC2005EECF6 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2U86LHQK8Q; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 17.5; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.poppool.PoppoolTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Poppool.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Poppool"; - }; - name = Release; - }; - BDCA41EE2CF35AC2005EECF6 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2U86LHQK8Q; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.poppool.PoppoolUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Poppool; - }; - name = Debug; - }; - BDCA41EF2CF35AC2005EECF6 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = 2U86LHQK8Q; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.poppool.PoppoolUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_TARGET_NAME = Poppool; - }; - name = Release; - }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -4009,24 +3783,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - BDCA41EA2CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "PoppoolTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BDCA41EB2CF35AC2005EECF6 /* Debug */, - BDCA41EC2CF35AC2005EECF6 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - BDCA41ED2CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "PoppoolUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - BDCA41EE2CF35AC2005EECF6 /* Debug */, - BDCA41EF2CF35AC2005EECF6 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Poppool/PoppoolTests/PoppoolTests.swift b/Poppool/PoppoolTests/PoppoolTests.swift deleted file mode 100644 index 931a598f..00000000 --- a/Poppool/PoppoolTests/PoppoolTests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// PoppoolTests.swift -// PoppoolTests -// -// Created by Porori on 11/24/24. -// - -import XCTest -@testable import Poppool - -final class PoppoolTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - // Any test you write for XCTest can be annotated as throws and async. - // Mark your test throws to produce an unexpected failure when your test encounters an uncaught error. - // Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/Poppool/PoppoolUITests/PoppoolUITests.swift b/Poppool/PoppoolUITests/PoppoolUITests.swift deleted file mode 100644 index cf8347f1..00000000 --- a/Poppool/PoppoolUITests/PoppoolUITests.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// PoppoolUITests.swift -// PoppoolUITests -// -// Created by Porori on 11/24/24. -// - -import XCTest - -final class PoppoolUITests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // UI tests must launch the application that they test. - let app = XCUIApplication() - app.launch() - - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testLaunchPerformance() throws { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { - // This measures how long it takes to launch your application. - measure(metrics: [XCTApplicationLaunchMetric()]) { - XCUIApplication().launch() - } - } - } -} diff --git a/Poppool/PoppoolUITests/PoppoolUITestsLaunchTests.swift b/Poppool/PoppoolUITests/PoppoolUITestsLaunchTests.swift deleted file mode 100644 index e80f88dd..00000000 --- a/Poppool/PoppoolUITests/PoppoolUITestsLaunchTests.swift +++ /dev/null @@ -1,32 +0,0 @@ -// -// PoppoolUITestsLaunchTests.swift -// PoppoolUITests -// -// Created by Porori on 11/24/24. -// - -import XCTest - -final class PoppoolUITestsLaunchTests: XCTestCase { - - override class var runsForEachTargetApplicationUIConfiguration: Bool { - true - } - - override func setUpWithError() throws { - continueAfterFailure = false - } - - func testLaunch() throws { - let app = XCUIApplication() - app.launch() - - // Insert steps here to perform after app launch but before taking a screenshot, - // such as logging into a test account or navigating somewhere in the app - - let attachment = XCTAttachment(screenshot: app.screenshot()) - attachment.name = "Launch Screen" - attachment.lifetime = .keepAlways - add(attachment) - } -} From e3cdf104b144297f35b46807bcb93e45def3b63b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 16:27:21 +0900 Subject: [PATCH 036/393] =?UTF-8?q?feat/#93:=20CI=20=EA=B9=83=ED=97=88?= =?UTF-8?q?=EB=B8=8C=20=EC=95=A1=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Build 테스트 포함 - Swiftlint 테스트 포함 --- .github/workflows/CI.yml | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..abaa4676 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,50 @@ +name: 📱 iOS CI + +on: + pull_request: + branches: [dev] # dev 브랜치로의 PR에 대해 실행 + push: + branches: [dev] # dev 브랜치로의 push에 대해 실행 + +jobs: + build: + name: Build & Lint Workflow + runs-on: macos-15 # 최신 macOS 15 환경에서 실행 + timeout-minutes: 15 # 최대 실행 시간 15분 설정 + + steps: + - name: ✅ Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + + - name: 🛠️ Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: ⬇️ Install SwiftLint # SwiftLint 설치 + run: brew install swiftlint + + - name: 🎨 Run SwiftLint # SwiftLint 코드 스타일 검사 실행 + run: swiftlint + + - name: 🔍 Detect Default Scheme # 기본 scheme 자동 검지 + id: detect_scheme + run: | + SCHEME=$(xcodebuild -list -json | jq -r '.project.schemes[0]') + echo "Detected scheme: $SCHEME" + echo "scheme=$SCHEME" >> "$GITHUB_OUTPUT" + + - name: 🔍 Detect Latest iPhone Simulator # 최신 사용 가능한 iPhone 시뮬레이터 검지 + id: detect_latest_simulator + run: | + DEVICE=$(xcrun simctl list devices available | grep -Eo 'iPhone .* \([0-9A-F\-]+\)' | head -n 1) + UDID=$(echo "$DEVICE" | grep -Eo '[0-9A-F\-]{36}') + NAME=$(echo "$DEVICE" | cut -d '(' -f1 | xargs) + echo "Detected simulator: $NAME ($UDID)" + echo "sim_name=$NAME" >> "$GITHUB_OUTPUT" + echo "sim_udid=$UDID" >> "$GITHUB_OUTPUT" + + - name: 🏗️ Build the project # 자동 검지된 Scheme과 Simulator로 빌드 수행 + run: | + xcodebuild -scheme "${{ steps.detect_scheme.outputs.scheme }}" \ + -workspace Poppool.xcworkspace \ + -destination "platform=iOS Simulator,id=${{ steps.detect_latest_simulator.outputs.sim_udid }}" \ + clean build | xcpretty From 4420daffa899fb427b3384c024d649fef5de9015 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 16:43:17 +0900 Subject: [PATCH 037/393] =?UTF-8?q?fix/#93:=20CI=EA=B0=80=20=EA=B9=83?= =?UTF-8?q?=EC=95=A1=EC=85=98=20=EB=B4=87=EC=9D=98=20=EC=BB=A4=EB=B0=8B?= =?UTF-8?q?=EC=9D=80=20=EB=AC=B4=EC=8B=9C=ED=95=98=EB=8F=84=EB=A1=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 --- .github/workflows/CI.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index abaa4676..a9735af5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,6 +11,7 @@ jobs: name: Build & Lint Workflow runs-on: macos-15 # 최신 macOS 15 환경에서 실행 timeout-minutes: 15 # 최대 실행 시간 15분 설정 + if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 steps: - name: ✅ Checkout Repository # 저장소 코드 체크아웃 From 2fcd2cfb7782848751cae5de63e65b9e2771b083 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 18:37:31 +0900 Subject: [PATCH 038/393] =?UTF-8?q?chore/#93:=20Debug.xcconfig=EB=A1=9C=20?= =?UTF-8?q?=EB=8C=80=EC=B2=B4=ED=95=98=EA=B8=B0=20=EC=9C=84=ED=95=9C=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=EC=84=A4=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 - Debug.xcconfig를 Resource 폴더에 추가함 - 해당 파일은 ignore로 걸러지도록 함 - info에 Key의 이름을 등록해두어 Bundle로 찾도록 설정 --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 ++++ Poppool/Poppool/Resource/Info.plist | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 43178a19..a29e30d4 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -472,6 +472,7 @@ /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 057151572D9D2E0800260615 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSection.swift; sourceTree = ""; }; 0818988F2D295DC80067BF01 /* MyPageLogoutSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSectionCell.swift; sourceTree = ""; }; 081898932D2965C20067BF01 /* ProfileEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditController.swift; sourceTree = ""; }; @@ -1338,6 +1339,7 @@ children = ( 08DE8A132D525A4A0049BCAC /* Strings */, 08B191C12CF615CA0057BC04 /* Secrets.swift */, + 057151572D9D2E0800260615 /* Debug.xcconfig */, 08B191532CF41D6F0057BC04 /* PP_loading.json */, 08B191542CF41D6F0057BC04 /* PP_splash.json */, 08B1914A2CF41D680057BC04 /* Font */, @@ -3678,6 +3680,7 @@ }; BDCA41E82CF35AC2005EECF6 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 057151572D9D2E0800260615 /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -3721,6 +3724,7 @@ }; BDCA41E92CF35AC2005EECF6 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 057151572D9D2E0800260615 /* Debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 9b8b88b1..c4884906 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,6 +2,16 @@ + KAKAO_AUTH_APP_KEY + ${KAKAO_AUTH_APP_KEY} + POPPOOL_BASE_URL + ${POPPOOL_BASE_URL} + POPPOOL_S3_BASE_URL + ${POPPOOL_S3_BASE_URL} + POPPOOL_API_KEY + ${POPPOOL_API_KEY} + NAVER_MAP_CLIENT_ID + ${NAVER_MAP_CLIENT_ID} CFBundleURLTypes From 37a7022648ea0a44e3b59d395542dac994d689c6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 18:38:57 +0900 Subject: [PATCH 039/393] =?UTF-8?q?chore/#93:=20gitignore=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 새로운 Debug.xcconfig를 무시하도록 수정 - 기존 ignore 설정을 보기 쉽게 정리 --- .gitignore | 51 +++++++++++++++------------------------------------ 1 file changed, 15 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index df2cb547..67a5e84e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,65 +1,44 @@ -# Xcode -# -# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore - -## User settings +# Xcode 관련 xcuserdata/ +.DS_Store + +# 개인 설정 및 비밀 정보 +Secrets.swift +*.xcconfig -## Obj-C/Swift specific +# Objective-C / Swift 관련 *.hmap -## App packaging +# 앱 패키징 *.ipa *.dSYM.zip *.dSYM -## Playgrounds +# Playgrounds timeline.xctimeline playground.xcworkspace -# Swift Package Manager -# -# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Swift Package Manager (SPM) +.build/ +# 패키지 관련 파일을 무시하고 싶다면 아래 항목을 활성화하세요. # Packages/ # Package.pins # Package.resolved # *.xcodeproj -# -# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata -# hence it is not needed unless you have added a package configuration file to your project # .swiftpm -.build/ - # CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# +# Pods 디렉토리를 무시하고 싶다면 아래 항목을 활성화하세요. # Pods/ -# -# Add this line if you want to avoid checking in source code from the Xcode workspace # *.xcworkspace # Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. -# Carthage/Checkouts - Carthage/Build/ +# Carthage 의존성을 무시하고 싶다면 아래 항목을 활성화하세요. +# Carthage/Checkouts # fastlane -# -# It is recommended to not store the screenshots in the git repo. -# Instead, use fastlane to re-generate the screenshots whenever they are needed. -# For more information about the recommended setup visit: -# https://docs.fastlane.tools/best-practices/source-control/#source-control - fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output - -.DS_Store -Secrets.swift From 46b93119ee25b6521817cd79bda9c25ca84a9749 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 18:42:07 +0900 Subject: [PATCH 040/393] =?UTF-8?q?refactor/#93:=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?=EB=90=9C=20=ED=82=A4=20=EC=A0=80=EC=9E=A5=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=82=A4=20=ED=98=B8=EC=B6=9C?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Secrets.swift의 enum을 열도록 할 예정 - 해당 파일에서 Key 호출을 위한 여러 컴퓨티드 프로퍼티 제공 - 변경된 프로퍼티에 맞게 호출 코드도 일부 수정 --- Poppool/Poppool/Application/AppDelegate.swift | 4 +- .../Network/AuthAPI/AuthAPIEndPoint.swift | 4 +- .../CommentAPI/CommentAPIEndPoint.swift | 6 +-- .../Network/HomeAPI/HomeAPIEndpoint.swift | 8 ++-- .../Network/PopUpAPI/PopUpAPIEndPoint.swift | 10 ++--- .../Network/SignUpAPI/SignUpAPIEndpoint.swift | 6 +-- .../Network/UserAPI/UserAPIEndPoint.swift | 42 +++++++++---------- .../PreSignedAPIEndPoint.swift | 6 +-- .../PreSignedService/PreSignedService.swift | 2 +- .../PopUpStoreRegisterViewController.swift | 2 +- .../Presentation/Admin/AdminStoreCell.swift | 2 +- .../Admin/Data/MapDomain/MapAPIEndpoint.swift | 4 +- .../Admin/Data/Remote/AdminAPIEndpoint.swift | 16 +++---- .../Presentation/Extension/UIImageView+.swift | 4 +- .../MapGuideView/FindDirectionEndPoint.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 2 +- 16 files changed, 60 insertions(+), 60 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 40284a66..0480ecd5 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -8,8 +8,8 @@ import CoreLocation class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppkey.rawValue) - GMSServices.provideAPIKey(Secrets.popPoolApiKey.rawValue) + KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppKey) + GMSServices.provideAPIKey(Secrets.popPoolAPIKey) let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 diff --git a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift index ead5cbee..d556b641 100644 --- a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift @@ -18,7 +18,7 @@ struct AuthAPIEndPoint { /// - Returns: Endpoint static func auth_tryLogin(with userCredential: Encodable, path: String) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/auth/\(path)", method: .post, bodyParameters: userCredential, @@ -28,7 +28,7 @@ struct AuthAPIEndPoint { static func postTokenReissue() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/auth/token/reissue", method: .post ) diff --git a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift index fda16452..9e799b41 100644 --- a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift @@ -13,7 +13,7 @@ struct CommentAPIEndPoint { static func postCommentAdd(request: PostCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .post, bodyParameters: request @@ -22,7 +22,7 @@ struct CommentAPIEndPoint { static func deleteComment(request: DeleteCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct CommentAPIEndPoint { static func editComment(request: PutCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .put, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift index 94a63b0c..dba7b377 100644 --- a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift @@ -13,7 +13,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/home", method: .get, queryParameters: request @@ -24,7 +24,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/home/popular/popup-stores", method: .get, queryParameters: request @@ -35,7 +35,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/home/new/popup-stores", method: .get, queryParameters: request @@ -46,7 +46,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/home/custom/popup-stores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift index e163f336..4d115a20 100644 --- a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift @@ -13,7 +13,7 @@ struct PopUpAPIEndPoint { static func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/popup/closed", method: .get, queryParameters: request @@ -22,7 +22,7 @@ struct PopUpAPIEndPoint { static func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/popup/open", method: .get, queryParameters: request @@ -31,7 +31,7 @@ struct PopUpAPIEndPoint { static func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/search/popup-stores", method: .get, queryParameters: request @@ -40,7 +40,7 @@ struct PopUpAPIEndPoint { static func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/detail", method: .get, queryParameters: request @@ -49,7 +49,7 @@ struct PopUpAPIEndPoint { static func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/comments", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift index dc29870e..b587531b 100644 --- a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift @@ -14,7 +14,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_checkNickName(with request: CheckNickNameRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/signup/check-nickname", method: .get, queryParameters: request @@ -25,7 +25,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_getCategoryList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/signup/categories", method: .get ) @@ -36,7 +36,7 @@ struct SignUpAPIEndpoint { /// - Returns: RequestEndpoint static func signUp_trySignUp(with request: SignUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/signup", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift index fce30a7d..142d7350 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift @@ -13,7 +13,7 @@ struct UserAPIEndPoint { static func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .post, queryParameters: request @@ -22,7 +22,7 @@ struct UserAPIEndPoint { static func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct UserAPIEndPoint { static func postCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/likes", method: .post, queryParameters: request @@ -40,7 +40,7 @@ struct UserAPIEndPoint { static func deleteCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/likes", method: .delete, queryParameters: request @@ -49,7 +49,7 @@ struct UserAPIEndPoint { static func postUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/block", method: .post, queryParameters: request @@ -58,7 +58,7 @@ struct UserAPIEndPoint { static func deleteUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/unblock", method: .delete, queryParameters: request @@ -67,7 +67,7 @@ struct UserAPIEndPoint { static func getOtherUserCommentPopUpList(request: GetOtherUserCommentListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/\(request.commenterId ?? "")/comments", method: .get, queryParameters: request @@ -76,7 +76,7 @@ struct UserAPIEndPoint { static func getMyPage() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/my-page", method: .get ) @@ -84,7 +84,7 @@ struct UserAPIEndPoint { static func postLogout() -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/logout", method: .post ) @@ -92,7 +92,7 @@ struct UserAPIEndPoint { static func getWithdrawlList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/withdrawl/surveys", method: .get ) @@ -100,7 +100,7 @@ struct UserAPIEndPoint { static func postWithdrawl(request: PostWithdrawlListRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/delete", method: .post, bodyParameters: request @@ -109,7 +109,7 @@ struct UserAPIEndPoint { static func getMyProfile() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/profiles", method: .get ) @@ -117,7 +117,7 @@ struct UserAPIEndPoint { static func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/tailored-info", method: .put, bodyParameters: request @@ -126,7 +126,7 @@ struct UserAPIEndPoint { static func putUserCategory(request: PutUserCategoryRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/interests", method: .put, bodyParameters: request @@ -135,7 +135,7 @@ struct UserAPIEndPoint { static func putUserProfile(request: PutUserProfileRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/profiles", method: .put, bodyParameters: request @@ -144,7 +144,7 @@ struct UserAPIEndPoint { static func getMyCommentedPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/commented/popup", method: .get, queryParameters: request @@ -153,7 +153,7 @@ struct UserAPIEndPoint { static func getBlockUserList(request: GetBlockUserListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/blocked", method: .get, queryParameters: request @@ -162,7 +162,7 @@ struct UserAPIEndPoint { static func getNoticeList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/notice/list", method: .get ) @@ -170,7 +170,7 @@ struct UserAPIEndPoint { static func getNoticeDetail(noticeID: Int64) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/notice/\(noticeID)", method: .get ) @@ -178,7 +178,7 @@ struct UserAPIEndPoint { static func getRecentPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/recent-popupstores", method: .get, queryParameters: request @@ -187,7 +187,7 @@ struct UserAPIEndPoint { static func getBookmarkPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift index a1bba549..556bf5d2 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift @@ -11,7 +11,7 @@ struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/files/upload-preSignedUrl", method: .post, bodyParameters: request @@ -21,7 +21,7 @@ struct PreSignedAPIEndPoint { static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/files/download-preSignedUrl", method: .post, bodyParameters: request @@ -31,7 +31,7 @@ struct PreSignedAPIEndPoint { static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint { Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug) return RequestEndpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/files/delete", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift index 73343587..61665b4e 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift @@ -291,7 +291,7 @@ extension PreSignedService { } } func fullImageURL(from filePath: String) -> URL? { - let baseURL = Secrets.popPoolS3BaseURL.rawValue + let baseURL = Secrets.popPoolS3BaseURL // URL 인코딩 처리를 더 엄격하게 guard let encodedPath = filePath diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index a559ee54..710f001f 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1244,7 +1244,7 @@ private extension PopUpStoreRegisterViewController { // 이미지 업로드 private func uploadImages() { let uuid = UUID().uuidString - // let baseS3URL = Secrets.popPoolS3BaseURL.rawValue + // let baseS3URL = Secrets.popPoolS3BaseURL let updatedImages = images.enumerated().map { index, image in let filePath = "PopUpImage/\(nameField?.text ?? "")/\(uuid)/\(index).jpg" return ExtendedImage( diff --git a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift index 706addaf..46e8d56c 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift @@ -83,7 +83,7 @@ final class AdminStoreCell: UITableViewCell { statusChip.text = "운영" // mainImageUrl에서 baseURL 부분 제거 - let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL.rawValue, with: "") + let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift index e9da01f6..66806958 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift @@ -26,7 +26,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/locations/popup-stores", method: .get, queryParameters: params @@ -44,7 +44,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/locations/search", method: .get, queryParameters: params diff --git a/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift b/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift index cfb9baf3..2f9a6433 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift @@ -14,7 +14,7 @@ struct AdminAPIEndpoint { size: size ) return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores/list", method: .get, queryParameters: params @@ -26,7 +26,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .get, queryParameters: ["popUpStoreId": id] @@ -38,7 +38,7 @@ struct AdminAPIEndpoint { request: CreatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .post, bodyParameters: request @@ -50,7 +50,7 @@ struct AdminAPIEndpoint { request: UpdatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .put, bodyParameters: request @@ -62,7 +62,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .delete, queryParameters: ["popUpStoreId": id] @@ -74,7 +74,7 @@ struct AdminAPIEndpoint { request: CreateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice", method: .post, bodyParameters: request @@ -86,7 +86,7 @@ struct AdminAPIEndpoint { request: UpdateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice/\(id)", method: .put, bodyParameters: request @@ -97,7 +97,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice/\(id)", method: .delete ) diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index 8040bddb..03de32eb 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -15,7 +15,7 @@ extension UIImageView { self.image = UIImage(named: "image_default") return } - let imageURLString = Secrets.popPoolS3BaseURL.rawValue + path + let imageURLString = Secrets.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) self.kf.setImage(with: imageURL) { result in @@ -35,7 +35,7 @@ extension UIImageView { completion() return } - let imageURLString = Secrets.popPoolS3BaseURL.rawValue + path + let imageURLString = Secrets.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) self.kf.setImage(with: imageURL) { result in diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift index 973a81e5..b9a7c5ea 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift @@ -13,7 +13,7 @@ struct FindDirectionEndPoint { popUpStoreId: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseUrl.rawValue, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(popUpStoreId)/directions", method: .get ) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 165a8b7a..3b910a9b 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -381,7 +381,7 @@ final class DetailReactor: Reactor { func showSharedBoard(controller: BaseViewController) { let storeName = titleSection.inputDataList.first?.title ?? "" - let imagePath = Secrets.popPoolS3BaseURL.rawValue + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") + let imagePath = Secrets.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), From 7af96b8af3519b25a2f30e8c584876f59bc4697b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 19:02:52 +0900 Subject: [PATCH 041/393] =?UTF-8?q?refactor/#93:=20GitHubActions=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 깃헙 액션에 맞게 파일 이름 수정 - CI → ci - Swiftlint autocorrect → swiftlint-autocorrect - 추가로 autocorrect의 작동 시기를 Push → PR open으로 수정하여 깃액션 작동 빈도를 줄임 --- .github/workflows/Swiftlint autocorrect.yml | 51 --------------------- .github/workflows/{CI.yml => ios-ci.yml} | 2 +- .github/workflows/swiftlint-autocorrect.yml | 47 +++++++++++++++++++ 3 files changed, 48 insertions(+), 52 deletions(-) delete mode 100644 .github/workflows/Swiftlint autocorrect.yml rename .github/workflows/{CI.yml => ios-ci.yml} (96%) create mode 100644 .github/workflows/swiftlint-autocorrect.yml diff --git a/.github/workflows/Swiftlint autocorrect.yml b/.github/workflows/Swiftlint autocorrect.yml deleted file mode 100644 index 11cc6761..00000000 --- a/.github/workflows/Swiftlint autocorrect.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: SwiftLint Autocorrect - -on: - push: - branches: ['dev/**'] - -jobs: - autocorrect: - name: SwiftLint Autocorrect - runs-on: macos-15 - if: github.actor != 'github-actions[bot]' # 커밋 무한 루프 방지를 위해 봇 커밋은 무시 - - steps: - - name: Checkout Repository # 저장소 코드 체크아웃 - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: Set up Xcode # Xcode 16.2 선택 - run: sudo xcode-select -s /Applications/Xcode_16.2.app - - - name: Install SwiftLint # SwiftLint 설치 - run: brew install swiftlint - - - name: Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 - run: swiftlint autocorrect - - - name: Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 - run: | - LAST_COMMIT_MSG=$(git log -1 --pretty=%B) - if [[ "$LAST_COMMIT_MSG" == style/*Apply\ SwiftLint\ autocorrect* ]]; then - echo "Skipping: triggered by autocorrect commit." - exit 0 - fi - - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - if [ -n "$(git status --porcelain)" ]; then - BRANCH_NAME="${GITHUB_REF_NAME}" - if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then - ISSUE_NUMBER="${BASH_REMATCH[1]}" - else - ISSUE_NUMBER="#??" - fi - git add . - git commit -m "style/${ISSUE_NUMBER}-Apply SwiftLint autocorrect" - git push - else - echo "No changes to commit" - fi diff --git a/.github/workflows/CI.yml b/.github/workflows/ios-ci.yml similarity index 96% rename from .github/workflows/CI.yml rename to .github/workflows/ios-ci.yml index a9735af5..9b39b9c8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/ios-ci.yml @@ -17,7 +17,7 @@ jobs: - name: ✅ Checkout Repository # 저장소 코드 체크아웃 uses: actions/checkout@v4 - - name: 🛠️ Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 + - name: 🔨 Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 run: sudo xcode-select -s /Applications/Xcode_16.2.app - name: ⬇️ Install SwiftLint # SwiftLint 설치 diff --git a/.github/workflows/swiftlint-autocorrect.yml b/.github/workflows/swiftlint-autocorrect.yml new file mode 100644 index 00000000..895d5868 --- /dev/null +++ b/.github/workflows/swiftlint-autocorrect.yml @@ -0,0 +1,47 @@ +name: 🔧 Swiftlint Autocorrect + +on: + pull_request: + types: [opened] + branches: ['dev/**'] + +jobs: + autocorrect: + name: SwiftLint Autocorrect + runs-on: macos-15 + if: github.actor != 'github-actions[bot]' # 커밋 무한 루프 방지를 위해 봇 커밋은 무시 + + steps: + - name: 📁 Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: 🔨 Set up Xcode # Xcode 16.2 선택 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: ⬇️ Install SwiftLint # SwiftLint 설치 + run: brew install swiftlint + + - name: 🔧 Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 + run: swiftlint autocorrect + + - name: 🚀 Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + BRANCH_NAME="${GITHUB_REF_NAME}" + if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then + ISSUE_NUMBER="${BASH_REMATCH[1]}" + else + ISSUE_NUMBER="#??" + fi + + if [ -n "$(git status --porcelain)" ]; then + git add . + git commit -m "style/${ISSUE_NUMBER}: Apply SwiftLint autocorrect" + git push + else + echo "No changes to commit" + fi From fb7918fae2c20a9c793288e7d2355cc4c8a81f00 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 22:57:10 +0900 Subject: [PATCH 042/393] =?UTF-8?q?chore/#93:=20CI=EC=97=90=20Debug.xcconf?= =?UTF-8?q?ig=20=EC=83=9D=EC=84=B1=20=EC=8A=A4=ED=85=9D=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 --- .github/workflows/ios-ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 9b39b9c8..98f1a3a9 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -17,6 +17,16 @@ jobs: - name: ✅ Checkout Repository # 저장소 코드 체크아웃 uses: actions/checkout@v4 + - name: 🧾 Generate Debug.xcconfig + run: | + cat < Poppool/Poppool/Resource/Debug.xcconfig + KAKAO_AUTH_APP_KEY=${{ secrets.KAKAO_AUTH_APP_KEY }} + NAVER_MAP_CLIENT_ID=${{ secrets.NAVER_MAP_CLIENT_ID }} + POPPOOL_BASE_URL=${{ secrets.POPPOOL_BASE_URL }} + POPPOOL_S3_BASE_URL=${{ secrets.POPPOOL_S3_BASE_URL }} + POPPOOL_API_KEY=${{ secrets.POPPOOL_API_KEY }} + EOF + - name: 🔨 Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 run: sudo xcode-select -s /Applications/Xcode_16.2.app From b00d50bd35616eaad8504918f26b0afcd4870946 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 23:15:07 +0900 Subject: [PATCH 043/393] =?UTF-8?q?refactor/#93:=20Secrets.swift=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 - Secrets enum을 대체할 KeyPath enum을 추가 - gitignore에서 Secrets.swift 추적 제한 제거 - 빌드 파일 수정 --- .gitignore | 1 - Poppool/Poppool.xcodeproj/project.pbxproj | 8 +++--- Poppool/Poppool/Resource/KeyPath.swift | 30 +++++++++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 Poppool/Poppool/Resource/KeyPath.swift diff --git a/.gitignore b/.gitignore index 67a5e84e..3dd82707 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ xcuserdata/ .DS_Store # 개인 설정 및 비밀 정보 -Secrets.swift *.xcconfig # Objective-C / Swift 관련 diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index a29e30d4..d0a41648 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -336,7 +336,7 @@ 08B191B42CF609260057BC04 /* KakaoLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B32CF609260057BC04 /* KakaoLoginService.swift */; }; 08B191B62CF6092B0057BC04 /* AppleLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */; }; 08B191B82CF6092F0057BC04 /* AuthServiceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */; }; - 08B191C22CF615CA0057BC04 /* Secrets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191C12CF615CA0057BC04 /* Secrets.swift */; }; + 08B191C22CF615CA0057BC04 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191C12CF615CA0057BC04 /* KeyPath.swift */; }; 08CBEA032D38989E00248007 /* PostTokenReissueResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */; }; 08CBEA062D38991600248007 /* PostTokenReissueResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */; }; 08CBEA0B2D38DBD600248007 /* LastLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA0A2D38DBD600248007 /* LastLoginView.swift */; }; @@ -799,7 +799,7 @@ 08B191B32CF609260057BC04 /* KakaoLoginService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KakaoLoginService.swift; sourceTree = ""; }; 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleLoginService.swift; sourceTree = ""; }; 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthServiceable.swift; sourceTree = ""; }; - 08B191C12CF615CA0057BC04 /* Secrets.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Secrets.swift; sourceTree = ""; }; + 08B191C12CF615CA0057BC04 /* KeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = ""; }; 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTokenReissueResponseDTO.swift; sourceTree = ""; }; 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTokenReissueResponse.swift; sourceTree = ""; }; 08CBEA0A2D38DBD600248007 /* LastLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastLoginView.swift; sourceTree = ""; }; @@ -1338,7 +1338,7 @@ isa = PBXGroup; children = ( 08DE8A132D525A4A0049BCAC /* Strings */, - 08B191C12CF615CA0057BC04 /* Secrets.swift */, + 08B191C12CF615CA0057BC04 /* KeyPath.swift */, 057151572D9D2E0800260615 /* Debug.xcconfig */, 08B191532CF41D6F0057BC04 /* PP_loading.json */, 08B191542CF41D6F0057BC04 /* PP_splash.json */, @@ -3218,7 +3218,7 @@ 086DD9402D01EEEB00B97D3B /* SearchCountTitleSection.swift in Sources */, 083C864C2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift in Sources */, 086DD8CE2CFDFEB000B97D3B /* HomeListView.swift in Sources */, - 08B191C22CF615CA0057BC04 /* Secrets.swift in Sources */, + 08B191C22CF615CA0057BC04 /* KeyPath.swift in Sources */, 083C86242D087A44003F441C /* DetailTitleSection.swift in Sources */, 08A2E4822D1BCDEA00102313 /* CommentDetailController.swift in Sources */, 0841BAA32CFA31A300049E31 /* SpacingSection.swift in Sources */, diff --git a/Poppool/Poppool/Resource/KeyPath.swift b/Poppool/Poppool/Resource/KeyPath.swift new file mode 100644 index 00000000..4bc23b90 --- /dev/null +++ b/Poppool/Poppool/Resource/KeyPath.swift @@ -0,0 +1,30 @@ +import Foundation + +enum KeyPath { + static var kakaoAuthAppKey: String { + return getValue(forKey: "KAKAO_AUTH_APP_KEY") + } + + static var popPoolBaseURL: String { + return getValue(forKey: "POPPOOL_BASE_URL") + } + + static var popPoolS3BaseURL: String { + return getValue(forKey: "POPPOOL_S3_BASE_URL") + } + + static var popPoolAPIKey: String { + return getValue(forKey: "POPPOOL_API_KEY") + } + + static var naverMapClientID: String { + return getValue(forKey: "NAVER_MAP_CLIENT_ID") + } + + private static func getValue(forKey key: String) -> String { + guard let value = Bundle.main.object(forInfoDictionaryKey: key) as? String else { + fatalError("Missing key: \(key) in Info.plist") + } + return value + } +} From f958ff67ac5ce5a521e3dc780223d4a3f9547d8e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 2 Apr 2025 23:15:39 +0900 Subject: [PATCH 044/393] =?UTF-8?q?refactor/#93:=20Secrets=20enum=20?= =?UTF-8?q?=E2=86=92=20KeyPath=20enum=20=EB=B3=80=EA=B2=BD=20=EB=8C=80?= =?UTF-8?q?=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 4 +- .../Network/AuthAPI/AuthAPIEndPoint.swift | 4 +- .../CommentAPI/CommentAPIEndPoint.swift | 6 +-- .../Network/HomeAPI/HomeAPIEndpoint.swift | 8 ++-- .../Network/PopUpAPI/PopUpAPIEndPoint.swift | 10 ++--- .../Network/SignUpAPI/SignUpAPIEndpoint.swift | 6 +-- .../Network/UserAPI/UserAPIEndPoint.swift | 42 +++++++++---------- .../PreSignedAPIEndPoint.swift | 6 +-- .../PreSignedService/PreSignedService.swift | 2 +- .../Presentation/Admin/AdminStoreCell.swift | 2 +- .../Admin/Data/MapDomain/MapAPIEndpoint.swift | 4 +- .../Admin/Data/Remote/AdminAPIEndpoint.swift | 16 +++---- .../Presentation/Extension/UIImageView+.swift | 4 +- .../MapGuideView/FindDirectionEndPoint.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 2 +- 15 files changed, 59 insertions(+), 59 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 0480ecd5..200881dc 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -8,8 +8,8 @@ import CoreLocation class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppKey) - GMSServices.provideAPIKey(Secrets.popPoolAPIKey) + KakaoSDK.initSDK(appKey: KeyPath.kakaoAuthAppKey) + GMSServices.provideAPIKey(KeyPath.popPoolAPIKey) let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 diff --git a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift index d556b641..860fb485 100644 --- a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift @@ -18,7 +18,7 @@ struct AuthAPIEndPoint { /// - Returns: Endpoint static func auth_tryLogin(with userCredential: Encodable, path: String) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/auth/\(path)", method: .post, bodyParameters: userCredential, @@ -28,7 +28,7 @@ struct AuthAPIEndPoint { static func postTokenReissue() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/auth/token/reissue", method: .post ) diff --git a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift index 9e799b41..6c91aefb 100644 --- a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift @@ -13,7 +13,7 @@ struct CommentAPIEndPoint { static func postCommentAdd(request: PostCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/comments", method: .post, bodyParameters: request @@ -22,7 +22,7 @@ struct CommentAPIEndPoint { static func deleteComment(request: DeleteCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/comments", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct CommentAPIEndPoint { static func editComment(request: PutCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/comments", method: .put, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift index dba7b377..4140756c 100644 --- a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift @@ -13,7 +13,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/home", method: .get, queryParameters: request @@ -24,7 +24,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/home/popular/popup-stores", method: .get, queryParameters: request @@ -35,7 +35,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/home/new/popup-stores", method: .get, queryParameters: request @@ -46,7 +46,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/home/custom/popup-stores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift index 4d115a20..6e12dd8d 100644 --- a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift @@ -13,7 +13,7 @@ struct PopUpAPIEndPoint { static func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/popup/closed", method: .get, queryParameters: request @@ -22,7 +22,7 @@ struct PopUpAPIEndPoint { static func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/popup/open", method: .get, queryParameters: request @@ -31,7 +31,7 @@ struct PopUpAPIEndPoint { static func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/search/popup-stores", method: .get, queryParameters: request @@ -40,7 +40,7 @@ struct PopUpAPIEndPoint { static func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/detail", method: .get, queryParameters: request @@ -49,7 +49,7 @@ struct PopUpAPIEndPoint { static func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/comments", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift index b587531b..d36cc605 100644 --- a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift @@ -14,7 +14,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_checkNickName(with request: CheckNickNameRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/signup/check-nickname", method: .get, queryParameters: request @@ -25,7 +25,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_getCategoryList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/signup/categories", method: .get ) @@ -36,7 +36,7 @@ struct SignUpAPIEndpoint { /// - Returns: RequestEndpoint static func signUp_trySignUp(with request: SignUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/signup", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift index 142d7350..4916c847 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift @@ -13,7 +13,7 @@ struct UserAPIEndPoint { static func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .post, queryParameters: request @@ -22,7 +22,7 @@ struct UserAPIEndPoint { static func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct UserAPIEndPoint { static func postCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/likes", method: .post, queryParameters: request @@ -40,7 +40,7 @@ struct UserAPIEndPoint { static func deleteCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/likes", method: .delete, queryParameters: request @@ -49,7 +49,7 @@ struct UserAPIEndPoint { static func postUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/block", method: .post, queryParameters: request @@ -58,7 +58,7 @@ struct UserAPIEndPoint { static func deleteUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/unblock", method: .delete, queryParameters: request @@ -67,7 +67,7 @@ struct UserAPIEndPoint { static func getOtherUserCommentPopUpList(request: GetOtherUserCommentListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/\(request.commenterId ?? "")/comments", method: .get, queryParameters: request @@ -76,7 +76,7 @@ struct UserAPIEndPoint { static func getMyPage() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/my-page", method: .get ) @@ -84,7 +84,7 @@ struct UserAPIEndPoint { static func postLogout() -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/logout", method: .post ) @@ -92,7 +92,7 @@ struct UserAPIEndPoint { static func getWithdrawlList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/withdrawl/surveys", method: .get ) @@ -100,7 +100,7 @@ struct UserAPIEndPoint { static func postWithdrawl(request: PostWithdrawlListRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/delete", method: .post, bodyParameters: request @@ -109,7 +109,7 @@ struct UserAPIEndPoint { static func getMyProfile() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/profiles", method: .get ) @@ -117,7 +117,7 @@ struct UserAPIEndPoint { static func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/tailored-info", method: .put, bodyParameters: request @@ -126,7 +126,7 @@ struct UserAPIEndPoint { static func putUserCategory(request: PutUserCategoryRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/interests", method: .put, bodyParameters: request @@ -135,7 +135,7 @@ struct UserAPIEndPoint { static func putUserProfile(request: PutUserProfileRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/profiles", method: .put, bodyParameters: request @@ -144,7 +144,7 @@ struct UserAPIEndPoint { static func getMyCommentedPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/commented/popup", method: .get, queryParameters: request @@ -153,7 +153,7 @@ struct UserAPIEndPoint { static func getBlockUserList(request: GetBlockUserListRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/blocked", method: .get, queryParameters: request @@ -162,7 +162,7 @@ struct UserAPIEndPoint { static func getNoticeList() -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/notice/list", method: .get ) @@ -170,7 +170,7 @@ struct UserAPIEndPoint { static func getNoticeDetail(noticeID: Int64) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/notice/\(noticeID)", method: .get ) @@ -178,7 +178,7 @@ struct UserAPIEndPoint { static func getRecentPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/recent-popupstores", method: .get, queryParameters: request @@ -187,7 +187,7 @@ struct UserAPIEndPoint { static func getBookmarkPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift index 556bf5d2..e6e12f83 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift @@ -11,7 +11,7 @@ struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/files/upload-preSignedUrl", method: .post, bodyParameters: request @@ -21,7 +21,7 @@ struct PreSignedAPIEndPoint { static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/files/download-preSignedUrl", method: .post, bodyParameters: request @@ -31,7 +31,7 @@ struct PreSignedAPIEndPoint { static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint { Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug) return RequestEndpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/files/delete", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift index 61665b4e..b1a6912f 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift @@ -291,7 +291,7 @@ extension PreSignedService { } } func fullImageURL(from filePath: String) -> URL? { - let baseURL = Secrets.popPoolS3BaseURL + let baseURL = KeyPath.popPoolS3BaseURL // URL 인코딩 처리를 더 엄격하게 guard let encodedPath = filePath diff --git a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift index 46e8d56c..ed2815db 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift @@ -83,7 +83,7 @@ final class AdminStoreCell: UITableViewCell { statusChip.text = "운영" // mainImageUrl에서 baseURL 부분 제거 - let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") + let imagePath = store.mainImageUrl.replacingOccurrences(of: KeyPath.popPoolS3BaseURL, with: "") Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift index 66806958..5c4a4bdc 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift @@ -26,7 +26,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/locations/popup-stores", method: .get, queryParameters: params @@ -44,7 +44,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/locations/search", method: .get, queryParameters: params diff --git a/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift b/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift index 2f9a6433..b5ba885d 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/Remote/AdminAPIEndpoint.swift @@ -14,7 +14,7 @@ struct AdminAPIEndpoint { size: size ) return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/popup-stores/list", method: .get, queryParameters: params @@ -26,7 +26,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/popup-stores", method: .get, queryParameters: ["popUpStoreId": id] @@ -38,7 +38,7 @@ struct AdminAPIEndpoint { request: CreatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/popup-stores", method: .post, bodyParameters: request @@ -50,7 +50,7 @@ struct AdminAPIEndpoint { request: UpdatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/popup-stores", method: .put, bodyParameters: request @@ -62,7 +62,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/popup-stores", method: .delete, queryParameters: ["popUpStoreId": id] @@ -74,7 +74,7 @@ struct AdminAPIEndpoint { request: CreateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/notice", method: .post, bodyParameters: request @@ -86,7 +86,7 @@ struct AdminAPIEndpoint { request: UpdateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/notice/\(id)", method: .put, bodyParameters: request @@ -97,7 +97,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/admin/notice/\(id)", method: .delete ) diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index 03de32eb..a74655f3 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -15,7 +15,7 @@ extension UIImageView { self.image = UIImage(named: "image_default") return } - let imageURLString = Secrets.popPoolS3BaseURL + path + let imageURLString = KeyPath.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) self.kf.setImage(with: imageURL) { result in @@ -35,7 +35,7 @@ extension UIImageView { completion() return } - let imageURLString = Secrets.popPoolS3BaseURL + path + let imageURLString = KeyPath.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) self.kf.setImage(with: imageURL) { result in diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift index b9a7c5ea..430b5a91 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift @@ -13,7 +13,7 @@ struct FindDirectionEndPoint { popUpStoreId: Int64 ) -> Endpoint { return Endpoint( - baseURL: Secrets.popPoolBaseURL, + baseURL: KeyPath.popPoolBaseURL, path: "/popup/\(popUpStoreId)/directions", method: .get ) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 3b910a9b..61e5c3d8 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -381,7 +381,7 @@ final class DetailReactor: Reactor { func showSharedBoard(controller: BaseViewController) { let storeName = titleSection.inputDataList.first?.title ?? "" - let imagePath = Secrets.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") + let imagePath = KeyPath.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), From 334a4a3a0f0eb7b6a1f7873e176e5fac2cff1af8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 00:16:23 +0900 Subject: [PATCH 045/393] =?UTF-8?q?chore/#93:=20CI=20=ED=83=9C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 몇몇 이모지가 생각보다 촌스러워서 수정함 --- .github/workflows/ios-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ios-ci.yml index 98f1a3a9..8ea61826 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ios-ci.yml @@ -1,4 +1,4 @@ -name: 📱 iOS CI +name: Run CI on: pull_request: @@ -14,10 +14,10 @@ jobs: if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 steps: - - name: ✅ Checkout Repository # 저장소 코드 체크아웃 + - name: Checkout Repository # 저장소 코드 체크아웃 uses: actions/checkout@v4 - - name: 🧾 Generate Debug.xcconfig + - name: ⚙️ Generate xcconfig run: | cat < Poppool/Poppool/Resource/Debug.xcconfig KAKAO_AUTH_APP_KEY=${{ secrets.KAKAO_AUTH_APP_KEY }} @@ -27,7 +27,7 @@ jobs: POPPOOL_API_KEY=${{ secrets.POPPOOL_API_KEY }} EOF - - name: 🔨 Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 + - name: 🛠️ Select Xcode 16.2 # Xcode 16.2 버전 사용 설정 run: sudo xcode-select -s /Applications/Xcode_16.2.app - name: ⬇️ Install SwiftLint # SwiftLint 설치 From 2253ee840d9772fe590e4470ba8d70e1ac071a70 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 00:19:11 +0900 Subject: [PATCH 046/393] =?UTF-8?q?test/#93:=20CI=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=9E=84=EC=8B=9C=20?= =?UTF-8?q?swiftlint=EB=A3=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/.swiftlint.yml | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/Poppool/.swiftlint.yml b/Poppool/.swiftlint.yml index 0686d111..9e132e30 100644 --- a/Poppool/.swiftlint.yml +++ b/Poppool/.swiftlint.yml @@ -1,5 +1,14 @@ # 기본 활성화된 룰 중에 비활성화할 룰을 지정 disabled_rules: + - type_body_length + - function_body_length + - file_length + - line_length + - force_cast + - force_try + - duplicate_conditions + - identifier_name + - cyclomatic_complexity - redundant_optional_initialization # 기본(default) 룰이 아닌 룰들을 활성화 @@ -8,14 +17,3 @@ opt_in_rules: - direct_return - file_header - weak_delegate - -# 기본 활성화된 룰 중에 조건을 변경할 룰 -file_length: 400 - -function_body_length: - warning: 100 - error: 150 - -cyclomatic_complexity: - warning: 15 - error: 30 From 3d31395b8f680281fb6966ad9ca7991079697e23 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 00:47:10 +0900 Subject: [PATCH 047/393] =?UTF-8?q?refactor/#93:=20CI,=20AutoCorrect=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{ios-ci.yml => ci.yml} | 49 ++++++++++++++++++--- .github/workflows/swiftlint-autocorrect.yml | 47 -------------------- 2 files changed, 43 insertions(+), 53 deletions(-) rename .github/workflows/{ios-ci.yml => ci.yml} (61%) delete mode 100644 .github/workflows/swiftlint-autocorrect.yml diff --git a/.github/workflows/ios-ci.yml b/.github/workflows/ci.yml similarity index 61% rename from .github/workflows/ios-ci.yml rename to .github/workflows/ci.yml index 8ea61826..25fe74ca 100644 --- a/.github/workflows/ios-ci.yml +++ b/.github/workflows/ci.yml @@ -1,16 +1,53 @@ -name: Run CI +name: CI on: pull_request: - branches: [dev] # dev 브랜치로의 PR에 대해 실행 + branches: [dev] push: - branches: [dev] # dev 브랜치로의 push에 대해 실행 + branches: [dev] jobs: + autocorrect: + name: 🤖 Autocorrect Workflow + runs-on: macos-15 # 최신 macOS 15 환경에서 실행 + if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 + + steps: + - name: 📁 Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + + - name: 🛠️ Set up Xcode # Xcode 16.2 선택 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: ⬇️ Install SwiftLint # SwiftLint 설치 + run: brew install swiftlint + + - name: 🎨 Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 + run: swiftlint autocorrect + + - name: 🚀 Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + BRANCH_NAME="${GITHUB_REF_NAME}" + if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then + ISSUE_NUMBER="${BASH_REMATCH[1]}" + else + ISSUE_NUMBER="#??" + fi + + if [ -n "$(git status --porcelain)" ]; then + git add . + git commit -m "style/${ISSUE_NUMBER}: Apply SwiftLint autocorrect" + git push + else + echo "No changes to commit" + fi + build: - name: Build & Lint Workflow + name: 🏗️ Build Workflow runs-on: macos-15 # 최신 macOS 15 환경에서 실행 - timeout-minutes: 15 # 최대 실행 시간 15분 설정 if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 steps: @@ -32,7 +69,7 @@ jobs: - name: ⬇️ Install SwiftLint # SwiftLint 설치 run: brew install swiftlint - + - name: 🎨 Run SwiftLint # SwiftLint 코드 스타일 검사 실행 run: swiftlint diff --git a/.github/workflows/swiftlint-autocorrect.yml b/.github/workflows/swiftlint-autocorrect.yml deleted file mode 100644 index 895d5868..00000000 --- a/.github/workflows/swiftlint-autocorrect.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: 🔧 Swiftlint Autocorrect - -on: - pull_request: - types: [opened] - branches: ['dev/**'] - -jobs: - autocorrect: - name: SwiftLint Autocorrect - runs-on: macos-15 - if: github.actor != 'github-actions[bot]' # 커밋 무한 루프 방지를 위해 봇 커밋은 무시 - - steps: - - name: 📁 Checkout Repository # 저장소 코드 체크아웃 - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - - - name: 🔨 Set up Xcode # Xcode 16.2 선택 - run: sudo xcode-select -s /Applications/Xcode_16.2.app - - - name: ⬇️ Install SwiftLint # SwiftLint 설치 - run: brew install swiftlint - - - name: 🔧 Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 - run: swiftlint autocorrect - - - name: 🚀 Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - - BRANCH_NAME="${GITHUB_REF_NAME}" - if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then - ISSUE_NUMBER="${BASH_REMATCH[1]}" - else - ISSUE_NUMBER="#??" - fi - - if [ -n "$(git status --porcelain)" ]; then - git add . - git commit -m "style/${ISSUE_NUMBER}: Apply SwiftLint autocorrect" - git push - else - echo "No changes to commit" - fi From 05e9118503a799c05c8545f8f9a336aeedd14dda Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 00:51:51 +0900 Subject: [PATCH 048/393] =?UTF-8?q?fix/#93:=20=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=88=98=EC=A0=95=20=EB=AA=85=EB=A0=B9?= =?UTF-8?q?=EC=96=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25fe74ca..e8d391f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: run: brew install swiftlint - name: 🎨 Run SwiftLint Autocorrect # SwiftLint 자동 수정 실행 - run: swiftlint autocorrect + run: swiftlint --fix - name: 🚀 Commit and Push Changes # 변경 사항 자동 커밋 및 푸시 run: | From eac393c7fb41555cc5d2e86b3434b5f15e51d22a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 01:02:02 +0900 Subject: [PATCH 049/393] =?UTF-8?q?fix/#93:=20=EB=A6=B0=ED=8A=B8=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=88=98=EC=A0=95=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EB=B9=8C=EB=93=9C=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EA=B2=BD=EB=A1=9C=20=ED=83=90=EC=A7=80=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 --- .github/workflows/ci.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8d391f6..14c9fb25 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 steps: - - name: 📁 Checkout Repository # 저장소 코드 체크아웃 + - name: Checkout Repository # 저장소 코드 체크아웃 uses: actions/checkout@v4 - name: 🛠️ Set up Xcode # Xcode 16.2 선택 @@ -30,16 +30,18 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - BRANCH_NAME="${GITHUB_REF_NAME}" - if [[ "$BRANCH_NAME" =~ (#([0-9]+)) ]]; then + git checkout "${GITHUB_HEAD_REF}" + + BRANCH_NAME="${GITHUB_HEAD_REF}" + if [[ "$BRANCH_NAME" =~ ([0-9]+) ]]; then ISSUE_NUMBER="${BASH_REMATCH[1]}" else - ISSUE_NUMBER="#??" + ISSUE_NUMBER="" fi if [ -n "$(git status --porcelain)" ]; then git add . - git commit -m "style/${ISSUE_NUMBER}: Apply SwiftLint autocorrect" + git commit -m "style/#${ISSUE_NUMBER}: Apply SwiftLint autocorrect" git push else echo "No changes to commit" @@ -92,7 +94,8 @@ jobs: - name: 🏗️ Build the project # 자동 검지된 Scheme과 Simulator로 빌드 수행 run: | + WORKSPACE=$(find . -name "*.xcworkspace" | head -n 1) xcodebuild -scheme "${{ steps.detect_scheme.outputs.scheme }}" \ - -workspace Poppool.xcworkspace \ + -workspace "$WORKSPACE" \ -destination "platform=iOS Simulator,id=${{ steps.detect_latest_simulator.outputs.sim_udid }}" \ clean build | xcpretty From ee45fe3ef5a44ce7d8cfdc82eb8539cb8f8069d2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 01:07:23 +0900 Subject: [PATCH 050/393] =?UTF-8?q?fix/#93:=20=EC=9D=B4=EC=8A=88=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B2=80=EC=B6=9C=20=EC=A0=95=EA=B7=9C=ED=91=9C?= =?UTF-8?q?=ED=98=84=EC=8B=9D=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EB=A6=B0?= =?UTF-8?q?=ED=8A=B8=20=EC=9E=90=EB=8F=99=EC=88=98=EC=A0=95=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14c9fb25..a11ad67a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,10 +30,11 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" + git fetch origin "${GITHUB_HEAD_REF}:${GITHUB_HEAD_REF}" git checkout "${GITHUB_HEAD_REF}" BRANCH_NAME="${GITHUB_HEAD_REF}" - if [[ "$BRANCH_NAME" =~ ([0-9]+) ]]; then + if [[ "$BRANCH_NAME" =~ \#([0-9]+) ]]; then ISSUE_NUMBER="${BASH_REMATCH[1]}" else ISSUE_NUMBER="" From 50f48c8a9657571a8c52ca04021220fe6a719070 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 01:09:34 +0900 Subject: [PATCH 051/393] =?UTF-8?q?fix/#93:=20=EC=9E=90=EB=8F=99=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=BB=A4=EB=B0=8B=20=EC=95=A1=EC=85=98=20No=20upst?= =?UTF-8?q?ream=20branch=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a11ad67a..01cd77eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: if [ -n "$(git status --porcelain)" ]; then git add . git commit -m "style/#${ISSUE_NUMBER}: Apply SwiftLint autocorrect" - git push + git push --set-upstream origin "${GITHUB_HEAD_REF}" else echo "No changes to commit" fi From 13b2b6431243501e75f25765d0baf34a16fe3ec5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 2 Apr 2025 16:10:06 +0000 Subject: [PATCH 052/393] style/#93: Apply SwiftLint autocorrect --- Poppool/Poppool/Application/AppDelegate.swift | 9 +- .../Poppool/Application/SceneDelegate.swift | 3 +- .../Application/TestViewController.swift | 49 ++++---- .../Network/AuthAPI/AuthAPIEndPoint.swift | 6 +- .../CommentAPI/CommentAPIEndPoint.swift | 6 +- .../Network/HomeAPI/HomeAPIEndpoint.swift | 8 +- .../ResponseDTO/GetHomeInfoResponseDTO.swift | 1 - .../Network/PopUpAPI/PopUpAPIEndPoint.swift | 10 +- .../GetSearchPopUpListResponseDTO.swift | 2 - .../Network/SignUpAPI/SignUpAPIEndpoint.swift | 6 +- .../GetMyCommentedPopUpResponseDTO.swift | 1 - ...herUserCommentedPopUpListResponseDTO.swift | 3 - .../GetWithdrawlListResponseDTO.swift | 2 - .../Network/UserAPI/UserAPIEndPoint.swift | 48 ++++---- .../Repository/AuthAPIRepositoryImpl.swift | 10 +- .../Repository/CommentAPIRepository.swift | 10 +- .../Data/Repository/HomeAPIRepository.swift | 12 +- .../Repository/PopUpAPIRepositoryImpl.swift | 14 +-- .../Repository/SignUpRepositoryImpl.swift | 10 +- .../Repository/UserAPIRepositoryImpl.swift | 46 ++++---- .../Entities/GetMyCommentResponse.swift | 1 - .../Domain/Repository/AuthRepository.swift | 4 +- .../Domain/UseCase/AuthAPIUseCaseImpl.swift | 8 +- .../UseCase/CommentAPIUseCaseImpl.swift | 10 +- .../Domain/UseCase/HomeAPIUseCaseImpl.swift | 8 +- .../Domain/UseCase/PopUpAPIUseCaseImpl.swift | 14 +-- .../Domain/UseCase/SignUpAPIUseCaseImpl.swift | 4 +- .../Domain/UseCase/UserAPIUseCaseImpl.swift | 50 ++++----- .../Infrastructure/AppleLoginService.swift | 14 +-- .../Infrastructure/KakaoLoginService.swift | 28 ++--- .../Infrastructure/KeyChainService.swift | 20 ++-- .../Infrastructure/Logger/Logger.swift | 10 +- .../NetworkLayer/Common/Requestable.swift | 8 +- .../NetworkLayer/EndPoint/Endpoint.swift | 2 +- .../EndPoint/MultipartEndPoint.swift | 12 +- .../IndicatorMaker/IndicatorMaker.swift | 10 +- .../Interceptor/FormDataInterceptor.swift | 8 +- .../Interceptor/TokenInterceptor.swift | 6 +- .../NetworkLayer/Provider/Provider.swift | 8 +- .../NetworkLayer/Provider/ProviderImpl.swift | 2 +- .../PreSignedService/PreSignedService.swift | 13 +-- .../Infrastructure/UserDefaultService.swift | 14 +-- .../AdminBottomSheetView.swift | 85 +++++++------- .../AdminBottomSheetViewController.swift | 15 +-- .../Presentation/Admin/AdminReactor.swift | 2 +- .../PopUpStoreRegisterReactor.swift | 3 +- .../PopUpStoreRegisterView.swift | 6 +- .../PopUpStoreRegisterViewController.swift | 105 +++++++----------- .../Presentation/Admin/AdminStoreCell.swift | 4 +- .../Presentation/Admin/AdminView.swift | 4 +- .../Admin/AdminViewController.swift | 7 +- .../Admin/Common/DateTimePickerManager.swift | 2 +- .../Admin/Data/DTO/AdminResponseDTO.swift | 1 - .../GetAdminPopUpStoreListResponseDTO.swift | 5 +- .../Admin/Data/MapDomain/MapAPIEndpoint.swift | 3 +- .../Admin/Data/MapDomain/MapPopUpStore.swift | 5 +- .../MapDomain/Repository/MapRepository.swift | 2 - .../Data/MapDomain/UseCase/MapUseCase.swift | 3 +- .../Data/Repository/AdminRepository.swift | 9 +- .../Presentation/Admin/ImageCell.swift | 2 +- .../Admin/PopUpStoreRegisterReactor.swift | 12 +- .../Admin/PopUpStoreRegisterView.swift | 22 ++-- .../Presentation/Components/PPButton.swift | 27 +++-- .../Components/PPCancelHeaderView.swift | 12 +- .../Presentation/Components/PPLabel.swift | 6 +- .../Presentation/Components/PPPicker.swift | 22 ++-- .../PPProgressIndicator.swift | 21 ++-- .../PPProgressIndicator/PPProgressView.swift | 29 +++-- .../Components/PPReturnHeaderView.swift | 16 +-- .../Components/PPSegmentedControl.swift | 18 +-- .../Presentation/Extension/Date?+.swift | 4 +- .../Presentation/Extension/Reactive+.swift | 10 +- .../Presentation/Extension/String?+.swift | 8 +- .../Presentation/Extension/UIColor+.swift | 23 ++-- .../Presentation/Extension/UIFont+.swift | 5 +- .../Presentation/Extension/UIImage+.swift | 14 +-- .../Presentation/Extension/UIImageView+.swift | 2 +- .../Map/Common/ClusteringModels.swift | 1 - .../Presentation/Map/Common/FilterType.swift | 2 - .../Map/Common/GMSMapViewDelegateProxy.swift | 4 +- .../LocationPermissionBottomSheet.swift | 2 +- .../Map/Common/MapFilterChips.swift | 3 +- .../MapPopupCarouselView.swift | 5 +- .../MapPopupCardView/PopupCardCell.swift | 7 +- .../Map/Common/MapUtilities.swift | 3 - .../Map/CustomClusterRenderer.swift | 6 +- .../BalloonBackgroundView.swift | 10 +- .../FillterSheetView/BalloonChipCell.swift | 3 +- .../FillterSheetView/CategoryFilterView.swift | 8 +- .../FilterBottomSheetReactor.swift | 26 ++--- .../FilterBottomSheetView.swift | 31 ++---- .../FilterBottomSheetViewController.swift | 22 +--- .../Map/FillterSheetView/FilterCell.swift | 2 +- .../Map/FillterSheetView/FilterChip.swift | 2 +- .../FillterSheetView/FilterChipsView.swift | 4 +- .../Map/FillterSheetView/FilterTabsView.swift | 14 +-- .../FillterSheetView/LocationFilterView.swift | 8 +- .../MapGuideView/FindDirectionEndPoint.swift | 1 - .../FullScreenMapViewController.swift | 11 +- .../GetPopUpDirectionResponseDTO.swift | 1 - .../MapGuideView/MapDirectionRepository.swift | 3 +- .../MapGuideView/MapGuideReactor.swift | 2 +- .../MapGuideView/MapGuideViewController.swift | 8 +- .../Presentation/Map/MapStoreCard.swift | 16 +-- .../Presentation/Map/MapView/MapMarker.swift | 7 +- .../Presentation/Map/MapView/MapReactor.swift | 8 +- .../Map/MapView/MapSearchInput.swift | 2 +- .../Presentation/Map/MapView/MapView.swift | 9 +- .../Map/MapView/MapViewController.swift | 64 ++--------- .../Map/MapView/MarkerTooltipView.swift | 2 +- .../Map/MicroClusterMarkerView.swift | 1 - .../Map/StoreListPanelLayout.swift | 8 +- .../Map/StoreListView/StoreListCell.swift | 8 +- .../StoreListView/StoreListHeaderView.swift | 10 +- .../StoreListView/StoreListPanelLayout.swift | 3 - .../Map/StoreListView/StoreListReactor.swift | 22 +--- .../Map/StoreListView/StoreListView.swift | 2 +- .../StoreListViewController.swift | 19 ++-- .../Presentation/Map/TestViewController.swift | 49 ++++---- .../CommentCheck/CommentCheckController.swift | 14 +-- .../CommentCheck/CommentCheckReactor.swift | 20 ++-- .../CommentCheck/CommentCheckView.swift | 29 +++-- .../CommentDetailController.swift | 41 ++++--- .../CommentDetail/CommentDetailReactor.swift | 36 +++--- .../CommentDetailContentSection.swift | 14 +-- .../CommentDetailContentSectionCell.swift | 12 +- .../View/CommentDetailImageSection.swift | 12 +- .../View/CommentDetailView.swift | 30 +++-- .../CommentMyMenuController.swift | 16 +-- .../CommentMyMenu/CommentMyMenuReactor.swift | 18 +-- .../CommentMyMenu/CommentMyMenuView.swift | 24 ++-- .../CommentUserInfoController.swift | 18 +-- .../CommentUserInfoReactor.swift | 18 +-- .../CommentUserInfo/CommentUserInfoView.swift | 24 ++-- .../CommentList/CommentListController.swift | 34 +++--- .../CommentList/CommentListReactor.swift | 38 +++---- .../CommentListTitleSection.swift | 15 ++- .../CommentListTitleSectionCell.swift | 10 +- .../CommentList/View/CommentListView.swift | 18 ++- .../CommentSelectedController.swift | 16 +-- .../CommentSelectedReactor.swift | 18 +-- .../CommentSelected/CommentSelectedView.swift | 24 ++-- .../CommentUserBlockController.swift | 16 +-- .../CommentUserBlockReactor.swift | 20 ++-- .../CommentUserBlockView.swift | 29 +++-- .../InstaCommentAddController.swift | 24 ++-- .../InstaComment/InstaCommentAddReactor.swift | 30 ++--- .../View/InstaCommentAddView.swift | 22 ++-- .../InstaGuideChildSection.swift | 12 +- .../InstaGuideChildSectionCell.swift | 24 ++-- .../InstaGuideSection/InstaGuideSection.swift | 17 ++- .../InstaGuideSectionCell.swift | 51 +++++---- .../NormalCommentAddController.swift | 38 +++---- .../NormalCommentAddReactor.swift | 38 +++---- .../AddCommentDescriptionSection.swift | 14 +-- .../AddCommentDescriptionSectionCell.swift | 12 +- .../AddCommentImageSection.swift | 12 +- .../AddCommentImageSectionCell.swift | 31 +++--- .../AddCommentSection/AddCommentSection.swift | 12 +- .../AddCommentSectionCell.swift | 56 +++++----- .../AddCommentTitleSection.swift | 14 +-- .../AddCommentTitleSectionCell.swift | 15 ++- .../View/NormalCommentAddView.swift | 21 ++-- .../NormalCommentEditController.swift | 42 +++---- .../NormalCommentEditReactor.swift | 48 ++++---- .../NormalCommentEditView.swift | 21 ++-- .../OtherUserCommentController.swift | 39 ++++--- .../OtherUserCommentReactor.swift | 24 ++-- .../OtherUserCommentSection.swift | 12 +- .../OtherUserCommentSectionCell.swift | 34 +++--- .../View/OtherUserCommentView.swift | 12 +- .../Scene/Detail/DetailController.swift | 7 +- .../Scene/Detail/DetailReactor.swift | 74 ++++++------ .../DetailCommentImageCell.swift | 12 +- .../DetailCommentProfileView.swift | 21 ++-- .../DetailCommentSection.swift | 14 +-- .../DetailCommentSectionCell.swift | 83 +++++++------- .../DetailCommentTitleSection.swift | 14 +-- .../DetailCommentTitleSectionCell.swift | 23 ++-- .../DetailContentSection.swift | 14 +-- .../DetailContentSectionCell.swift | 25 ++--- .../DetailEmptyCommetSection.swift | 14 +-- .../DetailEmptyCommetSectionCell.swift | 20 ++-- .../DetailInfoSection/DetailInfoSection.swift | 14 +-- .../DetailInfoSectionCell.swift | 75 ++++++------- .../DetailSimilarSection.swift | 14 +-- .../DetailSimilarSectionCell.swift | 47 ++++---- .../DetailTitleSection.swift | 14 +-- .../DetailTitleSectionCell.swift | 25 ++--- .../Scene/Detail/View/DetailView.swift | 18 ++- .../Scene/Home/List/HomeListController.swift | 42 +++---- .../Scene/Home/List/HomeListReactor.swift | 32 +++--- .../Scene/Home/List/HomePopUpType.swift | 4 +- .../Home/List/View/HomeCardGridSection.swift | 12 +- .../Scene/Home/List/View/HomeListView.swift | 18 ++- .../Scene/Home/Main/HomeController.swift | 60 +++++----- .../Scene/Home/Main/HomeReactor.swift | 44 ++++---- .../HomeCardSection/HomeCardSection.swift | 12 +- .../HomeCardSection/HomeCardSectionCell.swift | 49 ++++---- .../Scene/Home/Main/View/HomeHeaderView.swift | 22 ++-- .../HomePopularCardSection.swift | 16 +-- .../HomePopularCardSectionCell.swift | 40 +++---- .../HomeTitleSection/HomeTitleSection.swift | 17 ++- .../HomeTitleSectionCell.swift | 28 +++-- .../Scene/Home/Main/View/HomeView.swift | 8 +- .../ImageBannerChildSection.swift | 12 +- .../ImageBannerChildSectionCell.swift | 14 +-- .../ImageBannerSection.swift | 17 ++- .../ImageBannerSectionCell.swift | 76 ++++++------- .../SectionBackGroundDecorationView.swift | 4 +- .../View/SpacingSection/SpacingSection.swift | 17 ++- .../SpacingSection/SpacingSectionCell.swift | 14 +-- .../ImageDetail/ImageDetailController.swift | 16 +-- .../ImageDetail/ImageDetailReactor.swift | 18 +-- .../Scene/ImageDetail/ImageDetailView.swift | 8 +- .../Scene/Login/LastLoginView.swift | 71 ++++++------ .../Scene/Login/Main/LoginController.swift | 22 ++-- .../Scene/Login/Main/LoginReactor.swift | 30 ++--- .../Scene/Login/Main/LoginView.swift | 42 ++++--- .../Scene/Login/Sub/SubLoginController.swift | 20 ++-- .../Scene/Login/Sub/SubLoginReactor.swift | 26 ++--- .../Scene/Login/Sub/SubLoginView.swift | 42 ++++--- .../Block/BlockUserManageController.swift | 26 ++--- .../MyPage/Block/BlockUserManageReactor.swift | 26 ++--- .../BlockUserListSection.swift | 12 +- .../BlockUserListSectionCell.swift | 29 +++-- .../Block/View/BlockUserManageView.swift | 17 ++- .../Main/MyPageBookmarkController.swift | 56 +++++----- .../Bookmark/Main/MyPageBookmarkReactor.swift | 26 ++--- .../Bookmark/Main/MyPageBookmarkView.swift | 29 +++-- .../Bookmark/Main/View/CountButtonView.swift | 24 ++-- .../PopUpCardSection/PopUpCardSection.swift | 12 +- .../PopUpCardSectionCell.swift | 53 +++++---- .../View/PopUpCardSection/PopUpCardView.swift | 58 +++++----- ...BookMarkPopUpViewTypeModalController.swift | 23 ++-- .../BookMarkPopUpViewTypeModalReactor.swift | 19 ++-- .../BookMarkPopUpViewTypeModalView.swift | 31 +++--- .../Scene/MyPage/FAQ/FAQController.swift | 26 ++--- .../Scene/MyPage/FAQ/FAQReactor.swift | 32 +++--- .../FAQDropdownSection.swift | 14 +-- .../FAQDropdownSectionCell.swift | 34 +++--- .../Scene/MyPage/FAQ/View/FAQView.swift | 12 +- .../Scene/MyPage/Main/MyPageController.swift | 67 ++++++----- .../Scene/MyPage/Main/MyPageReactor.swift | 10 +- .../MyPageCommentSection.swift | 12 +- .../MyPageCommentSectionCell.swift | 51 +++++---- .../MyPageListSection/MyPageListSection.swift | 14 +-- .../MyPageListSectionCell.swift | 25 ++--- .../MyPageLogoutSection.swift | 14 +-- .../MyPageLogoutSectionCell.swift | 17 ++- .../MyPageMyCommentTitleSection.swift | 14 +-- .../MyPageMyCommentTitleSectionCell.swift | 32 +++--- .../MyPageProfileSection.swift | 17 ++- .../MyPageProfileSectionCell.swift | 103 ++++++++--------- .../Scene/MyPage/Main/View/MyPageView.swift | 6 +- .../MyComment/Main/MyCommentController.swift | 33 +++--- .../MyComment/Main/MyCommentReactor.swift | 26 ++--- .../ListCountButtonSection.swift | 14 +-- .../ListCountButtonSectionCell.swift | 34 +++--- .../MyComment/Main/View/MyCommentView.swift | 12 +- .../MyCommentedPopUpGridSection.swift | 14 +-- .../MyCommentedPopUpGridSectionCell.swift | 29 +++-- .../MyCommentSortedModalController.swift | 23 ++-- .../MyCommentSortedModalReactor.swift | 19 ++-- .../MyCommentSortedModalView.swift | 31 +++--- .../Detail/MyPageNoticeDetailController.swift | 14 +-- .../Detail/MyPageNoticeDetailReactor.swift | 18 +-- .../Detail/MyPageNoticeDetailView.swift | 21 ++-- .../Notice/List/MyPageNoticeController.swift | 34 +++--- .../Notice/List/MyPageNoticeReactor.swift | 21 ++-- .../Notice/List/View/MyPageNoticeView.swift | 14 +-- .../NoticeListSection/NoticeListSection.swift | 14 +-- .../NoticeListSectionCell.swift | 22 ++-- .../CategoryEditModalController.swift | 33 +++--- .../CategoryEditModalReactor.swift | 26 ++--- .../CategoryEditModalView.swift | 26 ++--- .../InfoEditModalController.swift | 22 ++-- .../InfoEditModal/InfoEditModalReactor.swift | 23 ++-- .../InfoEditModal/InfoEditModalView.swift | 50 ++++----- .../Main/ProfileEditController.swift | 92 ++++++++------- .../ProfileEdit/Main/ProfileEditReactor.swift | 52 ++++----- .../Main/View/ProfileEditListButton.swift | 19 ++-- .../Main/View/ProfileEditView.swift | 49 ++++---- .../Recent/MyPageRecentController.swift | 36 +++--- .../MyPage/Recent/MyPageRecentReactor.swift | 26 ++--- .../MyPage/Recent/View/MyPageRecentView.swift | 10 +- .../RecentPopUpSection.swift | 15 ++- .../MyPage/Terms/MyPageTermsController.swift | 37 +++--- .../MyPage/Terms/MyPageTermsReactor.swift | 30 ++--- .../WithdrawlCheckModalController.swift | 20 ++-- .../WithdrawlCheckModalReactor.swift | 20 ++-- .../CheckModal/WithdrawlCheckModalView.swift | 41 ++++--- .../WithdrawlCompleteController.swift | 8 +- .../Complete/WithdrawlCompleteView.swift | 23 ++-- .../View/WithdrawlCheckSection.swift | 12 +- .../View/WithdrawlCheckSectionCell.swift | 54 +++++---- .../View/WithdrawlReasonView.swift | 31 +++--- .../WithdrawlReasonController.swift | 40 +++---- .../WithdrawlReasonReactor.swift | 26 ++--- .../AfterSearch/SearchResultController.swift | 24 ++-- .../AfterSearch/SearchResultReactor.swift | 29 +++-- .../SearchResultCountSection.swift | 14 +-- .../SearchResultCountSectionCell.swift | 16 +-- .../AfterSearch/View/SearchResultView.swift | 15 ++- .../BeforeSearch/SearchController.swift | 34 +++--- .../Search/BeforeSearch/SearchReactor.swift | 46 ++++---- .../CancelableTagSection.swift | 12 +- .../CancelableTagSectionCell.swift | 32 +++--- .../SearchCountTitleSection.swift | 14 +-- .../SearchCountTitleSectionCell.swift | 34 +++--- .../SearchTitleSection.swift | 14 +-- .../SearchTitleSectionCell.swift | 21 ++-- .../Search/BeforeSearch/View/SearchView.swift | 11 +- .../SearchCategoryController.swift | 28 ++--- .../SearchCategoryReactor.swift | 20 ++-- .../SearchCategoryView.swift | 29 +++-- .../Search/Main/SearchMainController.swift | 39 ++++--- .../Scene/Search/Main/SearchMainReactor.swift | 16 +-- .../Scene/Search/Main/SearchMainView.swift | 26 ++--- .../SearchSortedController.swift | 20 ++-- .../SearchSortedReactor.swift | 20 ++-- .../SortedController/SearchSortedView.swift | 50 ++++----- .../SignUp/Main/SignUpMainController.swift | 52 +++++---- .../Scene/SignUp/Main/SignUpMainReactor.swift | 28 ++--- .../SignUp/Main/View/SignUpMainView.swift | 10 +- .../SignUpCompleteController.swift | 16 +-- .../SignUpCompleteReactor.swift | 16 +-- .../SignUpComplete/SignUpCompleteView.swift | 51 ++++----- .../SignUp/Step1/SignUpStep1Controller.swift | 24 ++-- .../SignUp/Step1/SignUpStep1Reactor.swift | 22 ++-- .../Step1/View/SignUpCheckBoxButton.swift | 34 +++--- .../SignUp/Step1/View/SignUpStep1View.swift | 30 +++-- .../SignUp/Step1/View/SignUpTermsView.swift | 35 +++--- .../Scene/SignUp/Step2/IntroState.swift | 10 +- .../Scene/SignUp/Step2/NickNameState.swift | 24 ++-- .../SignUp/Step2/SignUpStep2Controller.swift | 35 +++--- .../SignUp/Step2/SignUpStep2Reactor.swift | 28 +++-- .../Scene/SignUp/Step2/SignUpStep2View.swift | 36 +++--- .../SignUp/Step3/SignUpStep3Controller.swift | 29 +++-- .../SignUp/Step3/SignUpStep3Reactor.swift | 26 ++--- .../SignUp/Step3/View/SignUpStep3View.swift | 44 ++++---- .../Step3/View/TagSection/TagSection.swift | 14 +-- .../View/TagSection/TagSectionCell.swift | 17 ++- .../AgeSelectedController.swift | 16 +-- .../AgeSelectedModal/AgeSelectedReactor.swift | 18 +-- .../AgeSelectedModal/AgeSelectedView.swift | 30 +++-- .../Step4/Main/SignUpStep4Controller.swift | 14 +-- .../Step4/Main/SignUpStep4Reactor.swift | 18 +-- .../Step4/Main/View/AgeSelectedButton.swift | 31 +++--- .../Step4/Main/View/SignUpStep4View.swift | 62 +++++------ .../TermsDetail/TermsDetailController.swift | 14 +-- .../SignUp/TermsDetail/TermsDetailView.swift | 18 ++- .../Scene/Splash/SplashController.swift | 20 ++-- .../Scene/Splash/View/SplashView.swift | 12 +- .../TabbarController/TabbarController.swift | 58 +++++----- .../Controllers/BaseTabmanController.swift | 8 +- .../Controllers/BaseViewController.swift | 16 +-- .../Sectionable/SectionDecorationItem.swift | 14 +-- .../SectionSupplementaryItem.swift | 20 ++-- .../Interfaces/Sectionable/Sectionable.swift | 57 +++++----- .../Utills/ToastMaker/BookMarkToastView.swift | 16 +-- .../Utills/ToastMaker/ToastMaker.swift | 33 +++--- .../Utills/ToastMaker/ToastView.swift | 20 ++-- 363 files changed, 3736 insertions(+), 4122 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 200881dc..ccd55bea 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,8 +1,8 @@ import UIKit -import KakaoSDKCommon -import GoogleMaps import CoreLocation +import GoogleMaps +import KakaoSDKCommon @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -10,10 +10,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { KakaoSDK.initSDK(appKey: KeyPath.kakaoAuthAppKey) GMSServices.provideAPIKey(KeyPath.popPoolAPIKey) - + let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() // 권한 요청 초기화 - + return true } @@ -22,4 +22,3 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } } - diff --git a/Poppool/Poppool/Application/SceneDelegate.swift b/Poppool/Poppool/Application/SceneDelegate.swift index a23213ca..5a7be40e 100644 --- a/Poppool/Poppool/Application/SceneDelegate.swift +++ b/Poppool/Poppool/Application/SceneDelegate.swift @@ -10,7 +10,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { static let appDidBecomeActive = PublishSubject() static let appDidDisconnect = PublishSubject() private let disposeBag = DisposeBag() - + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) @@ -43,4 +43,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } } - diff --git a/Poppool/Poppool/Application/TestViewController.swift b/Poppool/Poppool/Application/TestViewController.swift index c6847136..528e61d1 100644 --- a/Poppool/Poppool/Application/TestViewController.swift +++ b/Poppool/Poppool/Application/TestViewController.swift @@ -7,87 +7,86 @@ import UIKit -import SnapKit -import RxSwift -import RxGesture import RxCocoa +import RxGesture +import RxSwift +import SnapKit class TestViewController: UIViewController { - + private let topView: UIView = { let view = UIView() view.backgroundColor = .w100 view.alpha = 0 return view }() - + private let topViewLabel: UILabel = { let label = UILabel() label.text = "Top View Label" return label }() - + private let bottomView: UIView = { let view = UIView() view.backgroundColor = .w100 return view }() - + private let gestureBar: UIView = { let view = UIView() view.backgroundColor = .g200 return view }() - + private let listButton: PPButton = { - let button = PPButton(style: .secondary, text: "리스트 버튼") - return button + return PPButton(style: .secondary, text: "리스트 버튼") }() - + private let disposeBag = DisposeBag() - + private var bottomViewTopConstraints: Constraint? - + enum ModalState { case top case middle case bottom } - + var modalState: ModalState = .bottom - + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .blue setUpConstratins() bind() } - + func setUpConstratins() { view.addSubview(listButton) listButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(50) } - + view.addSubview(topView) topView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide.snp.top).offset(104) } - + topView.addSubview(topViewLabel) topViewLabel.snp.makeConstraints { make in make.center.equalToSuperview() } - + view.addSubview(bottomView) bottomView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() bottomViewTopConstraints = make.top.equalTo(topView.snp.bottom).offset(700).constraint make.height.equalTo(700) } - + bottomView.addSubview(gestureBar) gestureBar.snp.makeConstraints { make in make.width.equalTo(50) @@ -96,7 +95,7 @@ class TestViewController: UIViewController { make.centerX.equalToSuperview() } } - + func bind() { listButton.rx.tap .withUnretained(self) @@ -110,11 +109,11 @@ class TestViewController: UIViewController { } } .disposed(by: disposeBag) - + gestureBar.rx.swipeGesture(.up) .skip(1) .withUnretained(self) - .subscribe { (owner, gesture) in + .subscribe { (owner, _) in print("swipe up") UIView.animate(withDuration: 0.3) { owner.bottomViewTopConstraints?.update(offset: 0) @@ -124,11 +123,11 @@ class TestViewController: UIViewController { } } .disposed(by: disposeBag) - + gestureBar.rx.swipeGesture(.down) .skip(1) .withUnretained(self) - .subscribe { (owner, gesture) in + .subscribe { (owner, _) in print("swipe down") switch owner.modalState { case .top: diff --git a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift index 860fb485..4647490b 100644 --- a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift @@ -8,9 +8,9 @@ import Foundation struct AuthAPIEndPoint { - + // MARK: - Auth API - + /// 로그인을 시도합니다. /// - Parameters: /// - userCredential: 사용자 자격 증명 @@ -25,7 +25,7 @@ struct AuthAPIEndPoint { headers: ["Content-Type": "application/json"] ) } - + static func postTokenReissue() -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, diff --git a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift index 6c91aefb..02f4d689 100644 --- a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift @@ -10,7 +10,7 @@ import Foundation import RxSwift struct CommentAPIEndPoint { - + static func postCommentAdd(request: PostCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -19,7 +19,7 @@ struct CommentAPIEndPoint { bodyParameters: request ) } - + static func deleteComment(request: DeleteCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -28,7 +28,7 @@ struct CommentAPIEndPoint { queryParameters: request ) } - + static func editComment(request: PutCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, diff --git a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift index 4140756c..30e19486 100644 --- a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift @@ -8,7 +8,7 @@ import Foundation struct HomeAPIEndpoint { - + static func fetchHome( request: SortedRequestDTO ) -> Endpoint { @@ -19,7 +19,7 @@ struct HomeAPIEndpoint { queryParameters: request ) } - + static func fetchPopularPopUp( request: SortedRequestDTO ) -> Endpoint { @@ -30,7 +30,7 @@ struct HomeAPIEndpoint { queryParameters: request ) } - + static func fetchNewPopUp( request: SortedRequestDTO ) -> Endpoint { @@ -41,7 +41,7 @@ struct HomeAPIEndpoint { queryParameters: request ) } - + static func fetchCustomPopUp( request: SortedRequestDTO ) -> Endpoint { diff --git a/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift b/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift index 01660653..d33857b7 100644 --- a/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift @@ -40,4 +40,3 @@ extension GetHomeInfoResponseDTO { ) } } - diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift index 6e12dd8d..788b52d8 100644 --- a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift @@ -10,7 +10,7 @@ import Foundation import RxSwift struct PopUpAPIEndPoint { - + static func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -19,7 +19,7 @@ struct PopUpAPIEndPoint { queryParameters: request ) } - + static func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -28,7 +28,7 @@ struct PopUpAPIEndPoint { queryParameters: request ) } - + static func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -37,7 +37,7 @@ struct PopUpAPIEndPoint { queryParameters: request ) } - + static func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -46,7 +46,7 @@ struct PopUpAPIEndPoint { queryParameters: request ) } - + static func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift b/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift index b3deaf5f..60495f23 100644 --- a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift @@ -17,5 +17,3 @@ extension GetSearchPopUpListResponseDTO { return .init(popUpStoreList: popUpStoreList.map { $0.toDomain() }, loginYn: loginYn) } } - - diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift index d36cc605..147877c2 100644 --- a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift @@ -8,7 +8,7 @@ import Foundation struct SignUpAPIEndpoint { - + /// 닉네임 중복을 확인합니다. /// - Parameter request: 닉네임 체크 요청 DTO /// - Returns: Endpoint @@ -20,7 +20,7 @@ struct SignUpAPIEndpoint { queryParameters: request ) } - + /// 관심사 목록을 가져옵니다. /// - Returns: Endpoint static func signUp_getCategoryList() -> Endpoint { @@ -30,7 +30,7 @@ struct SignUpAPIEndpoint { method: .get ) } - + /// 회원가입을 시도합니다. /// - Parameter request: 회원가입 요청 DTO /// - Returns: RequestEndpoint diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift index 26323fbd..cd3b71ea 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift @@ -5,7 +5,6 @@ // Created by SeoJunYoung on 1/12/25. // - import Foundation struct GetMyCommentedPopUpResponseDTO: Decodable { diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift index 45948742..455b5d26 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift @@ -42,6 +42,3 @@ extension GetOtherUserCommentedPopUpResponseDTO { ) } } - - - diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift index f50372d7..862bccb1 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift @@ -26,8 +26,6 @@ extension GetWithdrawlListDataResponseDTO { } } - - struct PostWithdrawlListRequestDTO: Encodable { var checkedSurveyList: [GetWithdrawlListDataResponseDTO] } diff --git a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift index 4916c847..9ca2708d 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift @@ -10,7 +10,7 @@ import Foundation import RxSwift struct UserAPIEndPoint { - + static func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -19,7 +19,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -28,7 +28,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func postCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -37,7 +37,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func deleteCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -46,7 +46,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func postUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -55,7 +55,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func deleteUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -64,7 +64,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func getOtherUserCommentPopUpList(request: GetOtherUserCommentListRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -73,7 +73,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func getMyPage() -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -81,7 +81,7 @@ struct UserAPIEndPoint { method: .get ) } - + static func postLogout() -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -89,7 +89,7 @@ struct UserAPIEndPoint { method: .post ) } - + static func getWithdrawlList() -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -97,7 +97,7 @@ struct UserAPIEndPoint { method: .get ) } - + static func postWithdrawl(request: PostWithdrawlListRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -106,7 +106,7 @@ struct UserAPIEndPoint { bodyParameters: request ) } - + static func getMyProfile() -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -114,7 +114,7 @@ struct UserAPIEndPoint { method: .get ) } - + static func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -122,8 +122,8 @@ struct UserAPIEndPoint { method: .put, bodyParameters: request ) - } - + } + static func putUserCategory(request: PutUserCategoryRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -131,8 +131,8 @@ struct UserAPIEndPoint { method: .put, bodyParameters: request ) - } - + } + static func putUserProfile(request: PutUserProfileRequestDTO) -> RequestEndpoint { return RequestEndpoint( baseURL: KeyPath.popPoolBaseURL, @@ -141,7 +141,7 @@ struct UserAPIEndPoint { bodyParameters: request ) } - + static func getMyCommentedPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -150,7 +150,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func getBlockUserList(request: GetBlockUserListRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -159,7 +159,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func getNoticeList() -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -167,15 +167,15 @@ struct UserAPIEndPoint { method: .get ) } - + static func getNoticeDetail(noticeID: Int64) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, path: "/notice/\(noticeID)", method: .get ) - } - + } + static func getRecentPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, @@ -184,7 +184,7 @@ struct UserAPIEndPoint { queryParameters: request ) } - + static func getBookmarkPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( baseURL: KeyPath.popPoolBaseURL, diff --git a/Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift index 7c9e2628..7e01f977 100644 --- a/Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift @@ -9,15 +9,15 @@ import Foundation import RxSwift final class AuthAPIRepositoryImpl { - + var provider: Provider - + var tokenInterceptor = TokenInterceptor() - + init(provider: Provider) { self.provider = provider } - + func tryLogIn(userCredential: Encodable, socialType: String) -> Observable { let endPoint = AuthAPIEndPoint.auth_tryLogin(with: userCredential, path: socialType) return provider @@ -26,7 +26,7 @@ final class AuthAPIRepositoryImpl { return responseDTO.toDomain() } } - + func postTokenReissue() -> Observable { let endPoint = AuthAPIEndPoint.postTokenReissue() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) diff --git a/Poppool/Poppool/Data/Repository/CommentAPIRepository.swift b/Poppool/Poppool/Data/Repository/CommentAPIRepository.swift index 0f3bd9ae..f5264dbf 100644 --- a/Poppool/Poppool/Data/Repository/CommentAPIRepository.swift +++ b/Poppool/Poppool/Data/Repository/CommentAPIRepository.swift @@ -10,24 +10,24 @@ import Foundation import RxSwift final class CommentAPIRepository { - + private let provider: Provider private let tokenInterceptor = TokenInterceptor() - + init(provider: Provider) { self.provider = provider } - + func postCommentAdd(request: PostCommentRequestDTO) -> Completable { let endPoint = CommentAPIEndPoint.postCommentAdd(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func deleteComment(request: DeleteCommentRequestDTO) -> Completable { let endPoint = CommentAPIEndPoint.deleteComment(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func editComment(request: PutCommentRequestDTO) -> Completable { let endPoint = CommentAPIEndPoint.editComment(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) diff --git a/Poppool/Poppool/Data/Repository/HomeAPIRepository.swift b/Poppool/Poppool/Data/Repository/HomeAPIRepository.swift index 17c9a453..2bd55980 100644 --- a/Poppool/Poppool/Data/Repository/HomeAPIRepository.swift +++ b/Poppool/Poppool/Data/Repository/HomeAPIRepository.swift @@ -9,29 +9,29 @@ import Foundation import RxSwift final class HomeAPIRepository { - + private let provider: Provider private let tokenInterceptor = TokenInterceptor() - + init(provider: Provider) { self.provider = provider } - + func fetchHome(request: SortedRequestDTO) -> Observable { let endPoint = HomeAPIEndpoint.fetchHome(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - + func fetchCustomPopUp(request: SortedRequestDTO) -> Observable { let endPoint = HomeAPIEndpoint.fetchCustomPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - + func fetchNewPopUp(request: SortedRequestDTO) -> Observable { let endPoint = HomeAPIEndpoint.fetchNewPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - + func fetchPopularPopUp(request: SortedRequestDTO) -> Observable { let endPoint = HomeAPIEndpoint.fetchPopularPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) diff --git a/Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift index 2ee2ed50..3c786005 100644 --- a/Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift @@ -12,36 +12,36 @@ import RxSwift struct PopUpAPIRepositoryImpl { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - + init(provider: Provider) { self.provider = provider } - + func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { let endPoint = PopUpAPIEndPoint.getClosePopUpList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { let endPoint = PopUpAPIEndPoint.getOpenPopUpList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { let endPoint = PopUpAPIEndPoint.getSearchPopUpList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Observable { let endPoint = PopUpAPIEndPoint.getPopUpDetail(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Observable { let endPoint = PopUpAPIEndPoint.getPopUpComment(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) diff --git a/Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift b/Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift index 87455fbd..6402b3dc 100644 --- a/Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift +++ b/Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift @@ -9,25 +9,25 @@ import Foundation import RxSwift final class SignUpRepositoryImpl { - + var provider: Provider - + init(provider: Provider) { self.provider = provider } - + func checkNickName(nickName: String) -> Observable { let endPoint = SignUpAPIEndpoint.signUp_checkNickName(with: .init(nickName: nickName)) return provider.requestData(with: endPoint, interceptor: TokenInterceptor()) } - + func fetchCategoryList() -> Observable<[Category]> { let endPoint = SignUpAPIEndpoint.signUp_getCategoryList() return provider.requestData(with: endPoint, interceptor: TokenInterceptor()).map { responseDTO in return responseDTO.categoryResponseList.map({ $0.toDomain() }) } } - + func trySignUp( nickName: String, gender: String, diff --git a/Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift b/Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift index 5e4cdab2..55a66ddb 100644 --- a/Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift @@ -10,64 +10,64 @@ import Foundation import RxSwift final class UserAPIRepositoryImpl { - + private let provider: Provider private let tokenInterceptor = TokenInterceptor() - + init(provider: Provider) { self.provider = provider } - + func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.deleteBookmarkPopUp(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func postCommentLike(request: CommentLikeRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.postCommentLike(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func deleteCommentLike(request: CommentLikeRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.deleteCommentLike(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func postUserBlock(request: PostUserBlockRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.postUserBlock(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func deleteUserBlock(request: PostUserBlockRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.deleteUserBlock(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func getOtherUserCommentList(request: GetOtherUserCommentListRequestDTO) -> Observable { let endPoint = UserAPIEndPoint.getOtherUserCommentPopUpList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getMyPage() -> Observable { let endPoint = UserAPIEndPoint.getMyPage() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func postLogout() -> Completable { let endPoint = UserAPIEndPoint.postLogout() return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func getWithdrawlList() -> Observable { let endPoint = UserAPIEndPoint.getWithdrawlList() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func postWithdrawl(request: PostWithdrawlListRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.postWithdrawl(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) @@ -77,47 +77,47 @@ final class UserAPIRepositoryImpl { let endPoint = UserAPIEndPoint.getMyProfile() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.putUserTailoredInfo(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func putUserCategory(request: PutUserCategoryRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.putUserCategory(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) - } - + } + func putUserProfile(request: PutUserProfileRequestDTO) -> Completable { let endPoint = UserAPIEndPoint.putUserProfile(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - + func getMyCommentedPopUp(request: SortedRequestDTO) -> Observable { let endPoint = UserAPIEndPoint.getMyCommentedPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getBlockUserList(request: GetBlockUserListRequestDTO) -> Observable { let endPoint = UserAPIEndPoint.getBlockUserList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getNoticeList() -> Observable { let endPoint = UserAPIEndPoint.getNoticeList() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getNoticeDetail(noticeID: Int64) -> Observable { let endPoint = UserAPIEndPoint.getNoticeDetail(noticeID: noticeID) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getRecentPopUp(request: SortedRequestDTO) -> Observable { let endPoint = UserAPIEndPoint.getRecentPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } - + func getBookmarkPopUp(request: SortedRequestDTO) -> Observable { let endPoint = UserAPIEndPoint.getBookmarkPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) diff --git a/Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift b/Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift index 85d346d8..6d47e920 100644 --- a/Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift +++ b/Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift @@ -11,7 +11,6 @@ struct GetMyCommentedPopUpResponse { var popUpInfoList: [GetMyCommentedPopUpDataResponse] } - struct GetMyCommentedPopUpDataResponse { var popUpStoreId: Int64 var popUpStoreName: String? diff --git a/Poppool/Poppool/Domain/Repository/AuthRepository.swift b/Poppool/Poppool/Domain/Repository/AuthRepository.swift index 4aea0f51..0a8c43be 100644 --- a/Poppool/Poppool/Domain/Repository/AuthRepository.swift +++ b/Poppool/Poppool/Domain/Repository/AuthRepository.swift @@ -8,7 +8,7 @@ import Foundation import RxSwift -//protocol AuthRepository { +// protocol AuthRepository { // // /// 네트워크 요청을 처리하는 프로바이더 // var provider: Provider { get set } @@ -19,4 +19,4 @@ import RxSwift // /// - socialType: 소셜 로그인 타입 (예: "google", "facebook") // /// - Returns: 로그인 응답을 나타내는 Observable 객체 // func tryLogIn(userCredential: Encodable, socialType: String) -> Observable -//} +// } diff --git a/Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift index bb96f2c2..e15877e6 100644 --- a/Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift @@ -9,17 +9,17 @@ import Foundation import RxSwift final class AuthAPIUseCaseImpl { - + var repository: AuthAPIRepositoryImpl - + init(repository: AuthAPIRepositoryImpl) { self.repository = repository } - + func postTryLogin(userCredential: Encodable, socialType: String) -> Observable { return repository.tryLogIn(userCredential: userCredential, socialType: socialType) } - + func postTokenReissue() -> Observable { let endPoint = AuthAPIEndPoint.postTokenReissue() return repository.postTokenReissue().map { $0.toDomain() } diff --git a/Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift index 1cd85b34..f6d83409 100644 --- a/Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift @@ -10,21 +10,21 @@ import Foundation import RxSwift final class CommentAPIUseCaseImpl { - + var repository: CommentAPIRepository - + init(repository: CommentAPIRepository) { self.repository = repository } - + func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { return repository.postCommentAdd(request: .init(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList)) } - + func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { return repository.deleteComment(request: .init(popUpStoreId: popUpStoreId, commentId: commentId)) } - + func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable { return repository.editComment(request: .init(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList)) } diff --git a/Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift index 3c28efac..1664dd60 100644 --- a/Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift @@ -10,7 +10,7 @@ import RxSwift final class HomeAPIUseCaseImpl { var repository = HomeAPIRepository(provider: ProviderImpl()) - + func fetchHome( page: Int32?, size: Int32?, @@ -18,7 +18,7 @@ final class HomeAPIUseCaseImpl { ) -> Observable { return repository.fetchHome(request: .init(page: page, size: size, sort: sort)) } - + func fetchCustomPopUp( page: Int32?, size: Int32?, @@ -26,7 +26,7 @@ final class HomeAPIUseCaseImpl { ) -> Observable { return repository.fetchCustomPopUp(request: .init(page: page, size: size, sort: sort)) } - + func fetchNewPopUp( page: Int32?, size: Int32?, @@ -34,7 +34,7 @@ final class HomeAPIUseCaseImpl { ) -> Observable { return repository.fetchNewPopUp(request: .init(page: page, size: size, sort: sort)) } - + func fetchPopularPopUp( page: Int32?, size: Int32?, diff --git a/Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift index 2b00740c..6c405d4a 100644 --- a/Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift @@ -10,13 +10,13 @@ import Foundation import RxSwift final class PopUpAPIUseCaseImpl { - + var repository: PopUpAPIRepositoryImpl - + init(repository: PopUpAPIRepositoryImpl) { self.repository = repository } - + func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable { var categoryString: String? if !categories.isEmpty { @@ -29,17 +29,17 @@ final class PopUpAPIUseCaseImpl { return repository.getClosePopUpList(request: request).map { $0.toDomain() } } } - + func getSearchPopUpList(query: String?) -> Observable { return repository.getSearchPopUpList(request: .init(query: query)).map { $0.toDomain() } } - + func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool? = true) -> Observable { return repository.getPopUpDetail(request: .init(commentType: commentType, popUpStoreId: popUpStoredId, viewCountYn: isViewCount)).map { $0.toDomain() } } - + func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { - let request:GetPopUpCommentRequestDTO = .init(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) + let request: GetPopUpCommentRequestDTO = .init(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) return repository.getPopUpComment(request: request).map { $0.toDomain() } } } diff --git a/Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift index e67b1868..006d48fe 100644 --- a/Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift @@ -10,7 +10,7 @@ import RxSwift final class SignUpAPIUseCaseImpl { var repository: SignUpRepositoryImpl - + init(repository: SignUpRepositoryImpl) { self.repository = repository } @@ -36,7 +36,7 @@ final class SignUpAPIUseCaseImpl { func checkNickName(nickName: String) -> Observable { return repository.checkNickName(nickName: nickName) } - + func fetchCategoryList() -> Observable<[Category]> { return repository.fetchCategoryList() } diff --git a/Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift index 2eab0c45..1c907205 100644 --- a/Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift @@ -8,37 +8,37 @@ import RxSwift final class UserAPIUseCaseImpl { - + var repository: UserAPIRepositoryImpl - + init(repository: UserAPIRepositoryImpl) { self.repository = repository } - + func postBookmarkPopUp(popUpID: Int64) -> Completable { return repository.postBookmarkPopUp(request: .init(popUpStoreId: popUpID)) } - + func deleteBookmarkPopUp(popUpID: Int64) -> Completable { return repository.deleteBookmarkPopUp(request: .init(popUpStoreId: popUpID)) } - + func postCommentLike(commentId: Int64) -> Completable { return repository.postCommentLike(request: .init(commentId: commentId)) } - + func deleteCommentLike(commentId: Int64) -> Completable { return repository.deleteCommentLike(request: .init(commentId: commentId)) } - + func postUserBlock(blockedUserId: String?) -> Completable { return repository.postUserBlock(request: .init(blockedUserId: blockedUserId)) } - + func deleteUserBlock(blockedUserId: String?) -> Completable { return repository.deleteUserBlock(request: .init(blockedUserId: blockedUserId)) } - + func getOtherUserCommentedPopUpList( commenterId: String?, commentType: String?, @@ -56,31 +56,31 @@ final class UserAPIUseCaseImpl { ) .map { $0.toDomain() } } - + func getMyPage() -> Observable { return repository.getMyPage().map { $0.toDomain() } } - + func postLogout() -> Completable { return repository.postLogout() } - + func getWithdrawlList() -> Observable { return repository.getWithdrawlList().map { $0.toDomain() } } - + func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable { return repository.postWithdrawl(request: .init(checkedSurveyList: surveyList.map { .init(id: $0.id, survey: $0.survey)})) } - + func getMyProfile() -> Observable { return repository.getMyProfile().map { $0.toDomain() } } - + func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { return repository.putUserTailoredInfo(request: .init(gender: gender, age: age)) } - + func putUserCategory( interestCategoriesToAdd: [Int64], interestCategoriesToDelete: [Int64], @@ -94,31 +94,31 @@ final class UserAPIUseCaseImpl { ) ) } - + func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable { return repository.putUserProfile(request: .init(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro)) } - - func getMyCommentedPopUp(page: Int32?, size:Int32?, sort: String?) -> Observable { + + func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getMyCommentedPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } } - + func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getBlockUserList(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } } - + func getNoticeList() -> Observable { return repository.getNoticeList().map { $0.toDomain() } } - + func getNoticeDetail(noticeID: Int64) -> Observable { return repository.getNoticeDetail(noticeID: noticeID).map { $0.toDomain() } } - + func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getRecentPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } - } - + } + func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getBookmarkPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } } diff --git a/Poppool/Poppool/Infrastructure/AppleLoginService.swift b/Poppool/Poppool/Infrastructure/AppleLoginService.swift index a26f2e0c..ecee526f 100644 --- a/Poppool/Poppool/Infrastructure/AppleLoginService.swift +++ b/Poppool/Poppool/Infrastructure/AppleLoginService.swift @@ -5,25 +5,25 @@ // Created by SeoJunYoung on 8/20/24. // -import RxSwift import AuthenticationServices +import RxSwift final class AppleLoginService: NSObject, AuthServiceable { - + // 사용자 자격 증명 정보를 방출할 subject private var authServiceResponse: PublishSubject = .init() - + func fetchUserCredential() -> Observable { performRequest() return authServiceResponse } - + // Apple 인증 요청을 수행하는 함수 private func performRequest() { let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() request.requestedScopes = [.fullName, .email] - + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self authorizationController.presentationContextProvider = self @@ -48,7 +48,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi } return window } - + // 인증 성공 시 호출되는 함수 func authorizationController( controller: ASAuthorizationController, @@ -69,7 +69,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi guard let authorizationCode = appleIDCredential.authorizationCode else { return } - + guard let convertAuthorizationCode = String(data: authorizationCode, encoding: .utf8) else { return } diff --git a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift index 4d234418..382133e9 100644 --- a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift +++ b/Poppool/Poppool/Infrastructure/KakaoLoginService.swift @@ -1,11 +1,11 @@ -import RxSwift -import KakaoSDKUser import KakaoSDKAuth +import KakaoSDKUser +import RxSwift final class KakaoLoginService: AuthServiceable { - + var disposeBag = DisposeBag() - + func unlink() -> Observable { return Observable.create { observer in UserApi.shared.unlink { error in @@ -17,11 +17,11 @@ final class KakaoLoginService: AuthServiceable { observer.onCompleted() } } - + return Disposables.create() } } - + func fetchUserCredential() -> Observable { return Observable.create { [weak self] observer in guard let self else { @@ -33,7 +33,7 @@ final class KakaoLoginService: AuthServiceable { ) return Disposables.create() } - + // 카카오톡 설치 유무 확인 guard UserApi.isKakaoTalkLoginAvailable() else { Logger.log( @@ -42,28 +42,28 @@ final class KakaoLoginService: AuthServiceable { fileName: #file, line: #line ) - + // 카카오톡 미설치시 웹으로 인증 시도 loginWithKakaoTalkWeb(observer: observer) return Disposables.create() } - + // 카카오톡 설치시 앱으로 인증 시도 loginWithKakaoTalkApp(observer: observer) - + return Disposables.create() } } } private extension KakaoLoginService { - + /// 제공된 액세스 토큰을 사용하여 사용자의 카카오 ID를 가져옵니다. /// - Parameters: /// - observer: 인증 응답을 처리할 옵저버. /// - accessToken: 카카오 로그인 과정에서 얻은 액세스 토큰. func fetchUserId(observer: AnyObserver, accessToken: String) { - UserApi.shared.me() { user, error in + UserApi.shared.me { user, error in if let error = error { observer.onError(AuthError.unknownError(description: error.localizedDescription)) } else { @@ -72,7 +72,7 @@ private extension KakaoLoginService { } } } - + /// 카카오톡 앱을 사용하여 로그인하고 액세스 토큰을 가져옵니다. /// - Parameter observer: 인증 응답을 처리할 옵저버. func loginWithKakaoTalkApp(observer: AnyObserver) { @@ -86,7 +86,7 @@ private extension KakaoLoginService { } } } - + /// 카카오톡 웹을 사용하여 로그인하고 액세스 토큰을 가져옵니다. /// - Parameter observer: 인증 응답을 처리할 옵저버. func loginWithKakaoTalkWeb(observer: AnyObserver) { diff --git a/Poppool/Poppool/Infrastructure/KeyChainService.swift b/Poppool/Poppool/Infrastructure/KeyChainService.swift index 7717acea..ebcfcb70 100644 --- a/Poppool/Poppool/Infrastructure/KeyChainService.swift +++ b/Poppool/Poppool/Infrastructure/KeyChainService.swift @@ -18,10 +18,10 @@ final class KeyChainService { case unhandledError(status: OSStatus) // 예상치 못한 OSStatus 오류가 발생했을 때 발생 case dataConversionError(message: String) // 데이터 변환 중 오류가 발생했을 때 발생 } - + // KeyChain 서비스 이름 private let service = "keyChain" - + /// KeyChain에서 특정 타입의 토큰을 가져오는 메서드 /// - Parameter type: 가져오려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Returns: 가져온 토큰을 담은 `Single` @@ -34,11 +34,11 @@ final class KeyChainService { kSecReturnData: true, // CFData 타입으로 불러오라는 의미 kSecMatchLimit: kSecMatchLimitOne // 중복되는 경우 하나의 값만 가져오라는 의미 ] - + // 2. Read var dataTypeRef: AnyObject? let status = SecItemCopyMatching(keyChainQuery, &dataTypeRef) - + // 3. Result if status == errSecItemNotFound { return .failure(KeyChainError.noValueFound(message: "No value found for the specified key.")) @@ -72,7 +72,7 @@ final class KeyChainService { guard let convertValue = value.data(using: .utf8, allowLossyConversion: false) else { return .failure(KeyChainError.dataConversionError(message: "Failed to convert value to Data.")) } - + // 1. query 작성 let keyChainQuery: NSDictionary = [ kSecClass: kSecClassGenericPassword, @@ -80,11 +80,11 @@ final class KeyChainService { kSecAttrAccount: type.rawValue, kSecValueData: convertValue ] - + // 2. Delete // KeyChain은 Key값에 중복이 생기면 저장할 수 없기 때문에 먼저 Delete SecItemDelete(keyChainQuery) - + // 3. Create let status = SecItemAdd(keyChainQuery, nil) if status == errSecSuccess { @@ -99,7 +99,7 @@ final class KeyChainService { return .failure(KeyChainError.unhandledError(status: status)) } } - + /// KeyChain에서 특정 타입의 토큰을 삭제하는 메서드 /// - Parameter type: 삭제하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Returns: 완료 시 `Completable` @@ -110,10 +110,10 @@ final class KeyChainService { kSecAttrService: self.service, kSecAttrAccount: type.rawValue ] - + // 2. Delete let status = SecItemDelete(keyChainQuery) - + if status == errSecSuccess { Logger.log( message: "Successfully deleted \(type.rawValue) from KeyChain", diff --git a/Poppool/Poppool/Infrastructure/Logger/Logger.swift b/Poppool/Poppool/Infrastructure/Logger/Logger.swift index cf3cbadb..9ad233b2 100644 --- a/Poppool/Poppool/Infrastructure/Logger/Logger.swift +++ b/Poppool/Poppool/Infrastructure/Logger/Logger.swift @@ -15,7 +15,7 @@ struct Logger { case error case event case custom(categoryName: String) - + var categoryName: String { switch self { case .info: @@ -32,7 +32,7 @@ struct Logger { return categoryName } } - + var categoryIcon: String { switch self { case .info: @@ -50,13 +50,13 @@ struct Logger { } } } - + static var isShowFileName: Bool = false static var isShowLine: Bool = false static var isShowLog: Bool = true - + static private let noInputText = "Input is not found" - + static func log( message: Any, category: Level, diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift index cd902b29..9933fba3 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift @@ -24,14 +24,14 @@ extension Requestable { /// - Returns: URLRequest 반환 func getUrlRequest() throws -> URLRequest { let url = try url() - + Logger.log( message: "\(url) URL 생성", category: .network, fileName: #file, line: #line ) - + var urlRequest = URLRequest(url: url) // httpBody if let bodyParameters = try bodyParameters?.toDictionary() { @@ -46,7 +46,7 @@ extension Requestable { headers?.forEach { urlRequest.setValue($1, forHTTPHeaderField: $0) } return urlRequest } - + /// APIEndpoint에서 전달받은 DTO를 URL로 변환하는 메서드 /// - Returns: URL 반환 func url() throws -> URL { @@ -80,7 +80,7 @@ extension Requestable { } extension Encodable { - + /// URL에 요청할 쿼리 데이터를 JSON 형식에 맞게 딕셔너리 구조로 변환하는 메서드 /// - Returns: jsonData func toDictionary() throws -> [String: Any]? { diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift index cdbe56d6..fcc83980 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift @@ -13,7 +13,7 @@ protocol RequesteResponsable: Requestable, Responsable where Response: Decodable class Endpoint: RequesteResponsable { typealias Response = R - + var baseURL: String var path: String var method: HTTPMethod diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift index 42d83dd0..75b4ef2e 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift @@ -17,7 +17,7 @@ class MultipartEndPoint: URLRequestConvertible { var jsonData: [String: Any]? var images: [UIImage] var headers: [String: String]? - + init( baseURL: String, path: String, @@ -35,22 +35,22 @@ class MultipartEndPoint: URLRequestConvertible { self.images = images self.headers = headers } - + func asURLRequest() throws -> URLRequest { let url = try baseURL.asURL().appendingPathComponent(path) var request = URLRequest(url: url) Logger.log(message: "\(request) URL 생성", category: .network) request.method = method - + if let headers = headers { for (key, value) in headers { request.setValue(value, forHTTPHeaderField: key) } } - + return request } - + func asMultipartFormData(multipartFormData: MultipartFormData) { // JSON 데이터를 data 필드로 추가 if let jsonData = jsonData { @@ -65,7 +65,7 @@ class MultipartEndPoint: URLRequestConvertible { Logger.log(message: "JSON 변환 오류: \(error)", category: .network) } } - + // 이미지 파일 추가 for (index, image) in images.enumerated() { if let imageData = image.jpegData(compressionQuality: 0.8) { diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift index f25705e3..997125a1 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift @@ -11,26 +11,26 @@ import Lottie import SnapKit struct IndicatorMaker { - + static let indicatorImageView: LottieAnimationView = { let view = LottieAnimationView(name: "indicator") view.loopMode = .loop return view }() - + static let overlayView: UIView = { let view = UIView() view.backgroundColor = .black.withAlphaComponent(0.1) return view }() - + static func showIndicator() { DispatchQueue.main.async { guard let topVC = UIApplication.topViewController() else { print("Error: Cannot find top view controller") return } - + topVC.view.addSubview(overlayView) overlayView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -44,7 +44,7 @@ struct IndicatorMaker { topVC.view.isUserInteractionEnabled = false } } - + static func hideIndicator() { DispatchQueue.main.async { indicatorImageView.stop() diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift index efd1d123..f057df7d 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift @@ -5,20 +5,20 @@ // Created by SeoJunYoung on 10/25/24. // -import Foundation import Alamofire +import Foundation import RxSwift final class FormDataInterceptor: RequestInterceptor { - + private var disposeBag = DisposeBag() - + func adapt( _ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { } - + func retry( _ request: Request, for session: Session, diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift index 9b4d8c55..9ad8e40f 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift @@ -5,12 +5,12 @@ // Created by SeoJunYoung on 10/14/24. // -import Foundation import Alamofire +import Foundation import RxSwift final class TokenInterceptor: RequestInterceptor { - + func adapt( _ urlRequest: URLRequest, for session: Session, @@ -29,7 +29,7 @@ final class TokenInterceptor: RequestInterceptor { completion(.success(urlRequest)) } } - + func retry( _ request: Request, for session: Session, diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift index edc12bfd..1a2e4394 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift @@ -7,11 +7,11 @@ import Foundation -import RxSwift import Alamofire +import RxSwift protocol Provider { - + /// 네트워크 요청을 수행하고 결과를 반환하는 메서드 /// - Parameters: /// - endpoint: 요청할 엔드포인트 @@ -22,7 +22,7 @@ protocol Provider { with endpoint: E, interceptor: RequestInterceptor? ) -> Observable where R == E.Response - + /// 네트워크 요청을 수행하고 결과를 반환하는 메서드 /// - Parameters: /// - request: 요청할 Requestable 객체 @@ -33,7 +33,7 @@ protocol Provider { with request: E, interceptor: RequestInterceptor? ) -> Completable - + /// 이미지와 데이터를 `multipart/form-data`로 업로드하는 메서드 func uploadImages( with request: MultipartEndPoint, diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift index efa81ca6..643a0e1f 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift @@ -5,9 +5,9 @@ // Created by SeoJunYoung on 8/16/24. // +import Alamofire import Foundation import RxSwift -import Alamofire final class ProviderImpl: Provider { diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift index b1a6912f..26efb286 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift @@ -8,9 +8,9 @@ import Foundation import UIKit -import RxSwift -import RxCocoa import Alamofire +import RxCocoa +import RxSwift class ImageCache { static let shared = NSCache() @@ -24,9 +24,9 @@ class PreSignedService { } let tokenInterceptor = TokenInterceptor() - + let provider = ProviderImpl() - + let disposeBag = DisposeBag() func tryDelete(targetPaths: PresignedURLRequestDTO) -> Completable { @@ -82,7 +82,6 @@ class PreSignedService { } } - func tryDownload(filePaths: [String]) -> Single<[UIImage]> { return Single.create { [weak self] observer in @@ -149,7 +148,6 @@ class PreSignedService { } } - private extension PreSignedService { func uploadFromS3(url: String, image: UIImage) -> Single { @@ -204,8 +202,6 @@ private extension PreSignedService { } } - - func getUploadLinks(request: PresignedURLRequestDTO) -> Observable { Logger.log(message: "Presigned URL 생성 요청 데이터: \(request)", category: .debug) let provider = ProviderImpl() @@ -308,4 +304,3 @@ extension PreSignedService { } } - diff --git a/Poppool/Poppool/Infrastructure/UserDefaultService.swift b/Poppool/Poppool/Infrastructure/UserDefaultService.swift index ffb4c2de..ff64c2a6 100644 --- a/Poppool/Poppool/Infrastructure/UserDefaultService.swift +++ b/Poppool/Poppool/Infrastructure/UserDefaultService.swift @@ -10,27 +10,27 @@ import Foundation import RxSwift final class UserDefaultService { - + /// Userdefault 데이터 저장 메서드 /// - Parameters: /// - key: 저장하는 데이터의 키 값 i.e) 유저 id 등 /// - value: 저장하는 데이터 값 i.e) access token 등 /// - to: 로컬 데이터베이스 타입 - DatabaseType /// - Returns: 별도 안내 없음 - func save(key: String, value: String) { + func save(key: String, value: String) { UserDefaults.standard.set(value, forKey: key) } - + /// Userdefault 데이터 저장 메서드 /// - Parameters: /// - key: 저장하는 데이터의 키 값 i.e) 유저 id 등 /// - value: 저장하는 데이터 값 i.e) access token 등 /// - to: 로컬 데이터베이스 타입 - DatabaseType /// - Returns: 별도 안내 없음 - func save(key: String, value: [String]) { + func save(key: String, value: [String]) { UserDefaults.standard.set(value, forKey: key) } - + /// Userdefault 데이터 발견 메서드 /// - Parameters: /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 @@ -42,7 +42,7 @@ final class UserDefaultService { } return nil } - + /// Userdefault 데이터 발견 메서드 /// - Parameters: /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 @@ -54,7 +54,7 @@ final class UserDefaultService { } return nil } - + /// Userdefault 데이터 삭제 메서드 /// - Parameters: /// - key: 삭제하는 데이터의 키 값 i.e) 유저 id 등 diff --git a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 5876db70..88d37c32 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -1,15 +1,15 @@ -import UIKit -import SnapKit -import RxSwift -import RxCocoa import ReactorKit +import RxCocoa +import RxSwift +import SnapKit +import UIKit final class AdminBottomSheetView: UIView { - + // MARK: - Properties private var contentHeightConstraint: Constraint? typealias Reactor = AdminBottomSheetReactor - + // MARK: - Components let containerView: UIView = { let view = UIView() @@ -19,44 +19,43 @@ final class AdminBottomSheetView: UIView { view.layer.masksToBounds = true return view }() - + let headerView: UIView = { let view = UIView() view.backgroundColor = .white return view }() - + let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") label.textColor = .black return label }() - + let closeButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "icon_xmark"), for: .normal) - + button.tintColor = .black return button }() - + let segmentedControl: PPSegmentedControl = { - let control = PPSegmentedControl( + return PPSegmentedControl( type: .tab, segments: ["상태값", "카테고리"], selectedSegmentIndex: 0 ) - return control }() - + let contentCollectionView: UICollectionView = { - let layout = UICollectionViewCompositionalLayout { section, env in + let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), heightDimension: .absolute(36) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) - + let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(36) @@ -66,7 +65,7 @@ final class AdminBottomSheetView: UIView { subitems: [item] ) group.interItemSpacing = .fixed(12) - + let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init( top: 20, @@ -75,10 +74,10 @@ final class AdminBottomSheetView: UIView { trailing: 20 ) section.interGroupSpacing = 16 - + return section } - + let collectionView = UICollectionView( frame: .zero, collectionViewLayout: layout @@ -87,9 +86,9 @@ final class AdminBottomSheetView: UIView { collectionView.isScrollEnabled = false return collectionView }() - + let filterChipsView = FilterChipsView() - + let resetButton: PPButton = { let button = PPButton( style: .secondary, @@ -106,7 +105,7 @@ final class AdminBottomSheetView: UIView { ) return button }() - + let saveButton: PPButton = { let button = PPButton( style: .primary, @@ -124,7 +123,7 @@ final class AdminBottomSheetView: UIView { ) return button }() - + private let buttonStack: UIStackView = { let stack = UIStackView() stack.axis = .horizontal @@ -132,74 +131,74 @@ final class AdminBottomSheetView: UIView { stack.distribution = .fillEqually return stack }() - + // MARK: - Initialization override init(frame: CGRect) { super.init(frame: frame) setupLayout() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + // MARK: - Setup private func setupLayout() { backgroundColor = .clear addSubview(containerView) - + containerView.addSubview(headerView) headerView.addSubview(titleLabel) headerView.addSubview(closeButton) - + [segmentedControl, contentCollectionView, filterChipsView, buttonStack].forEach { containerView.addSubview($0) } - + buttonStack.addArrangedSubview(resetButton) buttonStack.addArrangedSubview(saveButton) - + setupConstraints() } - + private func setupConstraints() { containerView.snp.makeConstraints { make in make.left.right.bottom.equalToSuperview() make.top.equalTo(headerView.snp.top) } - + headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(60) } - + titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().offset(16) make.centerY.equalToSuperview() } - + closeButton.snp.makeConstraints { make in make.trailing.equalToSuperview().inset(16) make.centerY.equalToSuperview() make.size.equalTo(24) } - + segmentedControl.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() } - + contentCollectionView.snp.makeConstraints { make in make.top.equalTo(segmentedControl.snp.bottom).offset(16) make.leading.trailing.equalToSuperview() contentHeightConstraint = make.height.equalTo(160).constraint } - + filterChipsView.snp.makeConstraints { make in make.top.equalTo(contentCollectionView.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(16) } - + buttonStack.snp.makeConstraints { make in make.top.equalTo(filterChipsView.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(16) @@ -207,20 +206,20 @@ final class AdminBottomSheetView: UIView { make.height.equalTo(52) } } - + // MARK: - Public Methods func updateContentVisibility(isCategorySelected: Bool) { Logger.log(message: "높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) - + let newHeight: CGFloat = isCategorySelected ? 200 : 160 - + // 애니메이션 없이 바로 적용 contentHeightConstraint?.update(offset: newHeight) contentCollectionView.invalidateIntrinsicContentSize() - + setNeedsLayout() layoutIfNeeded() - + Logger.log(message: "높이 변경 완료", category: .debug) } } diff --git a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index dbee91f3..4f3a1a7b 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -1,9 +1,8 @@ -import UIKit -import SnapKit -import RxSwift -import RxCocoa import ReactorKit - +import RxCocoa +import RxSwift +import SnapKit +import UIKit final class AdminBottomSheetViewController: BaseViewController, View { @@ -28,8 +27,6 @@ final class AdminBottomSheetViewController: BaseViewController, View { fatalError("init(coder:) has not been implemented") } - - // MARK: - Life Cycle override func viewDidLoad() { super.viewDidLoad() @@ -74,7 +71,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { Logger.log(message: "최종 뷰 계층:", category: .debug) } - + private func setupCollectionView() { mainView.contentCollectionView.register( TagSectionCell.self, @@ -85,7 +82,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { // MARK: - Binding func bind(reactor: Reactor) { mainView.segmentedControl.rx.selectedSegmentIndex - .do(onNext: { index in + .do(onNext: { _ in }) .map { Reactor.Action.segmentChanged($0) } .bind(to: reactor.action) diff --git a/Poppool/Poppool/Presentation/Admin/AdminReactor.swift b/Poppool/Poppool/Presentation/Admin/AdminReactor.swift index 1d0f2cbd..e258e6df 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminReactor.swift @@ -1,6 +1,6 @@ import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class AdminReactor: Reactor { diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index b02ef0fe..f3acf044 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -1,7 +1,6 @@ import ReactorKit -import RxSwift import RxCocoa - +import RxSwift final class PopUpStoreRegisterReactor: Reactor { diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift index 5c25cf83..5b4b04fc 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift @@ -1,7 +1,7 @@ -import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit +import UIKit final class PopUpStoreRegisterView: UIView { // 상단 네비게이션 영역 diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 710f001f..e47cb8fe 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1,12 +1,12 @@ -import UIKit -import SnapKit -import ReactorKit -import RxSwift -import RxCocoa -import PhotosUI import Alamofire -import GoogleMaps import CoreLocation +import GoogleMaps +import PhotosUI +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit +import UIKit final class PopUpStoreRegisterViewController: BaseViewController { @@ -24,7 +24,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { private var lonField: UITextField? private var descTV: UITextView? - private let popupName: String = "" private let editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? let presignedService = PreSignedService() @@ -76,7 +75,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return lbl }() - private let menuButton: UIButton = { let btn = UIButton(type: .system) btn.setImage(UIImage(systemName: "adminlist"), for: .normal) @@ -101,7 +99,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return lbl }() - private let addImageButton = UIButton(type: .system).then { $0.setTitle("이미지 추가", for: .normal) $0.setTitleColor(.systemBlue, for: .normal) @@ -153,12 +150,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("카테고리 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() @@ -166,12 +163,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("기간 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() @@ -179,19 +176,19 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("시간 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = UIColor(white:0.95, alpha:1) + view.backgroundColor = UIColor(white: 0.95, alpha: 1) let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGesture.cancelsTouchesInView = false @@ -208,10 +205,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { setupKeyboardHandling() setupAddressField() - } - // MARK: - Navigation private func setupNavigation() { backButton.addTarget(self, action: #selector(onBack), for: .touchUpInside) @@ -243,7 +238,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { descTV?.text = storeDetail.desc let isoFormatter = ISO8601DateFormatter() - + if let startDate = isoFormatter.date(from: storeDetail.startDate), let endDate = isoFormatter.date(from: storeDetail.endDate) { self.selectedStartDate = startDate @@ -253,7 +248,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // 대표 이미지 로드 (생략된 부분은 기존 코드 참고) if let mainImageURL = presignedService.fullImageURL(from: storeDetail.mainImageUrl) { - URLSession.shared.dataTask(with: mainImageURL) { [weak self] data, response, error in + URLSession.shared.dataTask(with: mainImageURL) { [weak self] data, _, _ in guard let self = self, let data = data, let image = UIImage(data: data) else { return } @@ -338,8 +333,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } - - // MARK: - Layout private func setupLayout() { // (1) 상단 컨테이너 @@ -461,7 +454,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // 1) 주소 (TextField) let addressField = makeRoundedTextField("팝업스토어 주소를 입력해 주세요.") self.addressField = addressField - addressField.snp.makeConstraints { make in + addressField.snp.makeConstraints { _ in addressField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) } @@ -473,14 +466,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { self.latField = latField // latField와 연결 latField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) - let lonLabel = makePlainLabel("경도") let lonField = makeRoundedTextField("") self.lonField = lonField // lonField와 연결 lonField.textAlignment = .center lonField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) - let latStack = UIStackView(arrangedSubviews: [latLabel, latField]) latStack.axis = .horizontal latStack.spacing = 8 @@ -502,16 +493,15 @@ final class PopUpStoreRegisterViewController: BaseViewController { locationVStack.spacing = 8 locationVStack.distribution = .fillEqually - // 한 행에 왼쪽 "위치", 오른쪽 2줄(주소 / 위도경도) addRowCustom(leftTitle: "위치", rightView: locationVStack, rowHeight: nil, totalHeight: 80) // (마커) => 2줄 // 1) (마커명 Label + TF) let markerLabel = makePlainLabel("마커명") - + let markerField = makeRoundedTextField("") - + let markerStackH = UIStackView(arrangedSubviews: [markerLabel, markerField]) markerStackH.axis = .horizontal markerStackH.spacing = 8 @@ -531,7 +521,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { markerVStack.spacing = 8 markerVStack.distribution = .fillEqually - // 한 행 => "마커" 라벨, 오른쪽 2줄 (마커명, 스니펫) addRowCustom(leftTitle: "마커", rightView: markerVStack, rowHeight: nil, totalHeight: 80) @@ -562,7 +551,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } - // MARK: - Row private func addRowTextField(leftTitle: String, placeholder: String) { @@ -744,7 +732,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } - private func updatePeriodButtonTitle() { guard let s = selectedStartDate, let e = selectedEndDate else { return } let df = DateFormatter() @@ -851,7 +838,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeRoundedTextField(_ placeholder: String) -> UITextField { let tf = UITextField() tf.placeholder = placeholder - tf.font = UIFont.systemFont(ofSize:14) + tf.font = UIFont.systemFont(ofSize: 14) tf.textColor = .darkGray tf.borderStyle = .none tf.layer.cornerRadius = 8 @@ -865,12 +852,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle(title, for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn } @@ -879,7 +866,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { if let icon = UIImage(named: iconName) { btn.setImage(icon, for: .normal) btn.imageView?.contentMode = .scaleAspectFit - btn.titleEdgeInsets = UIEdgeInsets(top:0, left:6, bottom:0, right:0) + btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 0) } return btn } @@ -887,7 +874,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeSimpleLabel(_ text: String) -> UILabel { let lbl = UILabel() lbl.text = text - lbl.font = UIFont.systemFont(ofSize:14) + lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray return lbl } @@ -896,7 +883,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // 작은 라벨(위도/경도/마커명/스니펫 등) let lbl = UILabel() lbl.text = text - lbl.font = UIFont.systemFont(ofSize:14) + lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray lbl.textAlignment = .right lbl.setContentHuggingPriority(.required, for: .horizontal) @@ -905,12 +892,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeRoundedTextView() -> UITextView { let tv = UITextView() - tv.font = UIFont.systemFont(ofSize:14) + tv.font = UIFont.systemFont(ofSize: 14) tv.textColor = .darkGray tv.layer.cornerRadius = 8 tv.layer.borderWidth = 1 tv.layer.borderColor = UIColor.lightGray.cgColor - tv.textContainerInset = UIEdgeInsets(top:7, left:7, bottom:7, right:7) + tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) tv.isScrollEnabled = true return tv } @@ -918,8 +905,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Padding private extension UITextField { - func setLeftPaddingPoints(_ amount: CGFloat){ - let paddingView = UIView(frame: CGRect(x:0, y:0, width:amount, height: frame.size.height)) + func setLeftPaddingPoints(_ amount: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) leftView = paddingView leftViewMode = .always } @@ -986,7 +973,7 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { for (i, provider) in itemProviders.enumerated() { if provider.canLoadObject(ofClass: UIImage.self) { dispatchGroup.enter() - provider.loadObject(ofClass: UIImage.self) { [weak self] object, error in + provider.loadObject(ofClass: UIImage.self) { [weak self] object, _ in defer { dispatchGroup.leave() } guard let self = self, let image = object as? UIImage else { return } @@ -1046,7 +1033,6 @@ private extension PopUpStoreRegisterViewController { return false } - // (4) 위도/경도 Logger.log(message: "latField.text = \(latField?.text ?? "nil")", category: .debug) Logger.log(message: "lonField.text = \(lonField?.text ?? "nil")", category: .debug) @@ -1122,7 +1108,6 @@ private extension PopUpStoreRegisterViewController { } } - // 폼 데이터 검증 private func validateFormData() -> Bool { guard let name = nameField?.text, @@ -1211,8 +1196,6 @@ private extension PopUpStoreRegisterViewController { imagesToDelete: [] // 필요한 경우 기존 이미지 삭제 로직 추가 ) - - adminUseCase.updateStore(request: request) .subscribe( onNext: { [weak self] _ in @@ -1325,7 +1308,6 @@ private extension PopUpStoreRegisterViewController { startDateBeforeEndDate: isValidDateOrder ) - adminUseCase.createStore(request: request) .subscribe( onNext: { [weak self] _ in @@ -1366,7 +1348,6 @@ private extension PopUpStoreRegisterViewController { } } - private func createDateTime(date: Date?, time: Date?) -> Date? { guard let date = date else { return nil } @@ -1405,9 +1386,6 @@ private extension PopUpStoreRegisterViewController { return formatter.string(from: date) } - - - private func prepareDateTime() -> (startDate: String, endDate: String) { // 시작일/시간 결합 let startDateTime = createDateTime(date: selectedStartDate, time: selectedStartTime) @@ -1446,10 +1424,6 @@ private extension PopUpStoreRegisterViewController { return start < end } - - - - private func showSuccessAlert() { let alert = UIAlertController( title: "등록 성공", @@ -1499,7 +1473,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { return Observable.create { observer in let geocoder = CLGeocoder() let fullAddress = "\(address), Korea" - + geocoder.geocodeAddressString( fullAddress, in: nil, @@ -1511,7 +1485,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { observer.onCompleted() return } - + if let location = placemarks?.first?.location { observer.onNext(location) } else { @@ -1519,7 +1493,7 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { } observer.onCompleted() } - + return Disposables.create() } } @@ -1532,23 +1506,22 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { }) .disposed(by: disposeBag) } - - + @objc private func addressFieldDidChange(_ textField: UITextField) { guard let address = textField.text, !address.isEmpty else { return } - + // 한국 주소임을 명시 let geocoder = CLGeocoder() let addressWithCountry = address + ", South Korea" - + geocoder.geocodeAddressString(addressWithCountry) { [weak self] placemarks, error in if let error = error { print("Geocoding error: \(error.localizedDescription)") return } - + guard let location = placemarks?.first?.location else { return } - + DispatchQueue.main.async { self?.latField?.text = String(format: "%.6f", location.coordinate.latitude) self?.lonField?.text = String(format: "%.6f", location.coordinate.longitude) @@ -1556,5 +1529,5 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { } } } - + } diff --git a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift index ed2815db..5ab3d4c4 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminStoreCell.swift @@ -1,6 +1,6 @@ -import UIKit -import SnapKit import RxSwift +import SnapKit +import UIKit final class AdminStoreCell: UITableViewCell { private let disposeBag = DisposeBag() diff --git a/Poppool/Poppool/Presentation/Admin/AdminView.swift b/Poppool/Poppool/Presentation/Admin/AdminView.swift index 3ec4149e..2d13cc17 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminView.swift @@ -1,6 +1,6 @@ -import UIKit import SnapKit import Then +import UIKit final class AdminView: UIView { @@ -18,7 +18,7 @@ final class AdminView: UIView { let usernameLabel = PPLabel( style: .bold, fontSize: 14, - text: "" + text: "" ) let menuButton = UIButton(type: .system).then { diff --git a/Poppool/Poppool/Presentation/Admin/AdminViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminViewController.swift index 24e86807..48ad02f8 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminViewController.swift @@ -1,7 +1,7 @@ -import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift +import UIKit final class AdminViewController: BaseViewController, View { @@ -238,11 +238,10 @@ final class AdminViewController: BaseViewController, View { .compactMap { $0 } .subscribe(onNext: { [weak self] store in guard let self = self else { return } - self.editStore(store) + self.editStore(store) }) .disposed(by: disposeBag) - reactor.state.map { $0.storeList } .map { "총 \($0.count)개" } .bind(to: mainView.popupCountLabel.rx.text) diff --git a/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift b/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift index 3e251d01..b1de4138 100644 --- a/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift +++ b/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class DateTimePickerManager { diff --git a/Poppool/Poppool/Presentation/Admin/Data/DTO/AdminResponseDTO.swift b/Poppool/Poppool/Presentation/Admin/Data/DTO/AdminResponseDTO.swift index 5924f809..329934c5 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/DTO/AdminResponseDTO.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/DTO/AdminResponseDTO.swift @@ -13,7 +13,6 @@ struct GetAdminPopUpStoreListResponseDTO: Decodable { let mainImageUrl: String } - } // MARK: - Store Detail Response diff --git a/Poppool/Poppool/Presentation/Admin/Data/DTO/GetAdminPopUpStoreListResponseDTO.swift b/Poppool/Poppool/Presentation/Admin/Data/DTO/GetAdminPopUpStoreListResponseDTO.swift index 8be73a2b..6d14302a 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/DTO/GetAdminPopUpStoreListResponseDTO.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/DTO/GetAdminPopUpStoreListResponseDTO.swift @@ -1,4 +1,3 @@ - import Foundation // MARK: - Store List Request @@ -10,7 +9,7 @@ struct StoreListRequestDTO: Encodable { enum CodingKeys: String, CodingKey { case query case page - case size + case size } } @@ -62,7 +61,6 @@ struct CreatePopUpStoreRequestDTO: Encodable { } } - // MARK: - Update Store Request struct UpdatePopUpStoreRequestDTO: Encodable { let popUpStore: PopUpStore @@ -123,7 +121,6 @@ struct UpdatePopUpStoreRequestDTO: Encodable { } } - // MARK: - Notice Request struct CreateNoticeRequestDTO: Encodable { let title: String diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift index 5c4a4bdc..3b5c7107 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapAPIEndpoint.swift @@ -5,8 +5,8 @@ // Created by 김기현 on 12/4/24. // -import Foundation import Alamofire +import Foundation struct MapAPIEndpoint { /// 뷰 바운즈 내에 있는 팝업 스토어 정보를 조회 @@ -84,4 +84,3 @@ struct SearchQueryDTO: Encodable { let query: String let categories: [Int64]? } - diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift index bcc448f4..f448e161 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift @@ -4,8 +4,8 @@ // // Created by 김기현 on 12/3/24. // -import Foundation import CoreLocation +import Foundation struct MapPopUpStore: Equatable { let id: Int64 @@ -20,7 +20,6 @@ struct MapPopUpStore: Equatable { let markerTitle: String let markerSnippet: String let mainImageUrl: String? // 이미지 URL 추가 - var coordinate: CLLocationCoordinate2D { CLLocationCoordinate2D(latitude: latitude, longitude: longitude) @@ -36,8 +35,6 @@ struct MapPopUpStore: Equatable { count: 0 ) } - - func toStoreItem() -> StoreItem { return StoreItem( diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift index 53164882..07bbce5d 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift @@ -33,7 +33,6 @@ class DefaultMapRepository: MapRepository { self.provider = provider } - func fetchStoresInBounds( northEastLat: Double, northEastLon: Double, @@ -68,7 +67,6 @@ class DefaultMapRepository: MapRepository { .map { $0.popUpStoreList } } - func fetchCategories() -> Observable<[Category]> { Logger.log(message: "카테고리 매핑 요청을 시작합니다.", category: .network) diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/UseCase/MapUseCase.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/UseCase/MapUseCase.swift index 1d68c933..660ee9d0 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/UseCase/MapUseCase.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/UseCase/MapUseCase.swift @@ -36,7 +36,7 @@ class DefaultMapUseCase: MapUseCase { northEastLon: Double, southWestLat: Double, southWestLon: Double, - categories: [Int64] + categories: [Int64] ) -> Observable<[MapPopUpStore]> { return repository.fetchStoresInBounds( @@ -49,7 +49,6 @@ class DefaultMapUseCase: MapUseCase { .map { $0.map { $0.toDomain() } } } - func searchStores( query: String, categories: [Int64] diff --git a/Poppool/Poppool/Presentation/Admin/Data/Repository/AdminRepository.swift b/Poppool/Poppool/Presentation/Admin/Data/Repository/AdminRepository.swift index c11757bb..7d0bd1e6 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/Repository/AdminRepository.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/Repository/AdminRepository.swift @@ -1,7 +1,7 @@ +import Alamofire import Foundation import RxSwift import UIKit -import Alamofire protocol AdminRepository { func fetchStoreList(query: String?, page: Int, size: Int) -> Observable @@ -54,7 +54,6 @@ final class DefaultAdminRepository: AdminRepository { } } - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable { Logger.log(message: "createStore API 호출 시작", category: .info) let endpoint = AdminAPIEndpoint.createStore(request: request) @@ -84,8 +83,6 @@ final class DefaultAdminRepository: AdminRepository { ) } - - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable { let endpoint = AdminAPIEndpoint.updateStore(request: request) @@ -125,12 +122,11 @@ final class DefaultAdminRepository: AdminRepository { }) } - func deleteStore(id: Int64) -> Observable { Logger.log(message: "deleteStore API 호출 시작", category: .info) let endpoint = AdminAPIEndpoint.deleteStore(id: id) return provider.request(with: endpoint, interceptor: tokenInterceptor) - .andThen(Observable.just(EmptyResponse())) + .andThen(Observable.just(EmptyResponse())) .do( onNext: { _ in Logger.log(message: "deleteStore API 호출 성공", category: .info) @@ -140,7 +136,6 @@ final class DefaultAdminRepository: AdminRepository { } ) } - // MARK: - Notice Methods func createNotice(request: CreateNoticeRequestDTO) -> Observable { diff --git a/Poppool/Poppool/Presentation/Admin/ImageCell.swift b/Poppool/Poppool/Presentation/Admin/ImageCell.swift index 98f6fbf1..04704d15 100644 --- a/Poppool/Poppool/Presentation/Admin/ImageCell.swift +++ b/Poppool/Poppool/Presentation/Admin/ImageCell.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class ImageCell: UICollectionViewCell { static let identifier = "ImageCell" diff --git a/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterReactor.swift index 99d29f89..6e355394 100644 --- a/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterReactor.swift @@ -5,12 +5,12 @@ //// Created by 김기현 on 1/14/25. //// // -//import Foundation -//import ReactorKit -//import RxSwift -//import UIKit +// import Foundation +// import ReactorKit +// import RxSwift +// import UIKit // -//final class PopUpStoreRegisterReactor: Reactor { +// final class PopUpStoreRegisterReactor: Reactor { // // // MARK: - Action // enum Action { @@ -303,4 +303,4 @@ // default: return 100 // } // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterView.swift index 55c20d1c..6255da1a 100644 --- a/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Admin/PopUpStoreRegisterView.swift @@ -5,11 +5,11 @@ //// Created by 김기현 on 1/14/25. //// // -//import UIKit -//import SnapKit -//import Then +// import UIKit +// import SnapKit +// import Then // -//final class PopUpStoreRegisterView: UIView { +// final class PopUpStoreRegisterView: UIView { // // // MARK: - Callbacks (Closure) // /// "이미지 추가" 버튼 탭 @@ -320,10 +320,10 @@ // // return row // } -//} +// } // //// MARK: - UICollectionViewDataSource -//extension PopUpStoreRegisterView: UICollectionViewDataSource { +// extension PopUpStoreRegisterView: UICollectionViewDataSource { // func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // return images.count // } @@ -347,15 +347,15 @@ // } // return cell // } -//} +// } // //// MARK: - UICollectionViewDelegateFlowLayout -//extension PopUpStoreRegisterView: UICollectionViewDelegateFlowLayout { +// extension PopUpStoreRegisterView: UICollectionViewDelegateFlowLayout { // // 혹시 셀 사이즈/간격을 동적으로 조정하고 싶다면 여기서 -//} +// } // //// MARK: - PopUpImageCell (같은 파일) -//final class PopUpImageCell: UICollectionViewCell { +// final class PopUpImageCell: UICollectionViewCell { // static let identifier = "PopUpImageCell" // // // 콜백 @@ -421,4 +421,4 @@ // thumbImageView.image = item.image // mainCheckButton.backgroundColor = item.isMain ? .systemRed : .gray // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Components/PPButton.swift b/Poppool/Poppool/Presentation/Components/PPButton.swift index 87063272..5d446403 100644 --- a/Poppool/Poppool/Presentation/Components/PPButton.swift +++ b/Poppool/Poppool/Presentation/Components/PPButton.swift @@ -8,14 +8,14 @@ import UIKit class PPButton: UIButton { - + enum ButtonStyle { case primary case secondary case tertiary case kakao case apple - + var backgroundColor: UIColor { switch self { case .primary: @@ -30,7 +30,7 @@ class PPButton: UIButton { return .g900 } } - + var textColor: UIColor { switch self { case .primary: @@ -45,7 +45,7 @@ class PPButton: UIButton { return .w100 } } - + var disabledBackgroundColor: UIColor { switch self { case .primary: @@ -56,7 +56,7 @@ class PPButton: UIButton { return .blu500 } } - + var disabledTextColor: UIColor { switch self { case .primary: @@ -68,8 +68,7 @@ class PPButton: UIButton { } } } - - + init( style: ButtonStyle, text: String, @@ -78,25 +77,25 @@ class PPButton: UIButton { cornerRadius: CGFloat = 4 ) { super.init(frame: .zero) - + self.setTitle(text, for: .normal) self.setTitle(disabledText, for: .disabled) - + self.setTitleColor(style.textColor, for: .normal) self.setTitleColor(style.disabledTextColor, for: .disabled) - + self.setBackgroundColor(style.backgroundColor, for: .normal) self.setBackgroundColor(style.disabledBackgroundColor, for: .disabled) - + self.titleLabel?.font = font self.layer.cornerRadius = cornerRadius self.clipsToBounds = true } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + /// 버튼 배경색 설정 /// - Parameters: /// - color: 색상 @@ -109,7 +108,7 @@ class PPButton: UIButton { let backgroundImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() - + self.setBackgroundImage(backgroundImage, for: state) } } diff --git a/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift b/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift index a033274a..eac352f6 100644 --- a/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift +++ b/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class PPCancelHeaderView: UIView { - + // MARK: - Components let backButton: UIButton = { let button = UIButton(type: .system) @@ -18,7 +18,7 @@ final class PPCancelHeaderView: UIView { button.tintColor = .black return button }() - + let cancelButton: UIButton = { let button = UIButton(type: .system) button.setTitle("취소", for: .normal) @@ -26,13 +26,13 @@ final class PPCancelHeaderView: UIView { button.setTitleColor(.black, for: .normal) return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -40,7 +40,7 @@ final class PPCancelHeaderView: UIView { // MARK: - SetUp private extension PPCancelHeaderView { - + func setUpConstraints() { self.addSubview(backButton) backButton.snp.makeConstraints { make in @@ -48,7 +48,7 @@ private extension PPCancelHeaderView { make.top.bottom.equalToSuperview().inset(8) make.leading.equalToSuperview().inset(12) } - + self.addSubview(cancelButton) cancelButton.snp.makeConstraints { make in make.centerY.equalTo(backButton) diff --git a/Poppool/Poppool/Presentation/Components/PPLabel.swift b/Poppool/Poppool/Presentation/Components/PPLabel.swift index 41030705..25f16a0d 100644 --- a/Poppool/Poppool/Presentation/Components/PPLabel.swift +++ b/Poppool/Poppool/Presentation/Components/PPLabel.swift @@ -8,9 +8,9 @@ import UIKit class PPLabel: UILabel { - + init( - style: UIFont.FontStyle, + style: UIFont.FontStyle, fontSize: CGFloat, text: String = "", lineHeight: CGFloat = 1.2 @@ -24,7 +24,7 @@ class PPLabel: UILabel { attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle] ) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Poppool/Poppool/Presentation/Components/PPPicker.swift b/Poppool/Poppool/Presentation/Components/PPPicker.swift index e5164c9c..82fa75fc 100644 --- a/Poppool/Poppool/Presentation/Components/PPPicker.swift +++ b/Poppool/Poppool/Presentation/Components/PPPicker.swift @@ -5,13 +5,13 @@ // Created by SeoJunYoung on 7/3/24. // +import RxCocoa +import RxSwift import SnapKit import UIKit -import RxSwift -import RxCocoa final class PPPicker: UIView { - + // MARK: - Components private let components: [String] let pickerView = UIPickerView() @@ -24,7 +24,7 @@ final class PPPicker: UIView { private let disposeBag = DisposeBag() /// 항목 선택 이벤트를 전달하는 PublishSubject입니다. let itemSelectObserver: PublishSubject = .init() - + // MARK: - init /// PickerCPNT init /// - Parameter components: UIPickerView에 표시할 문자열 배열입니다. @@ -35,7 +35,7 @@ final class PPPicker: UIView { setUpConstraints() bind() } - + @available(*, unavailable) required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") @@ -49,7 +49,7 @@ extension PPPicker { pickerView.delegate = self pickerView.dataSource = self } - + func setUpConstraints() { self.addSubview(selectView) self.addSubview(pickerView) @@ -62,7 +62,7 @@ extension PPPicker { make.center.equalToSuperview() } } - + func bind() { pickerView.rx.itemSelected .withUnretained(self) @@ -75,7 +75,7 @@ extension PPPicker { // MARK: - Methods extension PPPicker { - + /// 지정된 인덱스로 UIPickerView를 설정 /// - Parameter index: 설정할 인덱스 값 func setIndex(index: Int) { @@ -88,11 +88,11 @@ extension PPPicker: UIPickerViewDelegate, UIPickerViewDataSource { func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } - + func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return components.count } - + func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let label = UILabel() label.text = components[row] @@ -110,7 +110,7 @@ extension PPPicker: UIPickerViewDelegate, UIPickerViewDataSource { func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 48 } - + func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { pickerView.reloadAllComponents() } diff --git a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift b/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift index 4908fec6..ec709b83 100644 --- a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift +++ b/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift @@ -6,18 +6,17 @@ // import Foundation -import UIKit -import SnapKit -import RxSwift import RxCocoa - +import RxSwift +import SnapKit +import UIKit final class PPProgressIndicator: UIStackView { - + // MARK: - Properties private var progressViews: [PPProgressView] private var progressIndex: Int - + // MARK: - init /// 전체 단계 수와 시작 지점을 기반으로 CMPTProgressIndicator를 초기화 /// - Parameters: @@ -32,7 +31,7 @@ final class PPProgressIndicator: UIStackView { setUp() setUpConstraints() } - + required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -40,14 +39,14 @@ final class PPProgressIndicator: UIStackView { // MARK: - SetUp private extension PPProgressIndicator { - + /// 스택 뷰 속성 설정 func setUp() { self.axis = .horizontal self.distribution = .fillEqually self.spacing = 6 } - + /// 진행 뷰의 제약 조건을 설정 func setUpConstraints() { progressViews.forEach { views in @@ -58,7 +57,7 @@ private extension PPProgressIndicator { // MARK: - Methods extension PPProgressIndicator { - + /// 진행 인디케이터를 한 단계 앞으로 이동 func increaseIndicator() { if progressIndex < progressViews.count { @@ -67,7 +66,7 @@ extension PPProgressIndicator { progressViews[progressIndex - 1].fillAnimation(option: .fromLeft) } } - + /// 진행 인디케이터를 한 단계 뒤로 이동 func decreaseIndicator() { if progressIndex - 1 > 0 { diff --git a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift b/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift index 46cf39cc..60a3137a 100644 --- a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift +++ b/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift @@ -6,33 +6,32 @@ // import Foundation -import UIKit import SnapKit +import UIKit final class PPProgressView: UIView { - - + /// CMTPProgressView Animation Type enum ProgressFillAnimation { case fromLeft case fromRight } - + // MARK: - Components private var selectedView: UIView = { let view = UIView() - view.backgroundColor = . systemBlue //수정 필요 + view.backgroundColor = . systemBlue // 수정 필요 view.layer.cornerRadius = 1 return view }() - + private var normalView: UIView = { let view = UIView() view.backgroundColor = .secondarySystemBackground // 수정 필요 view.layer.cornerRadius = 1 return view }() - + /// CMTPProgressView 초기화 /// - Parameter isSelected: 선택 여부 init(isSelected: Bool) { @@ -41,7 +40,7 @@ final class PPProgressView: UIView { setUpConstraints() setUp() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -49,17 +48,17 @@ final class PPProgressView: UIView { // MARK: - SetUp private extension PPProgressView { - + /// 뷰 설정 func setUp() { self.clipsToBounds = true } - + /// 제약 조건 설정 func setUpConstraints() { self.addSubview(normalView) self.addSubview(selectedView) - + self.snp.makeConstraints { make in make.height.equalTo(4) } @@ -75,7 +74,7 @@ private extension PPProgressView { } extension PPProgressView { - + /// 선택된 뷰를 채우는 애니메이션 /// - Parameter option: 애니메이션 시작 방향 func fillAnimation(option: ProgressFillAnimation) { @@ -89,7 +88,7 @@ extension PPProgressView { }) self.layoutIfNeeded() self.selectedView.isHidden = false - + UIView.animate(withDuration: 0.2, animations: { self.selectedView.snp.updateConstraints { make in make.leading.equalToSuperview() @@ -104,7 +103,7 @@ extension PPProgressView { }) self.layoutIfNeeded() self.selectedView.isHidden = false - + UIView.animate(withDuration: 0.2, animations: { self.selectedView.snp.updateConstraints { make in make.leading.equalToSuperview() @@ -113,7 +112,7 @@ extension PPProgressView { }) } } - + /// 선택된 뷰를 사라지게 하는 애니메이션 /// - Parameter option: 애니메이션 시작 방향 func disappearAnimation(option: ProgressFillAnimation) { diff --git a/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift b/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift index f56b0aae..eb91144d 100644 --- a/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift +++ b/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift @@ -5,11 +5,11 @@ // Created by Porori on 11/27/24. // -import UIKit import SnapKit +import UIKit final class PPReturnHeaderView: UIView { - + // MARK: - Components let backButton: UIButton = { let button = UIButton(type: .system) @@ -17,24 +17,24 @@ final class PPReturnHeaderView: UIView { button.tintColor = .black return button }() - + let headerLabel: UILabel = { let label = UILabel() label.font = .KorFont(style: .regular, size: 15) label.textColor = .g1000 return label }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func configure(with text: String) { headerLabel.text = text } @@ -42,7 +42,7 @@ final class PPReturnHeaderView: UIView { // MARK: - SetUp private extension PPReturnHeaderView { - + func setUpConstraints() { self.addSubview(backButton) backButton.snp.makeConstraints { make in @@ -50,7 +50,7 @@ private extension PPReturnHeaderView { make.leading.equalToSuperview().inset(12) make.size.equalTo(28) } - + self.addSubview(headerLabel) headerLabel.snp.makeConstraints { make in make.centerY.equalTo(backButton) diff --git a/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift b/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift index 7699c7fe..7566851e 100644 --- a/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift +++ b/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift @@ -5,11 +5,11 @@ // Created by SeoJunYoung on 6/27/24. // -import UIKit import SnapKit +import UIKit final class PPSegmentedControl: UISegmentedControl { - + /// 세그먼트 컨트롤 타입 enum SegmentedControlType { case radio @@ -31,24 +31,24 @@ final class PPSegmentedControl: UISegmentedControl { bottomLineView.addSubview(view) return view }() - + init(type: SegmentedControlType, segments: [String], selectedSegmentIndex: Int? = nil) { super.init(frame: .zero) setUpSegments(type: type, segments: segments) if let selectedSegmentIndex = selectedSegmentIndex { self.selectedSegmentIndex = selectedSegmentIndex } - + } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + /// 서브뷰 레이아웃 설정 override func layoutSubviews() { super.layoutSubviews() - //layout이 업데이트 될 때 underbar 업데이트 + // layout이 업데이트 될 때 underbar 업데이트 let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(self.selectedSegmentIndex) self.underlineView.snp.updateConstraints { make in make.leading.equalTo(underlineFinalXPosition) @@ -61,7 +61,7 @@ final class PPSegmentedControl: UISegmentedControl { // MARK: - SetUp private extension PPSegmentedControl { - + /// 세그먼트 설정 메서드 /// - Parameters: /// - type: 세그먼트 컨트롤 타입 @@ -122,7 +122,7 @@ private extension PPSegmentedControl { setFont(color: .g400, font: .KorFont(style: .medium, size: 15), state: .normal) } } - + /// 폰트 설정 메서드 /// - Parameters: /// - color: 폰트 색상 diff --git a/Poppool/Poppool/Presentation/Extension/Date?+.swift b/Poppool/Poppool/Presentation/Extension/Date?+.swift index f8117e7e..550cd427 100644 --- a/Poppool/Poppool/Presentation/Extension/Date?+.swift +++ b/Poppool/Poppool/Presentation/Extension/Date?+.swift @@ -19,7 +19,7 @@ extension Optional where Wrapped == Date { formatter.dateFormat = "yyyy. MM. dd" return formatter.string(from: date) } - + func toPPDateMonthString(defaultString: String = "") -> String { guard let date = self else { return defaultString @@ -28,7 +28,7 @@ extension Optional where Wrapped == Date { formatter.dateFormat = "MM월 dd일" return formatter.string(from: date) } - + func toPPTimeeString(defaultString: String = "") -> String { guard let date = self else { return defaultString diff --git a/Poppool/Poppool/Presentation/Extension/Reactive+.swift b/Poppool/Poppool/Presentation/Extension/Reactive+.swift index b3c44553..74665b05 100644 --- a/Poppool/Poppool/Presentation/Extension/Reactive+.swift +++ b/Poppool/Poppool/Presentation/Extension/Reactive+.swift @@ -11,27 +11,27 @@ import RxCocoa import RxSwift extension Reactive where Base: UIViewController { - + var viewDidLoad: ControlEvent { let source = self.methodInvoked(#selector(Base.viewDidLoad)).map( { _ in }) return ControlEvent(events: source) } - + var viewWillAppear: ControlEvent { let source = self.methodInvoked(#selector(Base.viewWillAppear)).map( { _ in }) return ControlEvent(events: source) } - + var viewDidAppear: ControlEvent { let source = self.methodInvoked(#selector(Base.viewDidAppear)).map( { _ in }) return ControlEvent(events: source) } - + var viewWillDisappear: ControlEvent { let source = self.methodInvoked(#selector(Base.viewWillDisappear)).map( { _ in }) return ControlEvent(events: source) } - + var viewDidDisappear: ControlEvent { let source = self.methodInvoked(#selector(Base.viewDidDisappear)).map( { _ in }) return ControlEvent(events: source) diff --git a/Poppool/Poppool/Presentation/Extension/String?+.swift b/Poppool/Poppool/Presentation/Extension/String?+.swift index c1c5a916..feab8506 100644 --- a/Poppool/Poppool/Presentation/Extension/String?+.swift +++ b/Poppool/Poppool/Presentation/Extension/String?+.swift @@ -13,10 +13,10 @@ extension Optional where Wrapped == String { /// ISO 8601 형식의 문자열을 `Date`로 변환하는 메서드 func toDate() -> Date? { guard let self = self else { return nil } // 옵셔널 해제 - + let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") - + if self.contains(".") { // 밀리초 포함 형식 dateFormatter.dateFormat = "yyyy.MM.dd'T'HH:mm:ss.SSS" @@ -24,10 +24,10 @@ extension Optional where Wrapped == String { // 밀리초 없는 형식 dateFormatter.dateFormat = "yyyy.MM.dd'T'HH:mm:ss" } - + return dateFormatter.date(from: self) } - + func isBrightImagePath(completion: @escaping (Bool) -> Void) { if let self = self { let imageView = UIImageView() diff --git a/Poppool/Poppool/Presentation/Extension/UIColor+.swift b/Poppool/Poppool/Presentation/Extension/UIColor+.swift index 5de4edb1..1ea48e23 100644 --- a/Poppool/Poppool/Presentation/Extension/UIColor+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIColor+.swift @@ -8,7 +8,7 @@ import UIKit extension UIColor { - + // 무채색 컬러 static let g50 = UIColor(hexCode: "F2F5F7") static let g100 = UIColor(hexCode: "DFE2E6") @@ -21,7 +21,7 @@ extension UIColor { static let g800 = UIColor(hexCode: "1F242B") static let g900 = UIColor(hexCode: "17191C") static let g1000 = UIColor(hexCode: "141414") - + // 화이트 톤 static let w4 = UIColor(hexCode: "ffffff", alpha: 0.04) static let w7 = UIColor(hexCode: "ffffff", alpha: 0.07) @@ -31,8 +31,7 @@ extension UIColor { static let w70 = UIColor(hexCode: "ffffff", alpha: 0.7) static let w90 = UIColor(hexCode: "ffffff", alpha: 0.9) static let w100 = UIColor(hexCode: "ffffff", alpha: 1.0) - - + // 퓨어 블랙 static let pb4 = UIColor(hexCode: "141414", alpha: 0.04) static let pb7 = UIColor(hexCode: "141414", alpha: 0.07) @@ -43,7 +42,7 @@ extension UIColor { static let pb70 = UIColor(hexCode: "141414", alpha: 0.7) static let pb90 = UIColor(hexCode: "141414", alpha: 0.9) static let pb100 = UIColor(hexCode: "141414", alpha: 1.0) - + // 블루 static let blu100 = UIColor(hexCode: "E5EEFF") static let blu200 = UIColor(hexCode: "B5CCFE") @@ -54,7 +53,7 @@ extension UIColor { static let blu700 = UIColor(hexCode: "023197") static let blu800 = UIColor(hexCode: "022364") static let blu900 = UIColor(hexCode: "011132") - + // 제이드 static let jd100 = UIColor(hexCode: "E6FFFA") static let jd200 = UIColor(hexCode: "CCFFF6") @@ -66,7 +65,7 @@ extension UIColor { static let jd800 = UIColor(hexCode: "00997D") static let jd900 = UIColor(hexCode: "00997D") static let jd1000 = UIColor(hexCode: "004D3E") - + // 레드 static let re100 = UIColor(hexCode: "FFE6E5") static let re200 = UIColor(hexCode: "FFCCCC") @@ -77,19 +76,19 @@ extension UIColor { static let re700 = UIColor(hexCode: "B30100") static let re800 = UIColor(hexCode: "800000") static let re900 = UIColor(hexCode: "4D0000") - + convenience init(hexCode: String, alpha: CGFloat = 1.0) { var hexFormatted: String = hexCode.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines).uppercased() - + if hexFormatted.hasPrefix("#") { hexFormatted = String(hexFormatted.dropFirst()) } - + assert(hexFormatted.count == 6, "Invalid hex code used.") - + var rgbValue: UInt64 = 0 Scanner(string: hexFormatted).scanHexInt64(&rgbValue) - + self.init(red: CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0, green: CGFloat((rgbValue & 0x00FF00) >> 8) / 255.0, blue: CGFloat(rgbValue & 0x0000FF) / 255.0, diff --git a/Poppool/Poppool/Presentation/Extension/UIFont+.swift b/Poppool/Poppool/Presentation/Extension/UIFont+.swift index 4d131df8..098bc0da 100644 --- a/Poppool/Poppool/Presentation/Extension/UIFont+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIFont+.swift @@ -9,7 +9,7 @@ import Foundation import UIKit extension UIFont { - + static func KorFont(style: FontStyle, size: CGFloat) -> UIFont? { return UIFont(name: "GothicA1\(style.rawValue)", size: size) } @@ -17,7 +17,7 @@ extension UIFont { static func EngFont(style: FontStyle, size: CGFloat) -> UIFont? { return UIFont(name: "Poppins\(style.rawValue)", size: size) } - + enum FontStyle: String { case bold = "-Bold" case medium = "-Medium" @@ -25,4 +25,3 @@ extension UIFont { case light = "-Light" } } - diff --git a/Poppool/Poppool/Presentation/Extension/UIImage+.swift b/Poppool/Poppool/Presentation/Extension/UIImage+.swift index be80b2a2..dfeb7e32 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImage+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImage+.swift @@ -22,7 +22,7 @@ extension UIImage { extension UIImage { func isBright(threshold: CGFloat = 0.5) -> Bool? { guard let cgImage = self.cgImage else { return nil } - + let width = 1 let height = 1 let bitsPerComponent = 8 @@ -30,9 +30,9 @@ extension UIImage { let bytesPerRow = bytesPerPixel * width let colorSpace = CGColorSpaceCreateDeviceRGB() let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue - + var pixelData = [UInt8](repeating: 0, count: width * height * bytesPerPixel) - + guard let context = CGContext( data: &pixelData, width: width, @@ -42,16 +42,16 @@ extension UIImage { space: colorSpace, bitmapInfo: bitmapInfo ) else { return nil } - + context.draw(cgImage, in: CGRect(x: 0, y: 0, width: width, height: height)) - + let red = CGFloat(pixelData[0]) / 255.0 let green = CGFloat(pixelData[1]) / 255.0 let blue = CGFloat(pixelData[2]) / 255.0 - + // Brightness calculation formula let brightness = (red * 0.299 + green * 0.587 + blue * 0.114) - + return brightness > threshold } } diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index a74655f3..761ae557 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -28,7 +28,7 @@ extension UIImageView { } } } - + func setPPImage(path: String?, completion: @escaping () -> Void) { guard let path = path else { self.image = UIImage(named: "image_default") diff --git a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift index 3d5a1c94..3716fd40 100644 --- a/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift +++ b/Poppool/Poppool/Presentation/Map/Common/ClusteringModels.swift @@ -20,7 +20,6 @@ enum MapZoomLevel { } } - struct RegionCluster { let name: String let subRegions: [String] diff --git a/Poppool/Poppool/Presentation/Map/Common/FilterType.swift b/Poppool/Poppool/Presentation/Map/Common/FilterType.swift index 354d319a..b19e7c64 100644 --- a/Poppool/Poppool/Presentation/Map/Common/FilterType.swift +++ b/Poppool/Poppool/Presentation/Map/Common/FilterType.swift @@ -1,5 +1,3 @@ - - import Foundation import UIKit diff --git a/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift b/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift index a3e7064b..3d0622d3 100644 --- a/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift +++ b/Poppool/Poppool/Presentation/Map/Common/GMSMapViewDelegateProxy.swift @@ -1,6 +1,6 @@ -import RxSwift -import RxCocoa import GoogleMaps +import RxCocoa +import RxSwift class GMSMapViewDelegateProxy: DelegateProxy, DelegateProxyType, GMSMapViewDelegate { diff --git a/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift b/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift index 88dcc961..4362eb92 100644 --- a/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift +++ b/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class LocationPermissionBottomSheet: UIViewController { diff --git a/Poppool/Poppool/Presentation/Map/Common/MapFilterChips.swift b/Poppool/Poppool/Presentation/Map/Common/MapFilterChips.swift index 7745525c..8006ce31 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapFilterChips.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapFilterChips.swift @@ -1,6 +1,5 @@ -import UIKit import SnapKit - +import UIKit class MapFilterChips: UIView { // MARK: - Components diff --git a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/MapPopupCarouselView.swift b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/MapPopupCarouselView.swift index cd217101..a8310561 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/MapPopupCarouselView.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/MapPopupCarouselView.swift @@ -1,6 +1,6 @@ -import UIKit -import SnapKit import FloatingPanel +import SnapKit +import UIKit final class MapPopupCarouselView: UICollectionView { // 스크롤 멈췄을 때의 콜백 @@ -44,7 +44,6 @@ final class MapPopupCarouselView: UICollectionView { ) super.init(frame: frame, collectionViewLayout: layout) - showsHorizontalScrollIndicator = false backgroundColor = .clear decelerationRate = .fast diff --git a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift index 4d1ee89d..27350c36 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift @@ -1,6 +1,6 @@ -import UIKit -import SnapKit import Kingfisher +import SnapKit +import UIKit final class PopupCardCell: UICollectionViewCell { static let identifier = "PopupCardCell" @@ -22,8 +22,6 @@ final class PopupCardCell: UICollectionViewCell { configureUI() } - - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -86,7 +84,6 @@ final class PopupCardCell: UICollectionViewCell { } } - private func configureUI() { contentView.backgroundColor = UIColor.white categoryLabel.textColor = .systemBlue diff --git a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift index ff6f6557..0860239c 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift @@ -1,5 +1,3 @@ - - import CoreLocation public func extractCity(from address: String) -> String { @@ -51,4 +49,3 @@ public struct RepresentativeScope { radius: 4000.0 ) } - diff --git a/Poppool/Poppool/Presentation/Map/CustomClusterRenderer.swift b/Poppool/Poppool/Presentation/Map/CustomClusterRenderer.swift index 611d0f3a..82fa4b7d 100644 --- a/Poppool/Poppool/Presentation/Map/CustomClusterRenderer.swift +++ b/Poppool/Poppool/Presentation/Map/CustomClusterRenderer.swift @@ -1,6 +1,6 @@ -//import GoogleMaps +// import GoogleMaps // -//class CustomClusterRenderer: GMUDefaultClusterRenderer { +// class CustomClusterRenderer: GMUDefaultClusterRenderer { // override func willRenderMarker(_ marker: GMSMarker) { // super.willRenderMarker(marker) // // 클러스터일 경우 처리 @@ -12,4 +12,4 @@ // marker.iconView = customView // } // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift index 3c673979..92956c8e 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class BalloonBackgroundView: UIView { @@ -15,7 +15,7 @@ final class BalloonBackgroundView: UIView { // 기존 말풍선 UI: 서브 지역을 나열하는 CollectionView (서울/경기/부산용) private let collectionView: UICollectionView = { - let layout = UICollectionViewCompositionalLayout { section, env in + let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(30), heightDimension: .absolute(30) @@ -177,7 +177,6 @@ final class BalloonBackgroundView: UIView { // layer.shadowRadius = 4 } - // MARK: - Public /// configure 메서드 @@ -214,7 +213,6 @@ final class BalloonBackgroundView: UIView { } } - private func setupTagSection() { let allKey = "\(mainRegionTitle)전체" @@ -236,7 +234,6 @@ final class BalloonBackgroundView: UIView { ) } - func calculateHeight() -> CGFloat { if collectionView.isHidden { return 145 @@ -293,8 +290,7 @@ final class BalloonBackgroundView: UIView { let iconWidth: CGFloat = isSelected ? 16 : 0 let iconGap: CGFloat = isSelected ? 4 : 0 let horizontalPadding: CGFloat = 24 - let calculatedWidth = textWidth + iconWidth + iconGap + horizontalPadding - return calculatedWidth + return textWidth + iconWidth + iconGap + horizontalPadding } } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift index 3919e1bf..e8c7dcd7 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class BalloonChipCell: UICollectionViewCell { static let identifier = "BalloonChipCell" @@ -46,7 +46,6 @@ final class BalloonChipCell: UICollectionViewCell { button.layer.borderWidth = 0 button.titleLabel?.font = .KorFont(style: .bold, size: 11) - } else { button.setImage(nil, for: .normal) button.semanticContentAttribute = .unspecified diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/CategoryFilterView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/CategoryFilterView.swift index b131a1b2..06fd3d99 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/CategoryFilterView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/CategoryFilterView.swift @@ -1,7 +1,7 @@ -//import UIKit -//import SnapKit +// import UIKit +// import SnapKit // -//final class CategoryFilterView: UIView { +// final class CategoryFilterView: UIView { // private let stackView = UIStackView() // private let categories = ["게임", "라이프스타일", "엔터테인먼트", "패션", "음식/요리", "키즈"] // @@ -32,4 +32,4 @@ // stackView.addArrangedSubview(chip) // } // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetReactor.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetReactor.swift index 190ad281..ed37ee77 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetReactor.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetReactor.swift @@ -1,5 +1,5 @@ -import ReactorKit import Foundation +import ReactorKit import RxSwift struct Location: Equatable { @@ -59,27 +59,27 @@ final class FilterBottomSheetReactor: Reactor { Location( main: "서울", sub: [ - "도봉/노원","강북/중랑","동대문/성북","중구/종로","성동/광진", - "송파/강동","동작/관악","서초/강남","은평/서대문/마포", - "영등포/구로","용산","양천/강서/금천" + "도봉/노원", "강북/중랑", "동대문/성북", "중구/종로", "성동/광진", + "송파/강동", "동작/관악", "서초/강남", "은평/서대문/마포", + "영등포/구로", "용산", "양천/강서/금천" ] ), Location( main: "경기", sub: [ - "포천/연천","동두천/양주/의정부","구리/남양주/가평", - "파주/고양/김포","용인/화성/수원","군포/의왕", - "과천/안양","부천/광명","시흥/안산", - "안성/평택/오산","성남/하남/광주","이천/여주/양평" + "포천/연천", "동두천/양주/의정부", "구리/남양주/가평", + "파주/고양/김포", "용인/화성/수원", "군포/의왕", + "과천/안양", "부천/광명", "시흥/안산", + "안성/평택/오산", "성남/하남/광주", "이천/여주/양평" ] ), Location(main: "인천", sub: ["부평", "송도"]), Location( main: "부산", sub: [ - "중구","서구","동구","영도구","부산진구", - "동래구","남구","북구","해운대구","사하구", - "금정구","강서구","연제구","수영구","사상구", + "중구", "서구", "동구", "영도구", "부산진구", + "동래구", "남구", "북구", "해운대구", "사하구", + "금정구", "강서구", "연제구", "수영구", "사상구", "기장군" ] ), @@ -177,14 +177,12 @@ final class FilterBottomSheetReactor: Reactor { switch mutation { case .setActiveSegment(let index): newState.activeSegment = index - - case .resetFilters: + case .resetFilters: newState.selectedSubRegions = [] newState.selectedCategories = [] newState.savedSubRegions = [] newState.savedCategories = [] // 여기서 forceSaveEnabled는 나중에 setForceSaveEnabled가 적용됨 - break case .applyFilters(let combined): print("필터 적용: \(newState.selectedSubRegions + newState.selectedCategories)") diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index 3301184c..15e91513 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class FilterBottomSheetView: UIView { // MARK: - UI Components @@ -32,8 +32,7 @@ final class FilterBottomSheetView: UIView { }() let segmentedControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) - return control + return PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) }() let locationScrollView: UIScrollView = { @@ -45,7 +44,7 @@ final class FilterBottomSheetView: UIView { var categoryHeightConstraint: Constraint? let categoryCollectionView: UICollectionView = { - let layout = UICollectionViewCompositionalLayout { section, env in + let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), heightDimension: .absolute(36) @@ -81,21 +80,16 @@ final class FilterBottomSheetView: UIView { return collectionView }() - let balloonBackgroundView = BalloonBackgroundView() let resetButton: PPButton = { - let button = PPButton(style: .secondary, text: "초기화") - return button + return PPButton(style: .secondary, text: "초기화") }() - let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - return button + return PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") }() - private let buttonStack: UIStackView = { let stack = UIStackView() stack.axis = .horizontal @@ -105,14 +99,13 @@ final class FilterBottomSheetView: UIView { }() let filterChipsView: FilterChipsView = { - let view = FilterChipsView() - return view + return FilterChipsView() }() private var balloonHeightConstraint: Constraint? // MARK: - Initialization - + override init(frame: CGRect) { super.init(frame: frame) setupLayout() @@ -330,9 +323,6 @@ final class FilterBottomSheetView: UIView { } } - - - private func createStyledButton(title: String, isSelected: Bool = false) -> PPButton { let button = PPButton( style: .secondary, @@ -368,7 +358,7 @@ final class FilterBottomSheetView: UIView { button.setBackgroundColor(.w100, for: .normal) button.setTitleColor(.g400, for: .normal) button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .KorFont(style: .medium, size: 13) + button.titleLabel?.font = .KorFont(style: .medium, size: 13) button.layer.borderWidth = 1 } } @@ -382,7 +372,6 @@ final class FilterBottomSheetView: UIView { } } - func updateBalloonPosition(for button: UIButton) { guard let window = button.window else { return } @@ -424,7 +413,7 @@ extension FilterBottomSheetView { filterChipsView.updateChips(with: filters) } -} +} extension FilterBottomSheetView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { // 선택된 버튼 찾기 @@ -432,7 +421,7 @@ extension FilterBottomSheetView: UIScrollViewDelegate { guard let button = view as? PPButton else { return false } return button.backgroundColor == .blu500 }) as? PPButton else { return } - + // 스크롤 중에도 실시간으로 위치 업데이트 DispatchQueue.main.async { [weak self] in self?.updateBalloonPosition(for: selectedButton) diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift index c398fc99..c767efbc 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift @@ -1,8 +1,8 @@ -import UIKit -import SnapKit -import RxSwift -import RxCocoa import ReactorKit +import RxCocoa +import RxSwift +import SnapKit +import UIKit final class FilterBottomSheetViewController: UIViewController, View { typealias Reactor = FilterBottomSheetReactor @@ -114,14 +114,11 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - }) .map { Reactor.Action.resetFilters } .bind(to: reactor.action) .disposed(by: disposeBag) - - containerView.saveButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -138,7 +135,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - containerView.closeButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -151,7 +147,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - // 5. 탭 변경 reactor.state.map { $0.activeSegment } .distinctUntilChanged() @@ -193,7 +188,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - let locationAndSubRegions = reactor.state .map { ($0.selectedLocationIndex, $0.selectedSubRegions) } .distinctUntilChanged { prev, curr in @@ -209,7 +203,6 @@ final class FilterBottomSheetViewController: UIViewController, View { guard let self = self, let reactor = self.reactor else { return } let (selectedIndexOptional, selectedSubRegions) = data - guard let selectedIndex = selectedIndexOptional, selectedIndex >= 0, selectedIndex < reactor.currentState.locations.count else { return } @@ -227,7 +220,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - if let button = self.containerView.locationContentView.subviews[selectedIndex] as? UIButton { self.containerView.updateBalloonPosition(for: button) } @@ -266,8 +258,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - - reactor.state.map { $0.selectedSubRegions + $0.selectedCategories } .distinctUntilChanged() .bind { [weak self] selectedOptions in @@ -288,7 +278,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - reactor.state.map { $0.isSaveEnabled } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -386,7 +375,6 @@ final class FilterBottomSheetViewController: UIViewController, View { let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { reactor.action.onNext(.selectLocation(index)) - } // 4. 필터 칩 뷰 업데이트 @@ -403,8 +391,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } } - - func hideBottomSheet() { UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { self.dimmedView.alpha = 0 diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterCell.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterCell.swift index f5783bfa..5c8981b7 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterCell.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterCell.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class FilterCell: UICollectionViewCell { // MARK: - Properties diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChip.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChip.swift index 0aa4964c..3ad18fc0 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChip.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChip.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class FilterChip: UIButton { enum Style { diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift index 68aa555a..e0f439c6 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterChipsView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class FilterChipsView: UIView { // MARK: - Components @@ -97,7 +97,7 @@ final class FilterChipsView: UIView { let removedFilter = filters[index] filters.remove(at: index) updateUI() - + // 콜백 호출 onRemoveChip?(removedFilter) } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterTabsView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterTabsView.swift index 85a3812c..99dfe49c 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterTabsView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterTabsView.swift @@ -1,8 +1,8 @@ -//import UIKit -//import RxSwift -//import RxCocoa +// import UIKit +// import RxSwift +// import RxCocoa // -//final class FilterTabsView: UIView { +// final class FilterTabsView: UIView { // private let tabs = ["지역", "카테고리"] // let segmentedControl = UISegmentedControl() // @@ -30,10 +30,10 @@ // make.edges.equalToSuperview() // } // } -//} +// } // -//extension Reactive where Base: FilterTabsView { +// extension Reactive where Base: FilterTabsView { // var selectedIndex: ControlProperty { // return base.segmentedControl.rx.selectedSegmentIndex // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/LocationFilterView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/LocationFilterView.swift index 4f4bbdf4..567dfd1d 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/LocationFilterView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/LocationFilterView.swift @@ -1,7 +1,7 @@ -//import UIKit -//import SnapKit +// import UIKit +// import SnapKit // -//final class LocationFilterView: UIView { +// final class LocationFilterView: UIView { // private let scrollView = UIScrollView() // private let contentStack = UIStackView() // @@ -37,4 +37,4 @@ // contentStack.addArrangedSubview(chip) // } // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift index 430b5a91..691cf788 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FindDirectionEndPoint.swift @@ -19,4 +19,3 @@ struct FindDirectionEndPoint { ) } } - diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 690afbd7..e788bb2f 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,9 +1,9 @@ -import Foundation -import UIKit -import RxSwift -import ReactorKit import CoreLocation +import Foundation import GoogleMaps +import ReactorKit +import RxSwift +import UIKit final class FullScreenMapViewController: MapViewController { var selectedStore: MapPopUpStore? @@ -29,8 +29,6 @@ final class FullScreenMapViewController: MapViewController { } } - - // MARK: - Binding override func bind(reactor: Reactor) { super.bind(reactor: reactor) @@ -158,7 +156,6 @@ final class FullScreenMapViewController: MapViewController { } } - @objc private func backButtonTapped() { dismiss(animated: true) } diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/GetPopUpDirectionResponseDTO.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/GetPopUpDirectionResponseDTO.swift index fa31b5d8..cd67dfee 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/GetPopUpDirectionResponseDTO.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/GetPopUpDirectionResponseDTO.swift @@ -7,7 +7,6 @@ import Foundation - struct GetPopUpDirectionResponseDTO: Decodable { let id: Int64 let categoryName: String diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapDirectionRepository.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapDirectionRepository.swift index 39b05082..fcb8e508 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapDirectionRepository.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapDirectionRepository.swift @@ -8,7 +8,6 @@ import Foundation import RxSwift - protocol MapDirectionRepository { func getPopUpDirection(popUpStoreId: Int64) -> Observable } @@ -25,7 +24,7 @@ final class DefaultMapDirectionRepository: MapDirectionRepository { let endpoint = FindDirectionEndPoint.fetchDirection(popUpStoreId: popUpStoreId) // print("🌎 [Repository]: 요청 생성 - \(endpoint)") return provider.requestData(with: endpoint, interceptor: TokenInterceptor()) - .do(onNext: { response in + .do(onNext: { _ in // print("✅ [Repository]: 응답 수신 - \(response)") }, onError: { error in print("❌ [Repository]: 요청 실패 - \(error)") diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideReactor.swift index 089cb950..dd3fef65 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -1,6 +1,6 @@ +import CoreLocation import ReactorKit import RxSwift -import CoreLocation import UIKit final class MapGuideReactor: Reactor { diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index c07ed2b8..cec51aea 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -1,9 +1,9 @@ -import UIKit -import SnapKit +import CoreLocation import GoogleMaps import ReactorKit import RxSwift -import CoreLocation +import SnapKit +import UIKit final class MapGuideViewController: UIViewController, View { // MARK: - Properties @@ -171,7 +171,7 @@ final class MapGuideViewController: UIViewController, View { .distinctUntilChanged() .compactMap { $0 } .take(1) - .subscribe(onNext: { [weak self] store in + .subscribe(onNext: { [weak self] _ in let fullScreenMapVC = FullScreenMapViewController() fullScreenMapVC.reactor = reactor diff --git a/Poppool/Poppool/Presentation/Map/MapStoreCard.swift b/Poppool/Poppool/Presentation/Map/MapStoreCard.swift index 9647c3d4..a4c86d8f 100644 --- a/Poppool/Poppool/Presentation/Map/MapStoreCard.swift +++ b/Poppool/Poppool/Presentation/Map/MapStoreCard.swift @@ -1,7 +1,7 @@ -//import UIKit -//import SnapKit +// import UIKit +// import SnapKit // -//final class MapStoreCard: UIView { +// final class MapStoreCard: UIView { // // MARK: - Components // private let containerView: UIView = { // let view = UIView() @@ -43,10 +43,10 @@ // required init?(coder: NSCoder) { // fatalError("init(coder:) has not been implemented") // } -//} +// } // //// MARK: - Setup -//private extension MapStoreCard { +// private extension MapStoreCard { // func setupLayout() { // addSubview(containerView) // @@ -94,10 +94,10 @@ // locationLabel.textColor = .g500 // dateLabel.textColor = .g500 // } -//} +// } // //// MARK: - Inputable -//extension MapStoreCard: Inputable { +// extension MapStoreCard: Inputable { // struct Input { // let image: UIImage? // let category: String @@ -113,4 +113,4 @@ // locationLabel.text = input.location // dateLabel.text = input.date // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift index a90eb377..b3c06309 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift @@ -1,13 +1,12 @@ -import UIKit -import SnapKit import GoogleMaps +import SnapKit +import UIKit final class MapMarker: UIView { // MARK: - Components private(set) var isSelected: Bool = false var currentInput: Input? - private let markerImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "Marker") @@ -181,8 +180,6 @@ extension MapMarker: Inputable { CATransaction.commit() } - - private func setupClusterMarker(_ input: Input) { markerImageView.isHidden = true clusterContainer.isHidden = false diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift index 8603c691..0342470b 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift @@ -1,6 +1,6 @@ +import CoreLocation import ReactorKit import RxSwift -import CoreLocation final class MapReactor: Reactor { // MARK: - Reactor @@ -166,8 +166,6 @@ final class MapReactor: Reactor { .just(.setLoading(false)) ]) - - case let .updateBothFilters(locations, categories): return .concat([ .just(.setLocationFilters(locations)), @@ -302,10 +300,9 @@ final class MapReactor: Reactor { case let .didSelectItem(store): return .concat([ .just(.setSelectedStore(store)), - .just(.setViewportStores(currentState.viewportStores)), // ✅ 선택된 마커를 캐러셀에서 최우선으로 반영 + .just(.setViewportStores(currentState.viewportStores)) // ✅ 선택된 마커를 캐러셀에서 최우선으로 반영 ]) - default: return .empty() } @@ -387,7 +384,6 @@ final class MapReactor: Reactor { newState.viewportStores = updatedStores - case let .setSelectedStore(store): newState.selectedStore = store print("[DEBUG] 📍 Selected Store: \(store.name)") diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapSearchInput.swift b/Poppool/Poppool/Presentation/Map/MapView/MapSearchInput.swift index f10a3973..e5532286 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapSearchInput.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapSearchInput.swift @@ -1,7 +1,7 @@ -import UIKit import ReactorKit import RxCocoa import RxSwift +import UIKit final class MapSearchInput: UIView, View { // MARK: - Components diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift index 30d4f7c2..a624a152 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift @@ -1,6 +1,6 @@ -import UIKit -import SnapKit import GoogleMaps +import SnapKit +import UIKit final class MapView: UIView { // MARK: - Components @@ -52,8 +52,7 @@ final class MapView: UIView { }() var storeCard: MapPopupCarouselView = { - let view = MapPopupCarouselView() - return view + return MapPopupCarouselView() }() // MARK: - Init @@ -104,7 +103,7 @@ private extension MapView { searchFilterContainer.addSubview(searchInput) searchInput.snp.makeConstraints { make in make.top.equalToSuperview() - make.leading.trailing.equalToSuperview().inset(20) + make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(37) } diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index 52bf64b6..ebed5d27 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -1,18 +1,16 @@ -import UIKit +import CoreLocation import FloatingPanel -import SnapKit -import RxSwift -import RxCocoa -import ReactorKit import GoogleMaps -import CoreLocation +import ReactorKit +import RxCocoa import RxGesture - +import RxSwift +import SnapKit +import UIKit class MapViewController: BaseViewController, View { typealias Reactor = MapReactor - fileprivate struct CoordinateKey: Hashable { let lat: Int let lng: Int @@ -28,7 +26,6 @@ class MapViewController: BaseViewController, View { var currentTooltipStores: [MapPopUpStore] = [] var currentTooltipCoordinate: CLLocationCoordinate2D? - // MARK: - Properties private var storeDetailsCache: [Int64: StoreItem] = [:] private var isMovingToMarker = false @@ -82,7 +79,6 @@ class MapViewController: BaseViewController, View { setUp() mainView.mapView.padding = UIEdgeInsets(top: 0, left: 0, bottom: 10, right: 0) - locationManager.delegate = self locationManager.requestWhenInUseAuthorization() locationManager.desiredAccuracy = kCLLocationAccuracyBest @@ -133,8 +129,6 @@ class MapViewController: BaseViewController, View { .disposed(by: disposeBag) } - - carouselView.rx.observe(Bool.self, "hidden") .distinctUntilChanged() .subscribe(onNext: { [weak self] isHidden in @@ -171,9 +165,6 @@ class MapViewController: BaseViewController, View { }) .disposed(by: disposeBag) - - - carouselView.onCardScrolled = { [weak self] pageIndex in guard let self = self, pageIndex >= 0, @@ -302,7 +293,7 @@ class MapViewController: BaseViewController, View { let markerPoint = self.mainView.mapView.projection.point(for: marker.position) let markerHeight = (marker.iconView as? MapMarker)?.imageView.frame.height ?? 32 tooltipView.frame = CGRect( - x: markerPoint.x , // 마커 오른쪽 10포인트 + x: markerPoint.x, y: markerPoint.y - markerHeight - tooltipView.frame.height - 14, width: tooltipView.frame.width, height: tooltipView.frame.height @@ -447,10 +438,6 @@ class MapViewController: BaseViewController, View { } .disposed(by: disposeBag) - - - - mainView.filterChips.onRemoveLocation = { [weak self] in guard let self = self else { return } // 필터 제거 액션 @@ -545,7 +532,6 @@ class MapViewController: BaseViewController, View { } .disposed(by: disposeBag) - reactor.state.map { $0.activeFilterType } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -599,8 +585,6 @@ class MapViewController: BaseViewController, View { }) .disposed(by: disposeBag) - - reactor.state.map { $0.searchResults } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -653,9 +637,6 @@ class MapViewController: BaseViewController, View { } .disposed(by: disposeBag) - - - // reactor.state.map { $0.searchResults.isEmpty } // .distinctUntilChanged() // .skip(1) // 초기값 스킵 @@ -672,7 +653,6 @@ class MapViewController: BaseViewController, View { // .disposed(by: disposeBag) } - // MARK: - List View Control private func toggleListView() { UIView.animate(withDuration: 0.3) { @@ -685,7 +665,6 @@ class MapViewController: BaseViewController, View { } - func addMarker(for store: MapPopUpStore) { let marker = GMSMarker() marker.position = store.coordinate @@ -772,7 +751,6 @@ class MapViewController: BaseViewController, View { } } - private func updateMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { let progress = (maxOffset - offset) / (maxOffset - minOffset) // 0(탑) ~ 1(바텀) mainView.mapView.alpha = max(0, min(progress, 1)) // 0(완전히 가림) ~ 1(완전히 보임) @@ -793,8 +771,6 @@ class MapViewController: BaseViewController, View { self.listViewTopConstraint?.update(offset: filterChipsFrame.maxY) self.mainView.searchInput.setBackgroundColor(.g50) - - case .middle: self.storeListViewController.setGrabberHandleVisible(true) let offset = max(self.view.frame.height * 0.3, self.filterContainerBottomY) @@ -860,7 +836,6 @@ class MapViewController: BaseViewController, View { } } - // updateMapWithClustering() 메서드 전체 구현 private func updateMapWithClustering() { let currentZoom = mainView.mapView.camera.zoom @@ -876,7 +851,6 @@ class MapViewController: BaseViewController, View { // 현재 화면에 보이는 스토어 업데이트 currentStores = visibleStores - CATransaction.begin() CATransaction.setDisableActions(true) @@ -1041,7 +1015,6 @@ class MapViewController: BaseViewController, View { return dict } - private func updateIndividualMarkers(_ stores: [MapPopUpStore]) { var newMarkerIDs = Set() @@ -1101,8 +1074,6 @@ class MapViewController: BaseViewController, View { } } - - func presentFilterBottomSheet(for filterType: FilterType) { guard let reactor = self.reactor else { return } @@ -1150,7 +1121,7 @@ class MapViewController: BaseViewController, View { } currentFilterBottomSheet = nil } - //기본 마커 + // 기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { mainView.mapView.clear() markerDictionary.removeAll() @@ -1215,8 +1186,6 @@ class MapViewController: BaseViewController, View { ) } - - // MARK: - Location private func checkLocationAuthorization() { switch locationManager.authorizationStatus { @@ -1264,7 +1233,6 @@ extension MapViewController: CLLocationManagerDelegate { locationManager.stopUpdatingLocation() } - private func findAndShowNearestStore(from location: CLLocation) { guard !currentStores.isEmpty else { Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) @@ -1341,7 +1309,6 @@ extension MapViewController: GMSMapViewDelegate { return false } - func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) { if !isMovingToMarker { currentTooltipView?.removeFromSuperview() @@ -1356,8 +1323,6 @@ extension MapViewController: GMSMapViewDelegate { } } - - func mapView(_ mapView: GMSMapView, willMove gesture: Bool) { if gesture && !isMovingToMarker { resetSelectedMarker() @@ -1396,9 +1361,6 @@ extension MapViewController: GMSMapViewDelegate { updateMapWithClustering() } - - - // MARK: - Helper for single marker tap func handleSingleStoreTap(_ marker: GMSMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true @@ -1477,9 +1439,6 @@ extension MapViewController: GMSMapViewDelegate { return true } - - - func handleRegionalClusterTap(_ marker: GMSMarker, clusterData: ClusterMarkerData) -> Bool { let currentZoom = mainView.mapView.camera.zoom let currentLevel = MapZoomLevel.getLevel(from: currentZoom) @@ -1633,10 +1592,8 @@ extension MapViewController: GMSMapViewDelegate { self.currentMarker = nil } - } - extension MapViewController { func bindViewport(reactor: MapReactor) { let cameraObservable = Observable.merge([ @@ -1693,7 +1650,6 @@ extension MapViewController { }) .disposed(by: disposeBag) - // 뷰포트 내 마커 업데이트 및 캐러셀 표시 (수정된 부분) reactor.state .map { $0.viewportStores } @@ -1841,7 +1797,6 @@ extension MapViewController { } } - private func findMarkerForStore(for store: MapPopUpStore) -> GMSMarker? { // individualMarkerDictionary에 저장된 모든 마커를 순회 for marker in individualMarkerDictionary.values { @@ -1878,8 +1833,6 @@ extension MapViewController { } } - - private func handleMarkerTap(_ marker: GMSMarker) -> Bool { isMovingToMarker = true @@ -1931,7 +1884,6 @@ private func handleMarkerTap(_ marker: GMSMarker) -> Bool { return true } - private func getCurrentViewportBounds() -> (northEast: CLLocationCoordinate2D, southWest: CLLocationCoordinate2D) { let region = mainView.mapView.projection.visibleRegion() return (northEast: region.farRight, southWest: region.nearLeft) diff --git a/Poppool/Poppool/Presentation/Map/MapView/MarkerTooltipView.swift b/Poppool/Poppool/Presentation/Map/MapView/MarkerTooltipView.swift index 936b8848..a1c2bffe 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MarkerTooltipView.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MarkerTooltipView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { diff --git a/Poppool/Poppool/Presentation/Map/MicroClusterMarkerView.swift b/Poppool/Poppool/Presentation/Map/MicroClusterMarkerView.swift index f41f6569..f7d21f38 100644 --- a/Poppool/Poppool/Presentation/Map/MicroClusterMarkerView.swift +++ b/Poppool/Poppool/Presentation/Map/MicroClusterMarkerView.swift @@ -1,3 +1,2 @@ - import Foundation import UIKit diff --git a/Poppool/Poppool/Presentation/Map/StoreListPanelLayout.swift b/Poppool/Poppool/Presentation/Map/StoreListPanelLayout.swift index 842bc1de..5dd5201d 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListPanelLayout.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListPanelLayout.swift @@ -1,8 +1,8 @@ // -//import FloatingPanel -//import UIKit +// import FloatingPanel +// import UIKit // -//class StoreListPanelLayout: FloatingPanelLayout { +// class StoreListPanelLayout: FloatingPanelLayout { // let position: FloatingPanelPosition = .bottom // let initialState: FloatingPanelState = .half // @@ -27,4 +27,4 @@ // func surfaceLayout(for size: CGSize) -> NSCollectionLayoutDimension { // return .fractionalWidth(1.0) // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListCell.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListCell.swift index e8e5e003..7b12cfb5 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListCell.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListCell.swift @@ -1,7 +1,7 @@ -import UIKit -import SnapKit -import RxSwift import ReactorKit +import RxSwift +import SnapKit +import UIKit final class StoreListCell: UICollectionViewCell { static let identifier = "StoreListCell" @@ -48,7 +48,7 @@ final class StoreListCell: UICollectionViewCell { private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12, text: "") label.textColor = .g400 - label.numberOfLines = 2 + label.numberOfLines = 2 return label }() diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListHeaderView.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListHeaderView.swift index 7911caca..65f574b0 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListHeaderView.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListHeaderView.swift @@ -1,10 +1,10 @@ // -//import UIKit -//import SnapKit -//import RxSwift +// import UIKit +// import SnapKit +// import RxSwift // // -//class StoreListHeaderView: UICollectionReusableView { +// class StoreListHeaderView: UICollectionReusableView { // static let identifier = "StoreListHeaderView" // // let searchInput = MapSearchInput() @@ -51,4 +51,4 @@ // // // } -//} +// } diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListPanelLayout.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListPanelLayout.swift index f5eee867..ab3b649d 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListPanelLayout.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListPanelLayout.swift @@ -20,9 +20,6 @@ class StoreListPanelLayout: FloatingPanelLayout { ] } - - - func backdropAlpha(for state: FloatingPanelState) -> CGFloat { return 0.0 } diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift index 864d2426..f335e42c 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListReactor.swift @@ -1,8 +1,7 @@ -import ReactorKit -import RxSwift import Foundation +import ReactorKit import RxCocoa - +import RxSwift final class StoreListReactor: Reactor { // MARK: - Reactor @@ -10,7 +9,6 @@ final class StoreListReactor: Reactor { private let popUpAPIUseCase: PopUpAPIUseCaseImpl private let bookmarkStateRelay = PublishRelay<(Int64, Bool)>() - // private var currentPage = 0 // private let pageSize = 10 // private var hasMorePages = true @@ -25,7 +23,6 @@ final class StoreListReactor: Reactor { case clearFilters(FilterType) case updateStoreBookmark(id: Int64, isBookmarked: Bool) // 추가 - } enum Mutation { @@ -59,7 +56,6 @@ final class StoreListReactor: Reactor { self.initialState = State() } - // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -84,7 +80,7 @@ final class StoreListReactor: Reactor { return .empty() } - let bookmarkRequest = popUpAPIUseCase.getPopUpDetail( + return popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", popUpStoredId: Int64(idInt32) // Int32 → Int64 변환 ) @@ -102,10 +98,6 @@ final class StoreListReactor: Reactor { ])) } - return bookmarkRequest - - - // // case let .setStores(storeItems): // return Observable.from(storeItems) @@ -160,7 +152,6 @@ final class StoreListReactor: Reactor { } return .empty() - case let .filterTapped(filterType): return .just(.setActiveFilter(filterType)) @@ -182,7 +173,6 @@ final class StoreListReactor: Reactor { } } - func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -206,10 +196,9 @@ final class StoreListReactor: Reactor { ) } - case let .showBookmarkToast(isBookmarked): if currentState.stores.isEmpty { - break + break } newState.shouldShowBookmarkToast = isBookmarked @@ -236,11 +225,8 @@ final class StoreListReactor: Reactor { bookmarkStateRelay.accept((storeId, isBookmarked)) } - - } - // MARK: - Model struct StoreItem { let id: Int64 diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListView.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListView.swift index 63f88bc5..79f01619 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListView.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class StoreListView: UIView { // MARK: - Components diff --git a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift index e8fb9c69..5ffbaa5e 100644 --- a/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/Poppool/Presentation/Map/StoreListView/StoreListViewController.swift @@ -1,10 +1,10 @@ -import UIKit -import SnapKit -import RxCocoa -import RxSwift -import ReactorKit import FloatingPanel +import ReactorKit +import RxCocoa import RxDataSources +import RxSwift +import SnapKit +import UIKit final class StoreListViewController: UIViewController, View { typealias Reactor = StoreListReactor @@ -56,7 +56,7 @@ final class StoreListViewController: UIViewController, View { func bind(reactor: Reactor) { let dataSource = RxCollectionViewSectionedReloadDataSource( - configureCell: { [weak self] ds, cv, indexPath, item in + configureCell: { [weak self] _, cv, indexPath, item in guard let self = self else { return UICollectionViewCell() } let cell = cv.dequeueReusableCell( withReuseIdentifier: StoreListCell.identifier, @@ -64,7 +64,7 @@ final class StoreListViewController: UIViewController, View { ) as! StoreListCell cell.injection(with: .init( - thumbnailURL: item.thumbnailURL, + thumbnailURL: item.thumbnailURL, category: item.category, title: item.title, location: item.location, @@ -124,7 +124,6 @@ final class StoreListViewController: UIViewController, View { }) .disposed(by: disposeBag) - // 4) viewWillAppear -> viewDidLoad // rx.viewWillAppear // .map { _ in Reactor.Action.viewDidLoad } @@ -155,7 +154,7 @@ final class StoreListViewController: UIViewController, View { // .compactMap { $0 } // .bind(to: reactor.action) // .disposed(by: disposeBag) - + } private func presentFilterBottomSheet(for filterType: FilterType) { @@ -167,7 +166,7 @@ final class StoreListViewController: UIViewController, View { viewController.containerView.segmentedControl.selectedSegmentIndex = initialIndex sheetReactor.action.onNext(.segmentChanged(initialIndex)) - viewController.onSave = { [weak self] selectedOptions in + viewController.onSave = { [weak self] _ in guard let self = self else { return } // 닫기 self.reactor?.action.onNext(.filterTapped(nil)) diff --git a/Poppool/Poppool/Presentation/Map/TestViewController.swift b/Poppool/Poppool/Presentation/Map/TestViewController.swift index 8a35c50d..82bd615b 100644 --- a/Poppool/Poppool/Presentation/Map/TestViewController.swift +++ b/Poppool/Poppool/Presentation/Map/TestViewController.swift @@ -7,87 +7,86 @@ import UIKit -import SnapKit -import RxSwift -import RxGesture import RxCocoa +import RxGesture +import RxSwift +import SnapKit class TestViewController: UIViewController { - + private let topView: UIView = { let view = UIView() view.backgroundColor = .w100 view.alpha = 0 return view }() - + private let topViewLabel: UILabel = { let label = UILabel() label.text = "Top View Label" return label }() - + private let bottomView: UIView = { let view = UIView() view.backgroundColor = .w100 return view }() - + private let gestureBar: UIView = { let view = UIView() view.backgroundColor = .g200 return view }() - + private let listButton: PPButton = { - let button = PPButton(style: .secondary, text: "리스트 버튼") - return button + return PPButton(style: .secondary, text: "리스트 버튼") }() - + private let disposeBag = DisposeBag() - + private var bottomViewTopConstraints: Constraint? - + enum ModalState { case top case middle case bottom } - + var modalState: ModalState = .bottom - + override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .blue setUpConstratins() bind() } - + func setUpConstratins() { view.addSubview(listButton) listButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(50) } - + view.addSubview(topView) topView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide.snp.top).offset(104) } - + topView.addSubview(topViewLabel) topViewLabel.snp.makeConstraints { make in make.center.equalToSuperview() } - + view.addSubview(bottomView) bottomView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() bottomViewTopConstraints = make.top.equalTo(topView.snp.bottom).offset(700).constraint make.height.equalTo(700) } - + bottomView.addSubview(gestureBar) gestureBar.snp.makeConstraints { make in make.width.equalTo(50) @@ -96,7 +95,7 @@ class TestViewController: UIViewController { make.centerX.equalToSuperview() } } - + func bind() { listButton.rx.tap .withUnretained(self) @@ -109,11 +108,11 @@ class TestViewController: UIViewController { } } .disposed(by: disposeBag) - + gestureBar.rx.swipeGesture(.up) .skip(1) .withUnretained(self) - .subscribe { (owner, gesture) in + .subscribe { (owner, _) in print("swipe up") UIView.animate(withDuration: 0.3) { owner.bottomViewTopConstraints?.update(offset: 0) @@ -123,11 +122,11 @@ class TestViewController: UIViewController { } } .disposed(by: disposeBag) - + gestureBar.rx.swipeGesture(.down) .skip(1) .withUnretained(self) - .subscribe { (owner, gesture) in + .subscribe { (owner, _) in print("swipe down") switch owner.modalState { case .top: diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift index 3bee6c71..f1e20e9f 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentCheckController: BaseViewController, View { - + typealias Reactor = CommentCheckReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentCheckView() } @@ -48,7 +48,7 @@ extension CommentCheckController { .map { Reactor.Action.continueButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.stopButton.rx.tap .map { Reactor.Action.stopButtonTapped } .bind(to: reactor.action) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift index 4afbec9d..9653a396 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift @@ -6,41 +6,41 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentCheckReactor: Reactor { - + // MARK: - Reactor enum Action { case continueButtonTapped case stopButtonTapped } - + enum Mutation { case setSelectedType(type: SelectedType) } - + struct State { var selectedType: SelectedType = .none } - + enum SelectedType { case none case continues case stop } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -50,7 +50,7 @@ final class CommentCheckReactor: Reactor { return Observable.just(.setSelectedType(type: .stop)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift index 51d6458e..c16fb508 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift @@ -10,42 +10,39 @@ import UIKit import SnapKit final class CommentCheckView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "코멘트 작성을 그만하시겠어요?") - return label + return PPLabel(style: .bold, fontSize: 18, text: "코멘트 작성을 그만하시겠어요?") }() - + private let descriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 14, text: "화면을 나가실 경우 작성중인 내용은 저장되지 않아요.") label.textColor = .g600 return label }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + let continueButton: PPButton = { - let button = PPButton(style: .secondary, text: "계속하기") - return button + return PPButton(style: .secondary, text: "계속하기") }() - + let stopButton: PPButton = { - let button = PPButton(style: .primary, text: "그만하기") - return button + return PPButton(style: .primary, text: "그만하기") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -53,20 +50,20 @@ final class CommentCheckView: UIView { // MARK: - SetUp private extension CommentCheckView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(32) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + buttonStackView.addArrangedSubview(continueButton) buttonStackView.addArrangedSubview(stopButton) self.addSubview(buttonStackView) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift index e4fb63a0..4196ffe8 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift @@ -7,23 +7,23 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentDetailController: BaseViewController, View { - + typealias Reactor = CommentDetailReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = CommentDetailView() - + private var sections: [any Sectionable] = [] - + private var cellTapped: PublishSubject = .init() } @@ -47,16 +47,16 @@ private extension CommentDetailController { DetailCommentImageCell.self, forCellWithReuseIdentifier: DetailCommentImageCell.identifiers ) - + mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( CommentDetailContentSectionCell.self, forCellWithReuseIdentifier: CommentDetailContentSectionCell.identifiers ) - + view.addSubview(mainView) mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) @@ -72,7 +72,7 @@ extension CommentDetailController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, indexPath) in @@ -80,12 +80,12 @@ extension CommentDetailController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.likeButton.rx.tap .map { Reactor.Action.likeButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -112,19 +112,18 @@ extension CommentDetailController: UICollectionViewDelegate, UICollectionViewDat func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let _ = collectionView.cellForItem(at: indexPath) as? DetailCommentImageCell { cellTapped.onNext(indexPath) @@ -139,11 +138,11 @@ extension CommentDetailController: PanModalPresentable { var longFormHeight: PanModalHeight { return .contentHeight(UIScreen.main.bounds.height - 68) } - + var shortFormHeight: PanModalHeight { return .contentHeight(UIScreen.main.bounds.height - 68) } - + var showDragIndicator: Bool { return false } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 978cb6bf..8b7d28f3 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -8,36 +8,36 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentDetailReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case imageCellTapped(controller: BaseViewController, row: Int) case likeButtonTapped } - + enum Mutation { case loadView case presentImageDetailView(controller: BaseViewController, row: Int) case likeChange } - + struct State { var commentData: DetailCommentSection.CellType.Input var sections: [any Sectionable] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -51,17 +51,17 @@ final class CommentDetailReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var imageSection = CommentDetailImageSection(inputDataList: []) private var contentSection = CommentDetailContentSection(inputDataList: []) - + private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - + // MARK: - init init(comment: DetailCommentSection.CellType.Input) { self.initialState = State(commentData: comment) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -73,7 +73,7 @@ final class CommentDetailReactor: Reactor { return Observable.just(.presentImageDetailView(controller: controller, row: row)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -93,26 +93,26 @@ final class CommentDetailReactor: Reactor { if newState.commentData.isLike { newState.commentData.likeCount += 1 userAPIUseCase.postCommentLike(commentId: newState.commentData.commentID) - .subscribe(onDisposed: { + .subscribe(onDisposed: { Logger.log(message: "CommentLike", category: .info) }) .disposed(by: disposeBag) } else { newState.commentData.likeCount -= 1 userAPIUseCase.deleteCommentLike(commentId: newState.commentData.commentID) - .subscribe(onDisposed: { + .subscribe(onDisposed: { Logger.log(message: "CommentLikeDelete", category: .info) }) .disposed(by: disposeBag) } newState.sections = getSection() } - + return newState } - + func getSection() -> [any Sectionable] { - if imageSection.isEmpty { + if imageSection.isEmpty { return [ contentSection ] diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift index 1bb2c50e..b127b716 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct CommentDetailContentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = CommentDetailContentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct CommentDetailContentSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift index 17650a73..0d814d0a 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class CommentDetailContentSectionCell: UICollectionViewCell { - + // MARK: - Components private let contentLabel: PPLabel = { @@ -19,15 +19,15 @@ final class CommentDetailContentSectionCell: UICollectionViewCell { label.numberOfLines = 0 return label }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -47,7 +47,7 @@ extension CommentDetailContentSectionCell: Inputable { struct Input { var content: String? } - + func injection(with input: Input) { contentLabel.setLineHeightText(text: input.content, font: .KorFont(style: .medium, size: 13)) } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift index a2644557..a3c61126 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct CommentDetailImageSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailCommentImageCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(80), diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift index 6bf879f4..c75acd0f 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift @@ -10,42 +10,40 @@ import UIKit import SnapKit final class CommentDetailView: UIView { - + // MARK: - Components let profileView: DetailCommentProfileView = { let view = DetailCommentProfileView() view.button.isHidden = true return view }() - + let likeButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let likeButtonTitleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13, text: "도움돼요") label.textColor = .g400 return label }() - + let likeButtonImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_like_gray") return view }() - + let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -53,32 +51,32 @@ final class CommentDetailView: UIView { // MARK: - SetUp private extension CommentDetailView { - + func setUpConstraints() { self.addSubview(profileView) profileView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) make.top.equalToSuperview().inset(40) } - + likeButton.addSubview(likeButtonTitleLabel) likeButtonTitleLabel.snp.makeConstraints { make in make.height.equalTo(20).priority(.high) make.top.bottom.trailing.equalToSuperview() } - + likeButton.addSubview(likeButtonImageView) likeButtonImageView.snp.makeConstraints { make in make.size.equalTo(20) make.leading.centerY.equalToSuperview() make.trailing.equalTo(likeButtonTitleLabel.snp.leading) } - + self.addSubview(likeButton) likeButton.snp.makeConstraints { make in make.bottom.trailing.equalToSuperview().inset(20) } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(profileView.snp.bottom).offset(16) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift index 3dcc05c4..088653fe 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentMyMenuController: BaseViewController, View { - + typealias Reactor = CommentMyMenuReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentMyMenuView() } @@ -50,14 +50,14 @@ extension CommentMyMenuController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.commentRemoveButton.rx.tap .map { _ in Reactor.Action.removeButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.commentEditButton.rx.tap .map { _ in Reactor.Action.editButtonTapped diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift index 25324ff7..caebe2ca 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift @@ -6,28 +6,28 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentMyMenuReactor: Reactor { - + // MARK: - Reactor enum Action { case cancelButtonTapped case removeButtonTapped case editButtonTapped } - + enum Mutation { case moveToRecentScene case setRemoveType case setEditType } - + struct State { var selectedType: SelectedType = .none } - + enum SelectedType { case none case cancel @@ -35,15 +35,15 @@ final class CommentMyMenuReactor: Reactor { case edit } // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init(nickName: String?) { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -55,7 +55,7 @@ final class CommentMyMenuReactor: Reactor { return Observable.just(.setEditType) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift index 59563034..93153e71 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift @@ -10,20 +10,20 @@ import UIKit import SnapKit final class CommentMyMenuView: UIView { - + // MARK: - Components let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) label.setLineHeightText(text: "내가 작성한 코멘트", font: .KorFont(style: .bold, size: 18)) return label }() - + let cancelButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let commentRemoveButton: UIButton = { let button = UIButton() button.setTitle("코멘트 삭제하기", for: .normal) @@ -32,13 +32,13 @@ final class CommentMyMenuView: UIView { button.contentHorizontalAlignment = .leading return button }() - + private let lineView: UIView = { let view = UIView() view.backgroundColor = .g50 return view }() - + let commentEditButton: UIButton = { let button = UIButton() button.setTitle("코멘트 수정하기", for: .normal) @@ -47,13 +47,13 @@ final class CommentMyMenuView: UIView { button.contentHorizontalAlignment = .leading return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -61,35 +61,35 @@ final class CommentMyMenuView: UIView { // MARK: - SetUp private extension CommentMyMenuView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(32) make.leading.equalToSuperview().inset(20) } - + self.addSubview(cancelButton) cancelButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(commentRemoveButton) commentRemoveButton.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(57) } - + self.addSubview(lineView) lineView.snp.makeConstraints { make in make.top.equalTo(commentRemoveButton.snp.bottom) make.height.equalTo(1) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(commentEditButton) commentEditButton.snp.makeConstraints { make in make.top.equalTo(lineView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift index 8c0ffa1d..190de3ae 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentUserInfoController: BaseViewController, View { - + typealias Reactor = CommentUserInfoReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentUserInfoView() } @@ -50,21 +50,21 @@ extension CommentUserInfoController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.normalCommentButton.rx.tap .map { _ in Reactor.Action.normalButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.instaCommentButton.rx.tap .map { _ in Reactor.Action.instaButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift index 9b12bdb6..76562c2b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift @@ -6,29 +6,29 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentUserInfoReactor: Reactor { - + // MARK: - Reactor enum Action { case cancelButtonTapped case normalButtonTapped case instaButtonTapped } - + enum Mutation { case moveToRecentScene case moveToCommentScene case moveToBlockScene } - + struct State { var selectedType: SelectedType = .none var nickName: String? } - + enum SelectedType { case none case cancel @@ -36,15 +36,15 @@ final class CommentUserInfoReactor: Reactor { case block } // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init(nickName: String?) { self.initialState = State(nickName: nickName) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -56,7 +56,7 @@ final class CommentUserInfoReactor: Reactor { return Observable.just(.moveToBlockScene) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift index 25356672..0ee09c62 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift @@ -10,20 +10,20 @@ import UIKit import SnapKit final class CommentUserInfoView: UIView { - + // MARK: - Components let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) label.setLineHeightText(text: "님에 대해 더 알아보기", font: .KorFont(style: .bold, size: 18)) return label }() - + let cancelButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let normalCommentButton: UIButton = { let button = UIButton() button.setTitle("코멘트를 작성한 팝업 모두보기", for: .normal) @@ -32,13 +32,13 @@ final class CommentUserInfoView: UIView { button.contentHorizontalAlignment = .leading return button }() - + private let lineView: UIView = { let view = UIView() view.backgroundColor = .g50 return view }() - + let instaCommentButton: UIButton = { let button = UIButton() button.setTitle("이 유저 차단하기", for: .normal) @@ -47,13 +47,13 @@ final class CommentUserInfoView: UIView { button.contentHorizontalAlignment = .leading return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -61,35 +61,35 @@ final class CommentUserInfoView: UIView { // MARK: - SetUp private extension CommentUserInfoView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(32) make.leading.equalToSuperview().inset(20) } - + self.addSubview(cancelButton) cancelButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(normalCommentButton) normalCommentButton.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(57) } - + self.addSubview(lineView) lineView.snp.makeConstraints { make in make.top.equalTo(normalCommentButton.snp.bottom) make.height.equalTo(1) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(instaCommentButton) instaCommentButton.snp.makeConstraints { make in make.top.equalTo(lineView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift index 14a2f784..a11d2f53 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class CommentListController: BaseViewController, View { - + typealias Reactor = CommentListReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentListView() private var sections: [any Sectionable] = [] private let scrollObserver: PublishSubject = .init() @@ -30,7 +30,7 @@ extension CommentListController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -58,18 +58,18 @@ private extension CommentListController { // MARK: - Methods extension CommentListController { func bind(reactor: Reactor) { - + scrollObserver .throttle(.seconds(1), scheduler: MainScheduler.instance) .map { Reactor.Action.scrollDidEndPoint } .bind(to: reactor.action) .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -77,7 +77,7 @@ extension CommentListController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -85,7 +85,7 @@ extension CommentListController { if state.isReloadView { owner.mainView.contentCollectionView.reloadData()} } .disposed(by: disposeBag) - + } } @@ -94,11 +94,11 @@ extension CommentListController: UICollectionViewDelegate, UICollectionViewDataS func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -113,7 +113,7 @@ extension CommentListController: UICollectionViewDelegate, UICollectionViewDataS } .bind(to: reactor.action) .disposed(by: cell.disposeBag) - + cell.profileView.button.rx.tap .withUnretained(self) .map { (owner, _) in @@ -121,7 +121,7 @@ extension CommentListController: UICollectionViewDelegate, UICollectionViewDataS } .bind(to: reactor.action) .disposed(by: cell.disposeBag) - + cell.totalViewButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -129,7 +129,7 @@ extension CommentListController: UICollectionViewDelegate, UICollectionViewDataS } .bind(to: reactor.action) .disposed(by: cell.disposeBag) - + cell.likeButton.rx.tap .map { Reactor.Action.likeButtonTapped(row: indexPath.row) } .bind(to: reactor.action) @@ -137,7 +137,7 @@ extension CommentListController: UICollectionViewDelegate, UICollectionViewDataS } return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index d62b4b1c..3823d183 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentListReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -24,7 +24,7 @@ final class CommentListReactor: Reactor { case profileButtonTapped(controller: BaseViewController, row: Int) case detailSceneLikeButtonTapped(row: Int) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController) @@ -33,26 +33,26 @@ final class CommentListReactor: Reactor { case presentImageScene(controller: BaseViewController, commentRow: Int, imageRow: Int) case presentCommentMenuScene(controller: BaseViewController, row: Int) } - + struct State { var sections: [any Sectionable] = [] var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let popUpID: Int64 private let popUpName: String? private var page: Int32 = 0 private var appendDataIsEmpty: Bool = false - + private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -66,10 +66,10 @@ final class CommentListReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var commentTitleSection = CommentListTitleSection(inputDataList: []) private var commentSection = DetailCommentSection(inputDataList: []) - + private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) private let spacing28Section = SpacingSection(inputDataList: [.init(spacing: 28)]) // MARK: - init @@ -78,7 +78,7 @@ final class CommentListReactor: Reactor { self.popUpID = popUpID self.popUpName = popUpName } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -173,7 +173,7 @@ final class CommentListReactor: Reactor { return Observable.just(.loadView) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -207,11 +207,11 @@ final class CommentListReactor: Reactor { } else { showOtherUserCommentMenu(controller: controller, comment: comment) } - + } return newState } - + func getSection() -> [any Sectionable] { return [ spacing24Section, @@ -220,7 +220,7 @@ final class CommentListReactor: Reactor { commentSection ] } - + func showOtherUserCommentMenu(controller: BaseViewController, comment: DetailCommentSection.CellType.Input) { let nextController = CommentUserInfoController() nextController.reactor = CommentUserInfoReactor(nickName: comment.nickName) @@ -250,7 +250,7 @@ final class CommentListReactor: Reactor { case .block: ToastMaker.createToast(message: "\(comment.nickName ?? "")을 차단했어요") self.userAPIUseCase.postUserBlock(blockedUserId: comment.creator) - .subscribe(onDisposed: { + .subscribe(onDisposed: { blockController.dismiss(animated: true) }) .disposed(by: self.disposeBag) @@ -268,13 +268,13 @@ final class CommentListReactor: Reactor { }) .disposed(by: disposeBag) } - + func showMyCommentMenu(controller: BaseViewController, comment: DetailCommentSection.CellType.Input) { let nextController = CommentMyMenuController() nextController.reactor = CommentMyMenuReactor(nickName: comment.nickName) imageService = PreSignedService() controller.presentPanModal(nextController) - + nextController.reactor?.state .withUnretained(nextController) .subscribe(onNext: { [weak self] (owner, state) in @@ -287,7 +287,7 @@ final class CommentListReactor: Reactor { ToastMaker.createToast(message: "작성한 코멘트를 삭제했어요") }) .disposed(by: self.disposeBag) - + let commentList = comment.imageList.compactMap { $0 } self.imageService.tryDelete(targetPaths: .init(objectKeyList: commentList)) .subscribe { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift index 18057b37..74440198 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift @@ -5,23 +5,22 @@ // Created by SeoJunYoung on 12/25/24. // - import UIKit import RxSwift struct CommentListTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = CommentListTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -38,7 +37,7 @@ struct CommentListTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift index 708c4b59..f8b33c01 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class CommentListTitleSectionCell: UICollectionViewCell { - + // MARK: - Components private let countLabel: PPLabel = { @@ -21,12 +21,12 @@ final class CommentListTitleSectionCell: UICollectionViewCell { }() let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -47,7 +47,7 @@ extension CommentListTitleSectionCell: Inputable { var count: Int var unit: String = "개" } - + func injection(with input: Input) { countLabel.setLineHeightText(text: "총 \(input.count)\(input.unit)", font: .KorFont(style: .regular, size: 13)) } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift index faa57203..66c5c957 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift @@ -10,24 +10,22 @@ import UIKit import SnapKit final class CommentListView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { - let view = PPReturnHeaderView() - return view + return PPReturnHeaderView() }() - + let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -35,14 +33,14 @@ final class CommentListView: UIView { // MARK: - SetUp private extension CommentListView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift index 603c2dcd..0756496e 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentSelectedController: BaseViewController, View { - + typealias Reactor = CommentSelectedReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentSelectedView() } @@ -50,14 +50,14 @@ extension CommentSelectedController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.normalCommentButton.rx.tap .map { _ in Reactor.Action.normalButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.instaCommentButton.rx.tap .map { _ in Reactor.Action.instaButtonTapped diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift index 9ca6bd7a..271a84b6 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift @@ -6,28 +6,28 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentSelectedReactor: Reactor { - + // MARK: - Reactor enum Action { case cancelButtonTapped case normalButtonTapped case instaButtonTapped } - + enum Mutation { case moveToRecentScene case moveToNormalScene case moveToInstaScene } - + struct State { var selectedType: SelectedType = .none } - + enum SelectedType { case none case cancel @@ -35,15 +35,15 @@ final class CommentSelectedReactor: Reactor { case insta } // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -55,7 +55,7 @@ final class CommentSelectedReactor: Reactor { return Observable.just(.moveToInstaScene) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift index 9ca4bc6e..7c41e5bd 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift @@ -10,20 +10,20 @@ import UIKit import SnapKit final class CommentSelectedView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) label.setLineHeightText(text: "코멘트 작성 방법 선택", font: .KorFont(style: .bold, size: 18)) return label }() - + let cancelButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let normalCommentButton: UIButton = { let button = UIButton() button.setTitle("일반 코멘트 작성하기", for: .normal) @@ -32,13 +32,13 @@ final class CommentSelectedView: UIView { button.contentHorizontalAlignment = .leading return button }() - + private let lineView: UIView = { let view = UIView() view.backgroundColor = .g50 return view }() - + let instaCommentButton: UIButton = { let button = UIButton() button.setTitle("인스타그램 연동 코멘트 작성하기", for: .normal) @@ -47,13 +47,13 @@ final class CommentSelectedView: UIView { button.contentHorizontalAlignment = .leading return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -61,34 +61,34 @@ final class CommentSelectedView: UIView { // MARK: - SetUp private extension CommentSelectedView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(34) make.leading.equalToSuperview().inset(20) } - + self.addSubview(cancelButton) cancelButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(normalCommentButton) normalCommentButton.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(40) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(lineView) lineView.snp.makeConstraints { make in make.top.equalTo(normalCommentButton.snp.bottom).offset(16) make.height.equalTo(2) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(instaCommentButton) instaCommentButton.snp.makeConstraints { make in make.top.equalTo(lineView.snp.bottom).offset(16) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift index a62c93ce..1c4871af 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CommentUserBlockController: BaseViewController, View { - + typealias Reactor = CommentUserBlockReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CommentUserBlockView() } @@ -48,12 +48,12 @@ extension CommentUserBlockController { .map { Reactor.Action.stopButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.blockButton.rx.tap .map { Reactor.Action.continueButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift index 7400fa39..72c9b9fa 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift @@ -6,42 +6,42 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CommentUserBlockReactor: Reactor { - + // MARK: - Reactor enum Action { case continueButtonTapped case stopButtonTapped } - + enum Mutation { case setSelectedType(type: SelectedType) } - + struct State { var selectedType: SelectedType = .none var nickName: String? } - + enum SelectedType { case none case cancel case block } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init(nickName: String?) { self.initialState = State(nickName: nickName) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -51,7 +51,7 @@ final class CommentUserBlockReactor: Reactor { return Observable.just(.setSelectedType(type: .cancel)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift index 1906ff04..3ca9705b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift @@ -10,43 +10,40 @@ import UIKit import SnapKit final class CommentUserBlockView: UIView { - + // MARK: - Components let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "님을 차단할까요?") - return label + return PPLabel(style: .bold, fontSize: 18, text: "님을 차단할까요?") }() - + private let descriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 14, text: "차단하시면 앞으로 이 유저가 남긴\n코멘트와 반응을 볼 수 없어요.") label.numberOfLines = 2 label.textColor = .g600 return label }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + let cancelButton: PPButton = { - let button = PPButton(style: .secondary, text: "취소") - return button + return PPButton(style: .secondary, text: "취소") }() - + let blockButton: PPButton = { - let button = PPButton(style: .primary, text: "차단하기") - return button + return PPButton(style: .primary, text: "차단하기") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -54,20 +51,20 @@ final class CommentUserBlockView: UIView { // MARK: - SetUp private extension CommentUserBlockView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(32) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + buttonStackView.addArrangedSubview(cancelButton) buttonStackView.addArrangedSubview(blockButton) self.addSubview(buttonStackView) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift index 7318d844..68c6327b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit import SwiftSoup final class InstaCommentAddController: BaseViewController, View { - + typealias Reactor = InstaCommentAddReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = InstaCommentAddView() private var sections: [any Sectionable] = [] } @@ -52,7 +52,7 @@ private extension InstaCommentAddController { // MARK: - Methods extension InstaCommentAddController { func bind(reactor: Reactor) { - + SceneDelegate.appDidBecomeActive .subscribe { _ in if let url = UIPasteboard.general.string { @@ -64,17 +64,17 @@ extension InstaCommentAddController { } } .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.instaButton.rx.tap .map { Reactor.Action.instaButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -90,18 +90,18 @@ extension InstaCommentAddController: UICollectionViewDelegate, UICollectionViewD func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) guard let reactor = reactor else { return cell } - + return cell } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift index 6546f5d2..e05674c3 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift @@ -8,31 +8,31 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class InstaCommentAddReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case instaButtonTapped } - + enum Mutation { case loadView case moveToInsta } - + struct State { var sections: [any Sectionable] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -46,7 +46,7 @@ final class InstaCommentAddReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private let guideSection = InstaGuideSection(inputDataList: [ .init( imageList: [ @@ -103,12 +103,12 @@ final class InstaCommentAddReactor: Reactor { ] ) ]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -118,7 +118,7 @@ final class InstaCommentAddReactor: Reactor { return Observable.just(.moveToInsta) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -126,21 +126,21 @@ final class InstaCommentAddReactor: Reactor { newState.sections = getSection() case .moveToInsta: openInstagram() - + } return newState } - + func getSection() -> [any Sectionable] { return [ guideSection ] } - + func openInstagram() { // Instagram 앱의 URL Scheme let instagramURL = URL(string: "instagram://app")! - + if UIApplication.shared.canOpenURL(instagramURL) { // Instagram 앱 열기 UIApplication.shared.open(instagramURL, options: [:], completionHandler: nil) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift index daf48dfe..9e7fc499 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift @@ -10,14 +10,14 @@ import UIKit import SnapKit final class InstaCommentAddView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .KorFont(style: .regular, size: 15)) return view }() - + let instaButton: UIButton = { let button = UIButton() button.setTitleColor(.white, for: .normal) @@ -26,7 +26,7 @@ final class InstaCommentAddView: UIView { let title = "Instagram 열기" let attributedTitle = NSMutableAttributedString(string: title) - + let englishFont = UIFont.EngFont(style: .medium, size: 15)! attributedTitle.addAttribute(.font, value: englishFont, range: (title as NSString).range(of: "Instagram")) @@ -37,25 +37,25 @@ final class InstaCommentAddView: UIView { return button }() - + private let instaImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_instagram") return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -63,26 +63,26 @@ final class InstaCommentAddView: UIView { // MARK: - SetUp private extension InstaCommentAddView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(instaButton) instaButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(52) } - + instaButton.addSubview(instaImageView) instaImageView.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.centerY.equalToSuperview() make.size.equalTo(22) } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift index 77095f97..cb88aa60 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct InstaGuideChildSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = InstaGuideChildSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift index 46596746..508c459f 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift @@ -7,15 +7,15 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class InstaGuideChildSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private let indexTrailgView: UIView = { let view = UIView() view.backgroundColor = .g900 @@ -23,7 +23,7 @@ final class InstaGuideChildSectionCell: UICollectionViewCell { view.layer.cornerRadius = 4 return view }() - + private let indexLabel: UILabel = { let label = UILabel() label.font = .EngFont(style: .medium, size: 16) @@ -31,27 +31,27 @@ final class InstaGuideChildSectionCell: UICollectionViewCell { label.textAlignment = .center return label }() - + private let titleLabel: UILabel = { let label = UILabel() label.numberOfLines = 2 return label }() - + private let imageView: UIImageView = { let view = UIImageView() view.layer.cornerRadius = 8 view.clipsToBounds = true return view }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -67,12 +67,12 @@ private extension InstaGuideChildSectionCell { make.width.equalTo(40) make.height.equalTo(33) } - + indexTrailgView.addSubview(indexLabel) indexLabel.snp.makeConstraints { make in make.center.equalToSuperview() } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) @@ -94,7 +94,7 @@ extension InstaGuideChildSectionCell: Inputable { var title: NSMutableAttributedString? var index: Int } - + func injection(with input: Input) { indexLabel.text = "#\(input.index + 1)" titleLabel.attributedText = input.title diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift index 98801b83..13bfca92 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct InstaGuideSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = InstaGuideSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -35,8 +35,7 @@ struct InstaGuideSection: Sectionable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - - return section + + return NSCollectionLayoutSection(group: group) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift index aed527cb..d3c3371e 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift @@ -7,24 +7,24 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class InstaGuideSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private var autoScrollTimer: Timer? - + private lazy var contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: compositionalLayout) view.isScrollEnabled = false view.backgroundColor = .g50 return view }() - + var pageControl: UIPageControl = { let controller = UIPageControl() controller.currentPage = 0 @@ -36,17 +36,17 @@ final class InstaGuideSectionCell: UICollectionViewCell { controller.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) return controller }() - + let stopButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_banner_stopButton_gray"), for: .normal) return button }() - + private var isAutoBannerPlay: Bool = false - + private var imageSection = InstaGuideChildSection(inputDataList: []) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -60,25 +60,25 @@ final class InstaGuideSectionCell: UICollectionViewCell { return getSection()[section].getSection(section: section, env: env) } }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUp() setUpConstraints() bind() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() stopAutoScroll() } - + deinit { stopAutoScroll() } @@ -89,7 +89,7 @@ private extension InstaGuideSectionCell { func setUp() { contentCollectionView.delegate = self contentCollectionView.dataSource = self - + contentCollectionView.register( InstaGuideChildSectionCell.self, forCellWithReuseIdentifier: InstaGuideChildSectionCell.identifiers @@ -101,21 +101,21 @@ private extension InstaGuideSectionCell { } .disposed(by: disposeBag) } - + func setUpConstraints() { contentView.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(504) } - + contentView.addSubview(pageControl) pageControl.snp.makeConstraints { make in make.top.equalTo(contentCollectionView.snp.bottom) make.centerX.equalToSuperview() make.bottom.equalToSuperview() } - + contentView.addSubview(stopButton) stopButton.snp.makeConstraints { make in make.size.equalTo(8) @@ -123,11 +123,11 @@ private extension InstaGuideSectionCell { make.leading.equalTo(pageControl.snp.trailing).offset(-36) } } - + func getSection() -> [any Sectionable] { return [imageSection] } - + func startAutoScroll(interval: TimeInterval = 3.0) { stopAutoScroll() // 기존 타이머를 중지 isAutoBannerPlay = true @@ -157,7 +157,7 @@ private extension InstaGuideSectionCell { contentCollectionView.scrollToItem(at: nextIndex, at: .centeredHorizontally, animated: true) pageControl.currentPage = nextIndex.item } - + func bind() { stopButton.rx.tap .withUnretained(self) @@ -177,7 +177,7 @@ extension InstaGuideSectionCell: Inputable { var imageList: [UIImage?] var title: [NSMutableAttributedString?] } - + func injection(with input: Input) { pageControl.numberOfPages = input.imageList.count let datas = zip(input.imageList, input.title).enumerated().map { $0 } @@ -189,19 +189,18 @@ extension InstaGuideSectionCell: Inputable { // MARK: - UICollectionViewDelegate, UICollectionViewDataSource extension InstaGuideSectionCell: UICollectionViewDelegate, UICollectionViewDataSource { - + func numberOfSections(in collectionView: UICollectionView) -> Int { return getSection().count } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return getSection()[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = getSection()[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return getSection()[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift index b14cf4de..c2cf2ca4 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift @@ -7,23 +7,23 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxKeyboard +import RxSwift +import SnapKit final class NormalCommentAddController: BaseViewController, View { - + typealias Reactor = NormalCommentAddReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var keyBoardDisposeBag = DisposeBag() - + private var mainView = NormalCommentAddView() - + private var sections: [any Sectionable] = [] } @@ -33,7 +33,7 @@ extension NormalCommentAddController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -79,12 +79,12 @@ private extension NormalCommentAddController { // MARK: - Methods extension NormalCommentAddController { func bind(reactor: Reactor) { - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -92,7 +92,7 @@ extension NormalCommentAddController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // RxKeyboard로 키보드 높이 감지 RxKeyboard.instance.visibleHeight .skip(1) @@ -103,7 +103,7 @@ extension NormalCommentAddController { UIView.animate(withDuration: 0.3) { self.mainView.transform = .identity } - + } else { UIView.animate(withDuration: 0.3) { self.mainView.transform = .init(translationX: 0, y: -100) @@ -111,7 +111,7 @@ extension NormalCommentAddController { } }) .disposed(by: keyBoardDisposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -120,7 +120,7 @@ extension NormalCommentAddController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -136,7 +136,7 @@ extension NormalCommentAddController { } owner.sections = state.sections if state.isReloadView { owner.mainView.contentCollectionView.reloadData() } - + } .disposed(by: disposeBag) } @@ -147,11 +147,11 @@ extension NormalCommentAddController: UICollectionViewDelegate, UICollectionView func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -164,7 +164,7 @@ extension NormalCommentAddController: UICollectionViewDelegate, UICollectionView .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? AddCommentSectionCell { cell.commentTextView.rx.didChange .withUnretained(cell) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index 04bb9027..63b51884 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -5,15 +5,15 @@ // Created by SeoJunYoung on 12/14/24. // -import UIKit import PhotosUI +import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class NormalCommentAddReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -24,7 +24,7 @@ final class NormalCommentAddReactor: Reactor { case inputComment(text: String?) case saveButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case showImagePicker(controller: BaseViewController) @@ -32,24 +32,24 @@ final class NormalCommentAddReactor: Reactor { case setComment(text: String?) case save(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var text: String? var isReloadView: Bool = true var isSaving: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private var popUpID: Int64 private var popUpName: String - + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) private let imageService = PreSignedService() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -79,7 +79,7 @@ final class NormalCommentAddReactor: Reactor { self.popUpID = popUpID self.popUpName = popUpName } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -102,7 +102,7 @@ final class NormalCommentAddReactor: Reactor { return Observable.just(.save(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -154,7 +154,7 @@ final class NormalCommentAddReactor: Reactor { let images = imageSection.inputDataList.compactMap { $0.image }.enumerated().map { $0 } let uuid = UUID().uuidString let pathList = images.map { "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg" } - + imageService.tryUpload(datas: images.map { .init(filePath: "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg", image: $0.element)}) .subscribe(onSuccess: { [weak self] _ in guard let self = self else { return } @@ -170,11 +170,11 @@ final class NormalCommentAddReactor: Reactor { }) .disposed(by: disposeBag) } - + } return newState } - + func getSection() -> [any Sectionable] { return [ spacing25Section, @@ -199,19 +199,19 @@ final class NormalCommentAddReactor: Reactor { extension NormalCommentAddReactor: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) - + // 이미지가 로드된 순서를 보장하기 위해 선택한 이미지 개수만큼의 nil 배열을 생성 var originImageList = [UIImage?](repeating: nil, count: results.count) let dispatchGroup = DispatchGroup() // 모든 이미지를 로드할 때까지 대기 - + // results에서 이미지를 비동기적으로 로드 for (index, result) in results.enumerated() { if result.itemProvider.canLoadObject(ofClass: UIImage.self) { dispatchGroup.enter() // 이미지 로드가 시작될 때 그룹에 등록 - + result.itemProvider.loadObject(ofClass: UIImage.self) { image, _ in defer { dispatchGroup.leave() } // 이미지 로드가 끝날 때 그룹에서 제거 - + if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { @@ -222,7 +222,7 @@ extension NormalCommentAddReactor: PHPickerViewControllerDelegate { Logger.log(message: "ItemProvider Can Not Load Object", category: .error) } } - + // 모든 이미지가 로드된 후에 한 번에 choiceImageList 업데이트 dispatchGroup.notify(queue: .main) { let filteredImages = originImageList.compactMap { $0 } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift index 3fb3c140..b7559602 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct AddCommentDescriptionSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = AddCommentDescriptionSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -36,7 +36,7 @@ struct AddCommentDescriptionSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift index e83fe204..910cf5f2 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift @@ -7,15 +7,15 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class AddCommentDescriptionSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private let descriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g600 @@ -23,12 +23,12 @@ final class AddCommentDescriptionSectionCell: UICollectionViewCell { return label }() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -48,7 +48,7 @@ extension AddCommentDescriptionSectionCell: Inputable { struct Input { var description: String? } - + func injection(with input: Input) { descriptionLabel.setLineHeightText(text: input.description, font: .KorFont(style: .regular, size: 13)) } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift index 09d0822d..f1df7177 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct AddCommentImageSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = AddCommentImageSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(80), diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift index d9b4d329..dc954aa0 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift @@ -7,20 +7,19 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class AddCommentImageSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let imageView: UIImageView = { - let view = UIImageView() - return view + return UIImageView() }() - + let deleteButton: UIButton = { let button = UIButton() button.backgroundColor = .w100 @@ -28,24 +27,24 @@ final class AddCommentImageSectionCell: UICollectionViewCell { button.clipsToBounds = true return button }() - + private let xmarkImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_xmark") return view }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -58,18 +57,18 @@ private extension AddCommentImageSectionCell { contentView.layer.cornerRadius = 4 contentView.clipsToBounds = true contentView.layer.borderColor = UIColor.blu400.cgColor - + contentView.addSubview(imageView) imageView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + contentView.addSubview(deleteButton) deleteButton.snp.makeConstraints { make in make.size.equalTo(16) make.top.trailing.equalToSuperview().inset(6) } - + deleteButton.addSubview(xmarkImageView) xmarkImageView.snp.makeConstraints { make in make.center.equalToSuperview() @@ -86,9 +85,9 @@ extension AddCommentImageSectionCell: Inputable { var imageURL: String? var imageID: Int64? } - + func injection(with input: Input) { - + if input.isFirstCell { imageView.image = UIImage(named: "icon_camera_blue") contentView.layer.borderWidth = 1 diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift index 051d1370..9ef3468c 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct AddCommentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = AddCommentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift index 8bc041f7..b264381b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift @@ -7,16 +7,16 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class AddCommentSectionCell: UICollectionViewCell { - + // MARK: - Components - + var disposeBag = DisposeBag() - + let commentTextView: UITextView = { let view = UITextView() view.textContainerInset = .zero @@ -24,42 +24,42 @@ final class AddCommentSectionCell: UICollectionViewCell { view.font = .KorFont(style: .medium, size: 14) return view }() - + let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.textColor = .g500 return label }() - + private let placeHolderLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "최소 10자 이상 입력해주세요") label.textColor = .g200 return label }() - + private let noticeLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 12, text: "최대 500자까지 입력해주세요") label.textColor = .re500 label.isHidden = true return label }() - + private var isActiveComment: Bool = false - + private var commentState: BehaviorRelay = .init(value: .empty) - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() bind() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -70,7 +70,7 @@ final class AddCommentSectionCell: UICollectionViewCell { // MARK: - SetUp private extension AddCommentSectionCell { func bind() { - + commentTextView.rx.didBeginEditing .withUnretained(self) .subscribe { (owner, _) in @@ -78,7 +78,7 @@ private extension AddCommentSectionCell { owner.commentState.accept(owner.checkValidation(text: owner.commentTextView.text)) } .disposed(by: disposeBag) - + commentTextView.rx.didEndEditing .withUnretained(self) .subscribe { (owner, _) in @@ -86,7 +86,7 @@ private extension AddCommentSectionCell { owner.commentState.accept(owner.checkValidation(text: owner.commentTextView.text)) } .disposed(by: disposeBag) - + commentTextView.rx.didChange .debounce(.milliseconds(5), scheduler: MainScheduler.instance) .withUnretained(self) @@ -94,7 +94,7 @@ private extension AddCommentSectionCell { owner.commentState.accept(owner.checkValidation(text: owner.commentTextView.text)) } .disposed(by: disposeBag) - + commentState .withUnretained(self) .subscribe { (owner, state) in @@ -109,7 +109,7 @@ private extension AddCommentSectionCell { } .disposed(by: disposeBag) } - + func setUpConstraints() { contentView.layer.cornerRadius = 4 contentView.clipsToBounds = true @@ -136,13 +136,13 @@ private extension AddCommentSectionCell { make.bottom.equalToSuperview().inset(16) } } - + func checkValidation(text: String?) -> CommentState { guard let text = text else { return .empty } if text.isEmpty { return isActiveComment ? .emptyActive : .empty } - + switch text.count { case 1...9: return isActiveComment ? .shortLengthActive : .shortLength @@ -158,7 +158,7 @@ extension AddCommentSectionCell: Inputable { struct Input { var text: String? } - + func injection(with input: Input) { commentTextView.text = input.text commentState.accept(checkValidation(text: input.text)) @@ -174,7 +174,7 @@ enum CommentState { case longLengthActive case normal case normalActive - + var borderColor: UIColor? { switch self { case .shortLength, .longLength, .longLengthActive: @@ -183,7 +183,7 @@ enum CommentState { return .g100 } } - + var countLabelColor: UIColor? { switch self { case .shortLength, .longLength, .longLengthActive: @@ -192,7 +192,7 @@ enum CommentState { return .g500 } } - + var textColor: UIColor? { switch self { case .shortLength, .longLength, .longLengthActive: @@ -201,7 +201,7 @@ enum CommentState { return .g1000 } } - + var description: String? { switch self { case .longLength, .longLengthActive: @@ -212,7 +212,7 @@ enum CommentState { return nil } } - + var isHiddenNoticeLabel: Bool { switch self { case .longLength, .longLengthActive, .shortLength: @@ -221,7 +221,7 @@ enum CommentState { return true } } - + var isHiddenPlaceHolder: Bool { switch self { case .empty, .emptyActive: diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift index 3a50e804..d8e99796 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct AddCommentTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = AddCommentTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -36,7 +36,7 @@ struct AddCommentTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift index 8c7ad96d..c71411bf 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift @@ -7,26 +7,25 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class AddCommentTitleSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16) - return label + return PPLabel(style: .bold, fontSize: 16) }() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -46,7 +45,7 @@ extension AddCommentTitleSectionCell: Inputable { struct Input { var title: String? } - + func injection(with input: Input) { titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift index c5030a02..1913249a 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift @@ -10,33 +10,32 @@ import UIKit import SnapKit final class NormalCommentAddView: UIView { - + // MARK: - Components - + let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 view.isScrollEnabled = false return view }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -44,19 +43,19 @@ final class NormalCommentAddView: UIView { // MARK: - SetUp private extension NormalCommentAddView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(52) } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift index 84732cc8..e451afc4 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift @@ -7,23 +7,23 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxKeyboard +import RxSwift +import SnapKit final class NormalCommentEditController: BaseViewController, View { - + typealias Reactor = NormalCommentEditReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var keyBoardDisposeBag = DisposeBag() - + private var mainView = NormalCommentEditView() - + private var sections: [any Sectionable] = [] } @@ -33,7 +33,7 @@ extension NormalCommentEditController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -79,12 +79,12 @@ private extension NormalCommentEditController { // MARK: - Methods extension NormalCommentEditController { func bind(reactor: Reactor) { - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -92,7 +92,7 @@ extension NormalCommentEditController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // RxKeyboard로 키보드 높이 감지 RxKeyboard.instance.visibleHeight .skip(1) @@ -103,7 +103,7 @@ extension NormalCommentEditController { UIView.animate(withDuration: 0.3) { self.mainView.transform = .identity } - + } else { UIView.animate(withDuration: 0.3) { self.mainView.transform = .init(translationX: 0, y: -100) @@ -111,7 +111,7 @@ extension NormalCommentEditController { } }) .disposed(by: keyBoardDisposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -120,7 +120,7 @@ extension NormalCommentEditController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -136,17 +136,17 @@ extension NormalCommentEditController { } owner.sections = state.sections if state.isReloadView { owner.mainView.contentCollectionView.reloadData() } - + } .disposed(by: disposeBag) - + reactor.state .take(1) .withUnretained(self) .subscribe { (owner, state) in owner.sections = state.sections if state.isReloadView { owner.mainView.contentCollectionView.reloadData() } - + } .disposed(by: disposeBag) } @@ -157,11 +157,11 @@ extension NormalCommentEditController: UICollectionViewDelegate, UICollectionVie func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -174,7 +174,7 @@ extension NormalCommentEditController: UICollectionViewDelegate, UICollectionVie .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? AddCommentSectionCell { cell.commentTextView.rx.didChange .withUnretained(cell) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 437a7046..dee4c3cc 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -5,15 +5,15 @@ // Created by SeoJunYoung on 2/1/25. // -import UIKit import PhotosUI +import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class NormalCommentEditReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -24,7 +24,7 @@ final class NormalCommentEditReactor: Reactor { case inputComment(text: String?) case saveButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case showImagePicker(controller: BaseViewController) @@ -32,25 +32,25 @@ final class NormalCommentEditReactor: Reactor { case setComment(text: String?) case save(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var text: String? var isReloadView: Bool = true var isSaving: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private var popUpID: Int64 private var popUpName: String private var originComment: DetailCommentSection.CellType.Input - + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) private let imageService = PreSignedService() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -74,7 +74,7 @@ final class NormalCommentEditReactor: Reactor { private let spacing5Section = SpacingSection(inputDataList: [.init(spacing: 5)]) private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private let spacing32Section = SpacingSection(inputDataList: [.init(spacing: 32)]) - + // MARK: - init init(popUpID: Int64, popUpName: String, comment: DetailCommentSection.CellType.Input) { self.initialState = State(text: comment.comment) @@ -86,7 +86,7 @@ final class NormalCommentEditReactor: Reactor { .init(image: nil, isFirstCell: false, isEditCase: true, imageURL: url, imageID: id) })) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -108,7 +108,7 @@ final class NormalCommentEditReactor: Reactor { return Observable.just(.save(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -147,26 +147,26 @@ final class NormalCommentEditReactor: Reactor { commentSection.inputDataList[0].text = text case .save(let controller): newState.isSaving = true - + let addImages = imageSection.inputDataList.compactMap { $0.image }.enumerated().map { $0 } let uuid = UUID().uuidString let pathList = addImages.map { "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg" } - + let keepImages = imageSection.inputDataList.compactMap { $0.imageURL } - + let originImages = zip(originComment.imageList, originComment.imageIDList) var deleteImages: [(String?, Int64)] = [] - + for (imageURL, imageID) in originImages { if !keepImages.contains(imageURL!) { deleteImages.append((imageURL, imageID)) } } - + var convertAddImages: [PutCommentImageDataRequestDTO] = addImages.map { .init(imageId: nil, imageUrl: pathList[$0.offset], actionType: "ADD")} var convertKeepImages: [PutCommentImageDataRequestDTO] = keepImages.map { .init(imageId: nil, imageUrl: $0, actionType: "KEEP")} var convertDeleteImages: [PutCommentImageDataRequestDTO] = deleteImages.map { .init(imageId: $0.1, imageUrl: $0.0, actionType: "DELETE")} - + if !addImages.isEmpty { imageService.tryUpload(datas: addImages.map { .init(filePath: pathList[$0.offset], image: $0.element)}) .subscribe { [weak self] _ in @@ -208,7 +208,7 @@ final class NormalCommentEditReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ spacing25Section, @@ -233,19 +233,19 @@ final class NormalCommentEditReactor: Reactor { extension NormalCommentEditReactor: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) - + // 이미지가 로드된 순서를 보장하기 위해 선택한 이미지 개수만큼의 nil 배열을 생성 var originImageList = [UIImage?](repeating: nil, count: results.count) let dispatchGroup = DispatchGroup() // 모든 이미지를 로드할 때까지 대기 - + // results에서 이미지를 비동기적으로 로드 for (index, result) in results.enumerated() { if result.itemProvider.canLoadObject(ofClass: UIImage.self) { dispatchGroup.enter() // 이미지 로드가 시작될 때 그룹에 등록 - + result.itemProvider.loadObject(ofClass: UIImage.self) { image, _ in defer { dispatchGroup.leave() } // 이미지 로드가 끝날 때 그룹에서 제거 - + if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { @@ -256,7 +256,7 @@ extension NormalCommentEditReactor: PHPickerViewControllerDelegate { Logger.log(message: "ItemProvider Can Not Load Object", category: .error) } } - + // 모든 이미지가 로드된 후에 한 번에 choiceImageList 업데이트 dispatchGroup.notify(queue: .main) { let filteredImages = originImageList.compactMap { $0 } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift index 544e47aa..b3d9d1b2 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift @@ -10,33 +10,32 @@ import UIKit import SnapKit final class NormalCommentEditView: UIView { - + // MARK: - Components - + let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "코멘트 수정하기", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 view.isScrollEnabled = false return view }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -44,19 +43,19 @@ final class NormalCommentEditView: UIView { // MARK: - SetUp private extension NormalCommentEditView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(52) } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift index 2665a516..cbe375f7 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift @@ -7,22 +7,22 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class OtherUserCommentController: BaseViewController, View { - + typealias Reactor = OtherUserCommentReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = OtherUserCommentView() - + private var sections: [any Sectionable] = [] - + private let cellTapped: PublishSubject = .init() } @@ -31,9 +31,9 @@ extension OtherUserCommentController { override func viewDidLoad() { super.viewDidLoad() setUp() - + } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -48,13 +48,13 @@ private extension OtherUserCommentController { mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) } - + if let layout = reactor?.compositionalLayout { mainView.contentCollectionView.collectionViewLayout = layout } mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self - + mainView.contentCollectionView.register( CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers @@ -62,7 +62,7 @@ private extension OtherUserCommentController { mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyCommentedPopUpGridSectionCell.self, forCellWithReuseIdentifier: MyCommentedPopUpGridSectionCell.identifiers @@ -77,7 +77,7 @@ extension OtherUserCommentController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -85,7 +85,7 @@ extension OtherUserCommentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -93,7 +93,7 @@ extension OtherUserCommentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -109,19 +109,18 @@ extension OtherUserCommentController: UICollectionViewDelegate, UICollectionView func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 3 { cellTapped.onNext(indexPath.row) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index af4281b6..bc2de562 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -8,38 +8,38 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class OtherUserCommentReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case backButtonTapped(controller: BaseViewController) case cellTapped(controller: BaseViewController, row: Int) } - + enum Mutation { case moveToRecentScene(controller: BaseViewController) case loadView case skip case moveToDetailScene(controller: BaseViewController, row: Int) } - + struct State { var sections: [any Sectionable] = [] var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private let commenterID: String? private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -56,13 +56,13 @@ final class OtherUserCommentReactor: Reactor { private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private var countTitleSection = CommentListTitleSection(inputDataList: []) private var popUpSection = MyCommentedPopUpGridSection(inputDataList: []) - + // MARK: - init init(commenterID: String?) { self.initialState = State() self.commenterID = commenterID } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -88,7 +88,7 @@ final class OtherUserCommentReactor: Reactor { return Observable.just(.moveToDetailScene(controller: controller, row: row)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -108,7 +108,7 @@ final class OtherUserCommentReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ spacing16Section, diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift index 68cacc55..42954f36 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct OtherUserCommentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = OtherUserCommentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute((UIScreen.main.bounds.width - 40 - 8) / 2), diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift index 2640bfa9..b086e771 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift @@ -7,56 +7,55 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class OtherUserCommentSectionCell: UICollectionViewCell { - + // MARK: - Components private let imageView: UIImageView = { - let view = UIImageView() - return view + return UIImageView() }() - + private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 11) label.textColor = .blu500 return label }() - + private let contentLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 11) label.lineBreakMode = . byTruncatingTail label.numberOfLines = 2 return label }() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) label.textColor = .g400 return label }() - + private let likeImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_like_clear") return view }() - + private let likeCountLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 11) label.textColor = .w100 return label }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -77,31 +76,30 @@ private extension OtherUserCommentSectionCell { make.top.leading.trailing.equalToSuperview() make.height.equalTo(163.5) } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(imageView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(12) } - + contentView.addSubview(contentLabel) contentLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(12) } - + contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(12) make.bottom.equalToSuperview().inset(16) } - imageView.addSubview(likeCountLabel) likeCountLabel.snp.makeConstraints { make in make.trailing.bottom.equalToSuperview().inset(12) } - + imageView.addSubview(likeImageView) likeImageView.snp.makeConstraints { make in make.size.equalTo(18) @@ -120,7 +118,7 @@ extension OtherUserCommentSectionCell: Inputable { var date: String? var popUpID: Int64 } - + func injection(with input: Input) { imageView.setPPImage(path: input.imagePath) titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 11)) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift index e6d99298..89715fd4 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift @@ -10,26 +10,26 @@ import UIKit import SnapKit final class OtherUserCommentView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "코멘트 작성 팝업", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,13 +37,13 @@ final class OtherUserCommentView: UIView { // MARK: - SetUp private extension OtherUserCommentView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift index 75cfd69c..d6f3535e 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift @@ -7,10 +7,10 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class DetailController: BaseViewController, View { @@ -52,7 +52,6 @@ extension DetailController { tabBarController?.tabBar.isHidden = false } - } // MARK: - SetUp @@ -266,7 +265,7 @@ extension DetailController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? DetailEmptyCommetSectionCell { cell.commentButton.rx.tap .withUnretained(self) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 61e5c3d8..74097657 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -7,13 +7,13 @@ import UIKit +import LinkPresentation import ReactorKit -import RxSwift import RxCocoa -import LinkPresentation +import RxSwift final class DetailReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -31,7 +31,7 @@ final class DetailReactor: Reactor { case backButtonTapped(controller: BaseViewController) case loginButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToCommentTypeSelectedScene(controller: BaseViewController) @@ -46,24 +46,24 @@ final class DetailReactor: Reactor { case moveToLoginScene(controller: BaseViewController) case moveToImageDetailScene(controller: BaseViewController, cellRow: Int, ImageRow: Int) } - + private var commentButtonIsEnable: Bool = false - + struct State { var sections: [any Sectionable] = [] var barkGroundImagePath: String? var commentButtonIsEnable: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let popUpID: Int64 private var popUpName: String? private var isLogin: Bool = false private var isFirstRequest: Bool = true - + private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) @@ -81,7 +81,7 @@ final class DetailReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var imageBannerSection = ImageBannerSection(inputDataList: []) private var titleSection = DetailTitleSection(inputDataList: []) private var contentSection = DetailContentSection(inputDataList: []) @@ -91,8 +91,7 @@ final class DetailReactor: Reactor { private var commentEmptySection = DetailEmptyCommetSection(inputDataList: [.init()]) private var similarTitleSecion = SearchTitleSection(inputDataList: [.init(title: "지금 보고있는 팝업과 비슷한 팝업")]) private var similarSection = DetailSimilarSection(inputDataList: []) - - + private var spacing70Section = SpacingSection(inputDataList: [.init(spacing: 70)]) private var spacing40Section = SpacingSection(inputDataList: [.init(spacing: 40)]) private var spacing36Section = SpacingSection(inputDataList: [.init(spacing: 36)]) @@ -106,7 +105,7 @@ final class DetailReactor: Reactor { self.popUpID = popUpID self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -140,7 +139,7 @@ final class DetailReactor: Reactor { return Observable.just(.moveToImageDetailScene(controller: controller, cellRow: cellRow, ImageRow: ImageRow)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -176,9 +175,6 @@ final class DetailReactor: Reactor { mapGuideController.modalTransitionStyle = .coverVertical controller.present(mapGuideController, animated: true) - - - case .moveToCommentTotalScene(let controller): if isLogin { let nextController = CommentListController() @@ -225,7 +221,7 @@ final class DetailReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { if similarSection.inputDataList.isEmpty { if commentSection.inputDataList.isEmpty { @@ -308,9 +304,9 @@ final class DetailReactor: Reactor { ] } } - + } - + func setContent() -> Observable { return popUpAPIUseCase.getPopUpDetail(commentType: "NORMAL", popUpStoredId: popUpID, isViewCount: isFirstRequest) .withUnretained(self) @@ -325,7 +321,7 @@ final class DetailReactor: Reactor { // titleSection owner.titleSection.inputDataList = [.init(title: response.name, isBookMark: response.bookmarkYn, isLogin: response.loginYn)] owner.popUpName = response.name - + // contentSection owner.contentSection.inputDataList = [.init(content: response.desc)] owner.infoSection.inputDataList = [.init( @@ -362,7 +358,7 @@ final class DetailReactor: Reactor { return .loadView } } - + func bookMark() -> Observable { if let isBookMark = titleSection.inputDataList.first?.isBookMark { titleSection.inputDataList[0].isBookMark.toggle() @@ -378,32 +374,32 @@ final class DetailReactor: Reactor { return Observable.just(.loadView) } } - + func showSharedBoard(controller: BaseViewController) { let storeName = titleSection.inputDataList.first?.title ?? "" let imagePath = KeyPath.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") - + // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedPath) else { Logger.log(message: "URL 생성 실패", category: .error) return } - + // 🔹 비동기적으로 이미지 다운로드 - URLSession.shared.dataTask(with: url) { data, response, error in + URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { Logger.log(message: "다운로드 실패", category: .error) return } - + guard let data = data, let image = UIImage(data: data) else { Logger.log(message: "이미지 변환 실패", category: .error) return } - + Logger.log(message: "이미지 다운로드 성공", category: .info) - + let sharedText = "[팝풀] \(storeName) 팝업 어때요?\n지금 바로 팝풀에서 확인해보세요!" // UI 업데이트는 메인 스레드에서 실행 DispatchQueue.main.async { @@ -414,10 +410,10 @@ final class DetailReactor: Reactor { ) controller.present(activityViewController, animated: true, completion: nil) } - + }.resume() } - + func commentLike(indexPath: IndexPath) -> Observable { let isLike = commentSection.inputDataList[indexPath.row].isLike let commentID = commentSection.inputDataList[indexPath.row].commentID @@ -432,7 +428,7 @@ final class DetailReactor: Reactor { .andThen(Observable.just(.loadView)) } } - + func showOtherUserCommentMenu(controller: BaseViewController, indexPath: IndexPath, comment: DetailCommentSection.CellType.Input) { let nextController = CommentUserInfoController() nextController.reactor = CommentUserInfoReactor(nickName: comment.nickName) @@ -462,7 +458,7 @@ final class DetailReactor: Reactor { case .block: ToastMaker.createToast(message: "\(comment.nickName ?? "")을 차단했어요") self.userAPIUseCase.postUserBlock(blockedUserId: comment.creator) - .subscribe(onDisposed: { + .subscribe(onDisposed: { blockController.dismiss(animated: true) }) .disposed(by: self.disposeBag) @@ -480,13 +476,13 @@ final class DetailReactor: Reactor { }) .disposed(by: disposeBag) } - + func showMyCommentMenu(controller: BaseViewController, indexPath: IndexPath, comment: DetailCommentSection.CellType.Input) { let nextController = CommentMyMenuController() nextController.reactor = CommentMyMenuReactor(nickName: comment.nickName) imageService = PreSignedService() controller.presentPanModal(nextController) - + nextController.reactor?.state .withUnretained(nextController) .subscribe(onNext: { [weak self] (owner, state) in @@ -499,7 +495,7 @@ final class DetailReactor: Reactor { ToastMaker.createToast(message: "작성한 코멘트를 삭제했어요") }) .disposed(by: self.disposeBag) - + let commentList = comment.imageList.compactMap { $0 } self.imageService.tryDelete(targetPaths: .init(objectKeyList: commentList)) .subscribe { @@ -527,7 +523,7 @@ final class DetailReactor: Reactor { class ItemDetailSource: NSObject { let name: String let image: UIImage - + init(name: String, image: UIImage) { self.name = name self.image = image @@ -535,14 +531,14 @@ class ItemDetailSource: NSObject { } extension ItemDetailSource: UIActivityItemSource { - + func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any { image } func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivity.ActivityType?) -> Any? { image } - + func activityViewControllerLinkMetadata(_ activityViewController: UIActivityViewController) -> LPLinkMetadata? { let metaData = LPLinkMetadata() metaData.title = name diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift index b94dfefb..514b9bf9 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailCommentImageCell: UICollectionViewCell { - + // MARK: - Components private let imageView: UIImageView = { let view = UIImageView() @@ -19,14 +19,14 @@ final class DetailCommentImageCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -46,7 +46,7 @@ extension DetailCommentImageCell: Inputable { struct Input { var imagePath: String? } - + func injection(with input: Input) { imageView.setPPImage(path: input.imagePath) } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift index fdbada94..44ec4a67 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class DetailCommentProfileView: UIStackView { - + // MARK: - Components let profileImageView: UIImageView = { let view = UIImageView() @@ -19,47 +19,46 @@ final class DetailCommentProfileView: UIStackView { view.contentMode = .scaleAspectFill return view }() - + let contentStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 3 return view }() - + let nickNameLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 13) - return label + return PPLabel(style: .bold, fontSize: 13) }() - + let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.textColor = .g400 return label }() - + let button: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_comment_button"), for: .normal) return button }() - + let spacingView: UIView = UIView() // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + } // MARK: - SetUp private extension DetailCommentProfileView { - + func setUpConstraints() { self.alignment = .center self.spacing = 12 diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift index ada32f6b..4f08bab2 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailCommentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailCommentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailCommentSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift index 6df1feb5..b7fceb6c 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailCommentSectionCell: UICollectionViewCell { - + // MARK: - Components private let contentStackView: UIStackView = { let view = UIStackView() @@ -20,12 +20,11 @@ final class DetailCommentSectionCell: UICollectionViewCell { view.spacing = 16 return view }() - + let profileView: DetailCommentProfileView = { - let view = DetailCommentProfileView() - return view + return DetailCommentProfileView() }() - + let imageCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.itemSize = .init(width: 80, height: 80) @@ -36,53 +35,51 @@ final class DetailCommentSectionCell: UICollectionViewCell { view.showsHorizontalScrollIndicator = false return view }() - + private let contentLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 13) label.numberOfLines = 3 return label }() - + let totalViewButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let buttonTitleLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 13, text: "코멘트 전체보기") label.textColor = .g600 return label }() - + private let buttonImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_right_black") return view }() - + let likeButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let likeButtonTitleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13, text: "도움돼요") label.textColor = .g400 return label }() - + private let likeButtonImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_like_gray") return view }() - + private let borderView: UIView = { let view = UIView() view.backgroundColor = .g100 return view }() - + private let blurBackGroundView: UIView = { let view = UIView() view.backgroundColor = .white @@ -95,9 +92,9 @@ final class DetailCommentSectionCell: UICollectionViewCell { view.isUserInteractionEnabled = false return view }() - + var disposeBag = DisposeBag() - + private let loginStackView: UIStackView = { let view = UIStackView() view.axis = .vertical @@ -105,13 +102,13 @@ final class DetailCommentSectionCell: UICollectionViewCell { view.spacing = 16 return view }() - + private let loginNoticelabel: UILabel = { let label = UILabel() label.numberOfLines = 2 return label }() - + let loginButton: UIButton = { let button = UIButton() button.setTitle("로그인하고 후기보기", for: .normal) @@ -121,19 +118,19 @@ final class DetailCommentSectionCell: UICollectionViewCell { button.backgroundColor = .blu500 return button }() - + private var imagePathList: [String?] = [] // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -143,12 +140,11 @@ final class DetailCommentSectionCell: UICollectionViewCell { // MARK: - SetUp private extension DetailCommentSectionCell { func setUpConstraints() { - + imageCollectionView.delegate = self imageCollectionView.dataSource = self imageCollectionView.register(DetailCommentImageCell.self, forCellWithReuseIdentifier: DetailCommentImageCell.identifiers) - - + totalViewButton.addSubview(buttonTitleLabel) buttonTitleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() @@ -163,7 +159,7 @@ private extension DetailCommentSectionCell { make.leading.equalTo(buttonTitleLabel.snp.trailing) make.centerY.equalToSuperview() } - + profileView.snp.makeConstraints { make in make.width.equalTo(contentView.bounds.width - 40).priority(.high) } @@ -178,32 +174,32 @@ private extension DetailCommentSectionCell { contentStackView.addArrangedSubview(imageCollectionView) contentStackView.addArrangedSubview(contentLabel) contentStackView.addArrangedSubview(totalViewButton) - + contentView.addSubview(contentStackView) contentStackView.snp.makeConstraints { make in make.top.equalToSuperview().inset(20) make.leading.trailing.equalToSuperview() } - + likeButton.addSubview(likeButtonTitleLabel) likeButtonTitleLabel.snp.makeConstraints { make in make.height.equalTo(20).priority(.high) make.top.bottom.trailing.equalToSuperview() } - + likeButton.addSubview(likeButtonImageView) likeButtonImageView.snp.makeConstraints { make in make.size.equalTo(20) make.leading.centerY.equalToSuperview() make.trailing.equalTo(likeButtonTitleLabel.snp.leading) } - + contentView.addSubview(likeButton) likeButton.snp.makeConstraints { make in make.top.equalTo(contentStackView.snp.bottom).offset(16) make.trailing.equalToSuperview().inset(20) } - + contentView.addSubview(borderView) borderView.snp.makeConstraints { make in make.top.equalTo(likeButton.snp.bottom).offset(16) @@ -211,7 +207,7 @@ private extension DetailCommentSectionCell { make.height.equalTo(1).priority(.high) make.bottom.equalToSuperview() } - + contentView.addSubview(blurBackGroundView) blurBackGroundView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -220,7 +216,7 @@ private extension DetailCommentSectionCell { blurView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + contentView.addSubview(loginStackView) loginStackView.snp.makeConstraints { make in make.centerY.equalToSuperview() @@ -281,7 +277,7 @@ extension DetailCommentSectionCell: Inputable { var isMyComment: Bool var isLastCell: Bool = false } - + func injection(with input: Input) { let comment = input.comment ?? "" @@ -308,7 +304,7 @@ extension DetailCommentSectionCell: Inputable { imageCollectionView.isHidden = false imagePathList = input.imageList } - + imageCollectionView.reloadData() if input.isLogin { blurBackGroundView.isHidden = true @@ -327,7 +323,7 @@ extension DetailCommentSectionCell: Inputable { let reviewRange = (fullText as NSString).range(of: "생생한 후기") let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.4 - + // 기본 스타일 (폰트, 색상 등) let normalAttributes: [NSAttributedString.Key: Any] = [ .font: UIFont.KorFont(style: .regular, size: 14)!, @@ -353,12 +349,11 @@ extension DetailCommentSectionCell: Inputable { attributedString.addAttributes(popupStoreAttributes, range: popupStoreRange) attributedString.addAttributes(reviewAttributes, range: reviewRange) - loginNoticelabel.attributedText = attributedString loginNoticelabel.textAlignment = .center loginNoticelabel.lineBreakStrategy = .hangulWordPriority loginNoticelabel.numberOfLines = 0 - + borderView.isHidden = input.isLastCell } } @@ -368,7 +363,7 @@ extension DetailCommentSectionCell: UICollectionViewDelegate, UICollectionViewDa func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return imagePathList.count } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift index d1a25558..d2979800 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailCommentTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailCommentTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailCommentTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift index a285a972..26024838 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift @@ -7,23 +7,22 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailCommentTitleSectionCell: UICollectionViewCell { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "이 팝업에 대한 코멘트") - return label + return PPLabel(style: .bold, fontSize: 16, text: "이 팝업에 대한 코멘트") }() - + private let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g600 return label }() - + let totalViewButton: UIButton = { let button = UIButton() let attributedTitle = NSAttributedString( @@ -38,16 +37,16 @@ final class DetailCommentTitleSectionCell: UICollectionViewCell { }() var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -61,13 +60,13 @@ private extension DetailCommentTitleSectionCell { titleLabel.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + contentView.addSubview(countLabel) countLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(5) make.leading.bottom.equalToSuperview() } - + contentView.addSubview(totalViewButton) totalViewButton.snp.makeConstraints { make in make.centerY.equalToSuperview() @@ -81,7 +80,7 @@ extension DetailCommentTitleSectionCell: Inputable { var commentCount: Int64 var buttonIsHidden: Bool = false } - + func injection(with input: Input) { countLabel.setLineHeightText(text: "총 \(input.commentCount)개", font: .KorFont(style: .regular, size: 13)) totalViewButton.isHidden = input.buttonIsHidden diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift index 24a4e9d4..8d9c926c 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailContentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailContentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailContentSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift index aa7c0f73..e319d2ab 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailContentSectionCell: UICollectionViewCell { - + // MARK: - Components private let contentStackView: UIStackView = { @@ -26,38 +26,37 @@ final class DetailContentSectionCell: UICollectionViewCell { label.numberOfLines = 3 return label }() - + let dropDownButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let buttonTitleLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 13, text: "더보기") label.textColor = .g600 return label }() - + let buttonImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_dropdown_bottom_gray") return view }() - + var isOpen: Bool = false - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -97,7 +96,7 @@ extension DetailContentSectionCell: Inputable { struct Input { var content: String? } - + func injection(with input: Input) { let text = input.content ?? "" contentLabel.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 13)) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift index 45ae3913..3cda98e4 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailEmptyCommetSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailEmptyCommetSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailEmptyCommetSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 20, leading: 20, bottom: 20, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift index a5a1e031..aa27bcbc 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift @@ -7,21 +7,21 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailEmptyCommetSectionCell: UICollectionViewCell { - + // MARK: - Components private let noticeLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 14, text: "아직 작성된 코멘트가 없어요\n가장 먼저 후기를 남겨주시겠어요?" , lineHeight: 1.5) + let label = PPLabel(style: .medium, fontSize: 14, text: "아직 작성된 코멘트가 없어요\n가장 먼저 후기를 남겨주시겠어요?", lineHeight: 1.5) label.textAlignment = .center label.numberOfLines = 2 label.textColor = .g400 return label }() - + let commentButton: UIButton = { let button = UIButton() let attributedTitle = NSAttributedString( @@ -34,19 +34,19 @@ final class DetailEmptyCommetSectionCell: UICollectionViewCell { button.setAttributedTitle(attributedTitle, for: .normal) return button }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -61,7 +61,7 @@ private extension DetailEmptyCommetSectionCell { make.top.equalToSuperview().inset(80) make.centerX.equalToSuperview() } - + contentView.addSubview(commentButton) commentButton.snp.makeConstraints { make in make.top.equalTo(noticeLabel.snp.bottom).offset(26) @@ -74,7 +74,7 @@ extension DetailEmptyCommetSectionCell: Inputable { struct Input { } - + func injection(with input: Input) { } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift index d21f060a..f869c617 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailInfoSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailInfoSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailInfoSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift index e509e3c4..e7029550 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift @@ -7,79 +7,73 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailInfoSectionCell: UICollectionViewCell { - + // MARK: - Components - + private let dateTitleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 13, text: "기간") - return label + return PPLabel(style: .bold, fontSize: 13, text: "기간") }() - + private let dateLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 14) - return label + return PPLabel(style: .regular, fontSize: 14) }() - + private let timeTitleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 13, text: "시간") - return label + return PPLabel(style: .bold, fontSize: 13, text: "시간") }() - + private let timeLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 14) - return label + return PPLabel(style: .regular, fontSize: 14) }() - + private let addressTitleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 13, text: "주소") - return label + return PPLabel(style: .bold, fontSize: 13, text: "주소") }() - + private let addressLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 14) label.numberOfLines = 2 return label }() - + let copyButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_copy_gray"), for: .normal) return button }() - + let mapButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let mapButtonTitle: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13, text: "찾아가는 길") label.textColor = .blu500 return label }() - + let mapButtonImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_arrow_right_blue") return view }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -94,36 +88,36 @@ private extension DetailInfoSectionCell { make.top.equalToSuperview() make.leading.equalToSuperview() } - + contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.centerY.equalTo(dateTitleLabel) make.leading.equalTo(dateTitleLabel.snp.trailing).offset(12) } - + contentView.addSubview(timeTitleLabel) timeTitleLabel.snp.makeConstraints { make in make.top.equalTo(dateTitleLabel.snp.bottom).offset(11) make.leading.equalToSuperview() } - + contentView.addSubview(timeLabel) timeLabel.snp.makeConstraints { make in make.centerY.equalTo(timeTitleLabel) make.leading.equalTo(timeTitleLabel.snp.trailing).offset(12) } - + contentView.addSubview(addressTitleLabel) addressTitleLabel.snp.makeConstraints { make in make.top.equalTo(timeTitleLabel.snp.bottom).offset(11) make.leading.equalToSuperview() } - + mapButton.addSubview(mapButtonTitle) mapButtonTitle.snp.makeConstraints { make in make.leading.centerY.equalToSuperview() } - + mapButton.addSubview(mapButtonImageView) mapButtonImageView.snp.makeConstraints { make in make.leading.equalTo(mapButtonTitle.snp.trailing) @@ -131,13 +125,13 @@ private extension DetailInfoSectionCell { make.trailing.equalToSuperview() make.centerY.equalToSuperview().offset(0.5) } - + contentView.addSubview(mapButton) mapButton.snp.makeConstraints { make in make.centerY.equalTo(addressTitleLabel) make.trailing.equalToSuperview() } - + contentView.addSubview(addressLabel) addressLabel.snp.makeConstraints { make in make.top.equalTo(addressTitleLabel).offset(-2) @@ -145,14 +139,13 @@ private extension DetailInfoSectionCell { make.width.lessThanOrEqualTo(188) make.bottom.equalToSuperview() } - + contentView.addSubview(copyButton) copyButton.snp.makeConstraints { make in make.size.equalTo(16) make.top.equalTo(addressLabel).offset(1) make.leading.equalTo(addressLabel.snp.trailing).offset(2) } - } } @@ -165,16 +158,16 @@ extension DetailInfoSectionCell: Inputable { var endTime: String? var address: String? } - + func injection(with input: Input) { let startDate = input.startDate ?? "?" let endDate = input.endDate ?? "?" let startTime = input.startTime ?? "?" let endTime = input.endTime ?? "?" - + dateLabel.setLineHeightText(text: startDate + " ~ " + endDate, font: .KorFont(style: .regular, size: 14)) timeLabel.setLineHeightText(text: startTime + " ~ " + endTime, font: .KorFont(style: .regular, size: 14)) addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 13)) } } - + diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift index 664f296e..c62203f8 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailSimilarSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailSimilarSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(163), @@ -39,7 +39,7 @@ struct DetailSimilarSection: Sectionable { section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) section.orthogonalScrollingBehavior = .continuous section.interGroupSpacing = 12 - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift index d746d9ae..e4b66e5f 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailSimilarSectionCell: UICollectionViewCell { - + // MARK: - Components private let imageView: UIImageView = { let view = UIImageView() @@ -19,28 +19,26 @@ final class DetailSimilarSectionCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) label.font = .EngFont(style: .regular, size: 11) label.textColor = .g400 return label }() - + private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 12) - return label + return PPLabel(style: .bold, fontSize: 12) }() - + let bookMarkButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let trailingView: UIView = UIView() - + var disposeBag = DisposeBag() - + // MARK: - init override init(frame: CGRect) { super.init(frame: frame) @@ -52,12 +50,11 @@ final class DetailSimilarSectionCell: UICollectionViewCell { addHolesToCell() } - + required init?(coder: NSCoder) { fatalError() } - - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -65,27 +62,27 @@ final class DetailSimilarSectionCell: UICollectionViewCell { private func addHolesToCell() { // 전체 영역 경로 let fullPath = UIBezierPath(roundedRect: contentView.bounds, cornerRadius: 4) - + // 왼쪽 아래와 오른쪽 아래 구멍을 뚫을 위치 설정 (이미지뷰의 frame 위치 고려) let leftHoleCenter = CGPoint(x: contentView.bounds.minX, y: 190) let rightHoleCenter = CGPoint(x: contentView.bounds.maxX, y: 190) - + // 구멍을 만드는 경로 생성 (반지름 6) let leftHolePath = UIBezierPath(arcCenter: leftHoleCenter, radius: 6, startAngle: -.pi / 2, endAngle: .pi / 2, clockwise: true) let rightHolePath = UIBezierPath(arcCenter: rightHoleCenter, radius: 6, startAngle: .pi / 2, endAngle: -.pi / 2, clockwise: true) - + // 구멍 경로를 전체 경로에서 빼기 fullPath.append(leftHolePath) fullPath.append(rightHolePath) fullPath.usesEvenOddFillRule = true - + // 기존에 구멍을 뚫을 경로를 추가하는 레이어 let holeLayer = CAShapeLayer() holeLayer.path = fullPath.cgPath holeLayer.fillRule = .evenOdd holeLayer.fillColor = UIColor.black.cgColor trailingView.layer.mask = holeLayer - + // 그림자 Layer let shadowLayer = CAShapeLayer() shadowLayer.path = fullPath.cgPath @@ -106,25 +103,25 @@ private extension DetailSimilarSectionCell { trailingView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + trailingView.addSubview(imageView) imageView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(190) } - + trailingView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.top.equalTo(imageView.snp.bottom).offset(10) make.leading.trailing.equalToSuperview().inset(12) } - + trailingView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(12) make.top.equalTo(dateLabel.snp.bottom).offset(5.5) } - + trailingView.addSubview(bookMarkButton) bookMarkButton.snp.makeConstraints { make in make.size.equalTo(20) @@ -141,7 +138,7 @@ extension DetailSimilarSectionCell: Inputable { var id: Int64 var isBookMark: Bool? } - + func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift index f99f45b0..8b2b7ee2 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct DetailTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct DetailTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift index b2a86cce..2f7f7767 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift @@ -7,41 +7,40 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class DetailTitleSectionCell: UICollectionViewCell { - + // MARK: - Components private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) label.numberOfLines = 2 return label }() - + let bookMarkButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let sharedButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_shared"), for: .normal) return button }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -56,14 +55,14 @@ private extension DetailTitleSectionCell { make.size.equalTo(28) make.top.trailing.equalToSuperview() } - + contentView.addSubview(bookMarkButton) bookMarkButton.snp.makeConstraints { make in make.size.equalTo(28) make.top.equalToSuperview() make.trailing.equalTo(sharedButton.snp.leading).offset(-4) } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.leading.equalToSuperview() @@ -79,7 +78,7 @@ extension DetailTitleSectionCell: Inputable { var isBookMark: Bool var isLogin: Bool } - + func injection(with input: Input) { let bookMarkImage = input.isBookMark ? UIImage(named: "icon_bookmark_blue") : UIImage(named: "icon_bookmark_gray") bookMarkButton.setImage(bookMarkImage, for: .normal) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift index a79b9333..0e286f01 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift @@ -10,19 +10,18 @@ import UIKit import SnapKit final class DetailView: UIView { - + // MARK: - Components let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.contentInsetAdjustmentBehavior = .never return view }() - + let commentPostButton: PPButton = { - let button = PPButton(style: .primary, text: "코멘트 작성하기", disabledText: "코멘트 작성 완료") - return button + return PPButton(style: .primary, text: "코멘트 작성하기", disabledText: "코멘트 작성 완료") }() - + private let buttonTopView: UIView = { var view = UIView() view.frame = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 32) @@ -39,13 +38,13 @@ final class DetailView: UIView { view.layer.addSublayer(gradientLayer) return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -53,21 +52,20 @@ final class DetailView: UIView { // MARK: - SetUp private extension DetailView { - + func setUpConstraints() { self.addSubview(commentPostButton) commentPostButton.snp.makeConstraints { make in make.bottom.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(52) } - self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(commentPostButton.snp.top) } - + self.addSubview(buttonTopView) buttonTopView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift index ce91a1f1..8fe382bf 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift @@ -7,24 +7,24 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class HomeListController: BaseViewController, View { - + typealias Reactor = HomeListReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = HomeListView() - + private var sections: [any Sectionable] = [] - + private let pageChange: PublishSubject = .init() - + private let cellTapped: PublishSubject = .init() } @@ -33,9 +33,9 @@ extension HomeListController { override func viewDidLoad() { super.viewDidLoad() setUp() - + } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -49,13 +49,13 @@ private extension HomeListController { mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) } - + if let layout = reactor?.compositionalLayout { mainView.contentCollectionView.collectionViewLayout = layout } mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self - + mainView.contentCollectionView.register( HomeCardSectionCell.self, forCellWithReuseIdentifier: HomeCardSectionCell.identifiers @@ -74,7 +74,7 @@ extension HomeListController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -82,13 +82,13 @@ extension HomeListController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + pageChange .throttle(.milliseconds(1000), scheduler: MainScheduler.asyncInstance) .map { Reactor.Action.changePage } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -96,7 +96,7 @@ extension HomeListController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -113,11 +113,11 @@ extension HomeListController: UICollectionViewDelegate, UICollectionViewDataSour func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -130,10 +130,10 @@ extension HomeListController: UICollectionViewDelegate, UICollectionViewDataSour .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height @@ -142,7 +142,7 @@ extension HomeListController: UICollectionViewDelegate, UICollectionViewDataSour pageChange.onNext(()) } } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 1 { cellTapped.onNext(indexPath.row)} } diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift index 56480168..23903e0b 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class HomeListReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -21,7 +21,7 @@ final class HomeListReactor: Reactor { case changePage case cellTapped(controller: BaseViewController, row: Int) } - + enum Mutation { case moveToRecentScene(controller: BaseViewController) case loadView @@ -30,28 +30,28 @@ final class HomeListReactor: Reactor { case appendData case moveToDetailScene(controller: BaseViewController, row: Int) } - + struct State { var popUpType: HomePopUpType var sections: [any Sectionable] = [] var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var popUpType: HomePopUpType - + private let homeAPIUseCase = HomeAPIUseCaseImpl() private let userDefaultService = UserDefaultService() private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + private var isLoading: Bool = false private var totalPage: Int32 = 0 private var currentPage: Int32 = 0 private var size: Int32 = 10 - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -68,13 +68,13 @@ final class HomeListReactor: Reactor { private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) private var cardSections = HomeCardGridSection(inputDataList: []) - + // MARK: - init init(popUpType: HomePopUpType) { self.initialState = State(popUpType: popUpType) self.popUpType = popUpType } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -122,7 +122,7 @@ final class HomeListReactor: Reactor { return Observable.just(.moveToDetailScene(controller: controller, row: row)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -149,11 +149,11 @@ final class HomeListReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { - return [spacing24Section,cardSections,spacing64Section] + return [spacing24Section, cardSections, spacing64Section] } - + func setSection(response: GetHomeInfoResponse) { let isLogin = response.loginYn switch popUpType { @@ -206,7 +206,7 @@ final class HomeListReactor: Reactor { totalPage = response.popularPopUpStoreTotalPages } } - + func appendSectionData(response: GetHomeInfoResponse) { let isLogin = response.loginYn switch popUpType { diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift b/Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift index d257675a..2a2a6071 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift @@ -11,7 +11,7 @@ enum HomePopUpType { case curation case new case popular - + var title: String { switch self { case .curation: @@ -22,7 +22,7 @@ enum HomePopUpType { return "인기 팝업 전체보기" } } - + var path: String { switch self { case .curation: diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift b/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift index 185069a7..92406df4 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct HomeCardGridSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = HomeCardSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute((UIScreen.main.bounds.width - 40 - 16) / 2), diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift b/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift index 7db0f713..e4769564 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift @@ -10,24 +10,22 @@ import UIKit import SnapKit final class HomeListView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { - let view = PPReturnHeaderView() - return view + return PPReturnHeaderView() }() - + let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -35,13 +33,13 @@ final class HomeListView: UIView { // MARK: - SetUp private extension HomeListView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift index 5a507a7d..f2a883d6 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift @@ -7,29 +7,28 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class HomeController: BaseViewController, View { - + typealias Reactor = HomeReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = HomeView() - + let homeHeaderView: HomeHeaderView = { - let view = HomeHeaderView() - return view + return HomeHeaderView() }() - + private let headerBackgroundView: UIView = UIView() let backGroundblurEffect = UIBlurEffect(style: .regular) lazy var backGroundblurView = UIVisualEffectView(effect: backGroundblurEffect) - + private var sections: [any Sectionable] = [] } @@ -39,7 +38,7 @@ extension HomeController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = false @@ -55,58 +54,57 @@ private extension HomeController { } mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self - + mainView.contentCollectionView.register( ImageBannerSectionCell.self, forCellWithReuseIdentifier: ImageBannerSectionCell.identifiers ) - + mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers ) - + mainView.contentCollectionView.register( HomeTitleSectionCell.self, forCellWithReuseIdentifier: HomeTitleSectionCell.identifiers ) - + mainView.contentCollectionView.register( HomeCardSectionCell.self, forCellWithReuseIdentifier: HomeCardSectionCell.identifiers ) - + mainView.contentCollectionView.register( HomePopularCardSectionCell.self, forCellWithReuseIdentifier: HomePopularCardSectionCell.identifiers ) - + view.addSubview(mainView) mainView.snp.makeConstraints { make in make.top.equalToSuperview() make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide) } - + view.addSubview(homeHeaderView) homeHeaderView.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide).inset(7) make.leading.trailing.equalToSuperview() } - + headerBackgroundView.addSubview(backGroundblurView) backGroundblurView.snp.makeConstraints { make in make.edges.equalToSuperview() } backGroundblurView.isUserInteractionEnabled = false backGroundblurView.isHidden = true - + view.addSubview(headerBackgroundView) headerBackgroundView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(homeHeaderView.snp.bottom).offset(7) } - view.bringSubviewToFront(homeHeaderView) } } @@ -118,7 +116,7 @@ extension HomeController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + homeHeaderView.searchBarButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -126,7 +124,7 @@ extension HomeController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -142,18 +140,18 @@ extension HomeController: UICollectionViewDelegate, UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) guard let reactor = reactor else { return cell } - + if let cell = cell as? ImageBannerSectionCell { cell.bannerTapped .withUnretained(self) @@ -162,7 +160,7 @@ extension HomeController: UICollectionViewDelegate, UICollectionViewDataSource { }) .bind(to: reactor.action) .disposed(by: cell.disposeBag) - + cell.imageSection.currentPage .distinctUntilChanged() .withUnretained(self) @@ -182,17 +180,17 @@ extension HomeController: UICollectionViewDelegate, UICollectionViewDataSource { .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? HomeCardSectionCell { cell.bookmarkButton.rx.tap .map { Reactor.Action.bookMarkButtonTapped(indexPath: indexPath)} .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { if scrollView.contentOffset.y <= (307 - headerBackgroundView.frame.maxY) { backGroundblurView.isHidden = true @@ -201,7 +199,7 @@ extension HomeController: UICollectionViewDelegate, UICollectionViewDataSource { backGroundblurView.isHidden = false } } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.collectionViewCellTapped(controller: self, indexPath: indexPath)) } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift index d7bdfb8a..4759ee2c 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class HomeReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -24,7 +24,7 @@ final class HomeReactor: Reactor { case bannerCellTapped(controller: BaseViewController, row: Int) case changeIndicatorColor(controller: BaseViewController, row: Int) } - + enum Mutation { case loadView case setHedaerState(isDarkMode: Bool) @@ -33,23 +33,23 @@ final class HomeReactor: Reactor { case moveToSearchScene(controller: BaseViewController) case skip } - + struct State { var sections: [any Sectionable] = [] var headerIsDarkMode: Bool = true var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State - + var disposeBag = DisposeBag() - + private let homeApiUseCase = HomeAPIUseCaseImpl() private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) private let userDefaultService = UserDefaultService() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -80,12 +80,12 @@ final class HomeReactor: Reactor { private var spaceGray40Section = SpacingSection(inputDataList: [.init(spacing: 40, backgroundColor: .g700)]) private var spaceGray28Section = SpacingSection(inputDataList: [.init(spacing: 28, backgroundColor: .g700)]) private var spaceGray24Section = SpacingSection(inputDataList: [.init(spacing: 24, backgroundColor: .g700)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -125,7 +125,7 @@ final class HomeReactor: Reactor { return Observable.just(.moveToDetailScene(controller: controller, indexPath: IndexPath(row: row, section: 0))) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -162,9 +162,9 @@ final class HomeReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { - + if isLoign { return [ loginImageBannerSection, @@ -193,7 +193,7 @@ final class HomeReactor: Reactor { } } - + func getNewSection() -> [any Sectionable] { if newSection.isEmpty { return [] @@ -206,17 +206,17 @@ final class HomeReactor: Reactor { ] } } - + func setBannerSection(response: GetHomeInfoResponse) { let imagePaths = response.bannerPopUpStoreList.map { $0.mainImageUrl } let idList = response.bannerPopUpStoreList.map { $0.id } loginImageBannerSection.inputDataList = imagePaths.isEmpty ? [] : [.init(imagePaths: imagePaths, idList: idList)] } - + func setCurationTitleSection(response: GetHomeInfoResponse) { curationTitleSection.inputDataList = [.init(blueText: response.nickname, topSubText: "님을 위한", bottomText: "맞춤 팝업 큐레이션")] } - + func setCurationSection(response: GetHomeInfoResponse) { let islogin = response.loginYn curationSection.inputDataList = response.customPopUpStoreList.map({ response in @@ -233,7 +233,7 @@ final class HomeReactor: Reactor { ) }) } - + func setPopularSection(response: GetHomeInfoResponse) { popularSection.inputDataList = response.popularPopUpStoreList.map({ response in return .init( @@ -246,7 +246,7 @@ final class HomeReactor: Reactor { ) }) } - + func setNewSection(response: GetHomeInfoResponse) { let islogin = response.loginYn newSection.inputDataList = response.newPopUpStoreList.map({ response in @@ -263,7 +263,7 @@ final class HomeReactor: Reactor { ) }) } - + func getDetailController(indexPath: IndexPath, currentController: BaseViewController) { if isLoign { switch indexPath.section { @@ -334,7 +334,7 @@ final class HomeReactor: Reactor { } } } - + func getPopUpData(indexPath: IndexPath) -> HomeCardSectionCell.Input { if isLoign { switch indexPath.section { diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift index 7d2e3c8e..23e570ee 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct HomeCardSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = HomeCardSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(158), diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 5bc534e3..8ca3caf6 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -7,35 +7,35 @@ import UIKit -import SnapKit -import RxSwift import Kingfisher +import RxSwift +import SnapKit final class HomeCardSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let imageView: UIImageView = { let view = UIImageView() view.contentMode = .scaleAspectFill return view }() - + private let categoryLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 11) label.textColor = .blu500 return label }() - + private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 14) label.numberOfLines = 2 label.lineBreakMode = .byTruncatingTail return label }() - + private let addressLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 11) label.numberOfLines = 1 @@ -43,19 +43,18 @@ final class HomeCardSectionCell: UICollectionViewCell { label.textColor = .g400 return label }() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 11) label.lineBreakMode = .byTruncatingTail label.textColor = .g400 return label }() - + let bookmarkButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let rankLabel: UILabel = { let label = UILabel() label.backgroundColor = .w10 @@ -65,19 +64,19 @@ final class HomeCardSectionCell: UICollectionViewCell { label.textColor = .w100 return label }() - + private let imageService = PreSignedService() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -89,7 +88,7 @@ private extension HomeCardSectionCell { func setUpConstraints() { contentView.layer.cornerRadius = 4 contentView.clipsToBounds = true - + contentView.addSubview(imageView) imageView.snp.makeConstraints { make in make.width.equalTo(contentView.bounds.width) @@ -99,42 +98,40 @@ private extension HomeCardSectionCell { } imageView.layer.cornerRadius = 4 imageView.clipsToBounds = true - + contentView.addSubview(categoryLabel) categoryLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.top.equalTo(imageView.snp.bottom).offset(12) make.height.equalTo(15) } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(categoryLabel.snp.bottom).offset(4) make.leading.trailing.equalToSuperview() } - - contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.height.equalTo(15).priority(.high) make.bottom.equalToSuperview() } - + contentView.addSubview(addressLabel) addressLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.bottom.equalTo(dateLabel.snp.top) make.height.equalTo(17).priority(.high) } - + contentView.addSubview(bookmarkButton) bookmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.top.trailing.equalToSuperview().inset(8) } - + imageView.addSubview(rankLabel) rankLabel.snp.makeConstraints { make in make.height.equalTo(24) @@ -158,7 +155,7 @@ extension HomeCardSectionCell: Inputable { var isPopular: Bool = false var row: Int? } - + func injection(with input: Input) { categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .KorFont(style: .bold, size: 11)) titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 14)) @@ -169,7 +166,7 @@ extension HomeCardSectionCell: Inputable { bookmarkButton.setImage(bookmarkImage, for: .normal) imageView.setPPImage(path: input.imagePath) bookmarkButton.isHidden = !input.isLogin - + rankLabel.isHidden = !input.isPopular let rank = input.row ?? 0 rankLabel.setLineHeightText(text: "\(rank + 1)위", font: .KorFont(style: .medium, size: 11), lineHeight: 1) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift index 1a3a4fee..ed9aadf7 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift @@ -10,37 +10,37 @@ import UIKit import SnapKit final class HomeHeaderView: UIView { - + // MARK: - Components - + let searchBarButton: UIButton = { let button = UIButton() button.layer.cornerRadius = 4 return button }() - + let blurEffect = UIBlurEffect(style: .regular) lazy var blurView = UIVisualEffectView(effect: blurEffect) - + private let searchIconImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_search_black") return view }() - + private let searchLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 14, text: "팝업스토어명을 입력해보세요") label.textColor = .g1000 label.isUserInteractionEnabled = false return label }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -48,7 +48,7 @@ final class HomeHeaderView: UIView { // MARK: - SetUp private extension HomeHeaderView { - + func setUpConstraints() { blurView.isUserInteractionEnabled = false blurView.layer.cornerRadius = 4 @@ -57,21 +57,21 @@ private extension HomeHeaderView { blurView.snp.makeConstraints { make in make.edges.equalTo(searchBarButton) } - + self.addSubview(searchBarButton) searchBarButton.snp.makeConstraints { make in make.height.equalTo(37) make.leading.trailing.equalToSuperview().inset(20) make.top.bottom.equalToSuperview() } - + searchBarButton.addSubview(searchIconImageView) searchIconImageView.snp.makeConstraints { make in make.size.equalTo(20) make.centerY.equalToSuperview() make.leading.equalToSuperview().inset(12) } - + searchBarButton.addSubview(searchLabel) searchLabel.snp.makeConstraints { make in make.centerY.equalTo(searchIconImageView) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift index e790e333..4196db68 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct HomePopularCardSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = HomePopularCardSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(232), @@ -33,13 +33,13 @@ struct HomePopularCardSection: Sectionable { heightDimension: .absolute(332) ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - + // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 16 section.orthogonalScrollingBehavior = .continuous - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index aa395113..f613e678 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit -import RxSwift import Kingfisher +import RxSwift +import SnapKit final class HomePopularCardSectionCell: UICollectionViewCell { - + // MARK: - Components private var backGroundImageView: UIImageView = { let view = UIImageView() view.contentMode = .scaleAspectFill return view }() - + private let blurView: UIView = { var view = UIView() view.frame = CGRect(x: 0, y: 0, width: 232, height: 332) @@ -36,43 +36,43 @@ final class HomePopularCardSectionCell: UICollectionViewCell { view.layer.addSublayer(gradientLayer) return view }() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 16) label.textColor = .w100 return label }() - + private let categoryLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 16) label.textColor = .g1000 label.backgroundColor = .w100 return label }() - + private let titleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 16) label.numberOfLines = 2 label.textColor = .w100 return label }() - + private let locationLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 16) label.textColor = .w100 return label }() - + let disposeBag = DisposeBag() - + private let imageService = PreSignedService() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -83,36 +83,36 @@ private extension HomePopularCardSectionCell { func setUpConstraints() { contentView.layer.cornerRadius = 4 contentView.clipsToBounds = true - + contentView.addSubview(backGroundImageView) backGroundImageView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + backGroundImageView.addSubview(blurView) blurView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.bottom.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(48) } - + contentView.addSubview(categoryLabel) categoryLabel.snp.makeConstraints { make in make.bottom.equalTo(titleLabel.snp.top).offset(-16) make.leading.equalToSuperview().inset(20) make.height.equalTo(24) } - + contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.bottom.equalTo(categoryLabel.snp.top).offset(-6) make.leading.trailing.equalToSuperview().inset(20) } - + contentView.addSubview(locationLabel) locationLabel.snp.makeConstraints { make in make.centerY.equalTo(categoryLabel) @@ -131,7 +131,7 @@ extension HomePopularCardSectionCell: Inputable { var id: Int64 var address: String? } - + func injection(with input: Input) { let date = "#\(input.endDate.toDate().toPPDateMonthString())까지 열리는" dateLabel.setLineHeightText(text: date, font: .KorFont(style: .regular, size: 16)) @@ -142,7 +142,7 @@ extension HomePopularCardSectionCell: Inputable { locationLabel.text = "#\(address)" } } - + categoryLabel.text = category titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 16)) backGroundImageView.setPPImage(path: input.imagePath) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift index 32dc908a..3be6b834 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct HomeTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = HomeTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -35,9 +35,8 @@ struct HomeTitleSection: Sectionable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) // section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 16) - - return section + + return NSCollectionLayoutSection(group: group) } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift index e74a2fcd..d25f06bf 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift @@ -7,47 +7,45 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class HomeTitleSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private var blueLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 16) label.textColor = .blu500 return label }() - + private let subLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16) - return label + return PPLabel(style: .bold, fontSize: 16) }() - + private let bottomLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16) - return label + return PPLabel(style: .bold, fontSize: 16) }() - + let detailButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_right_gray"), for: .normal) return button }() - + // MARK: - init override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -72,7 +70,7 @@ private extension HomeTitleSectionCell { make.leading.equalToSuperview().inset(20) make.bottom.equalToSuperview() } - + contentView.addSubview(detailButton) detailButton.snp.makeConstraints { make in make.size.equalTo(24) @@ -90,7 +88,7 @@ extension HomeTitleSectionCell: Inputable { var backgroundColor: UIColor? = .clear var textColor: UIColor? = .g1000 } - + func injection(with input: Input) { blueLabel.setLineHeightText(text: input.blueText, font: .KorFont(style: .bold, size: 16)) subLabel.setLineHeightText(text: input.topSubText, font: .KorFont(style: .bold, size: 16)) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift index cab79f80..3710e7e0 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift @@ -10,20 +10,20 @@ import UIKit import SnapKit final class HomeView: UIView { - + // MARK: - Components let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.contentInsetAdjustmentBehavior = .never return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -31,7 +31,7 @@ final class HomeView: UIView { // MARK: - SetUp private extension HomeView { - + func setUpConstraints() { self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift index 14ddb820..8d323718 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct ImageBannerChildSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = ImageBannerChildSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift index 43dcc0e9..173f05ed 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift @@ -7,28 +7,28 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class ImageBannerChildSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private let imageView: UIImageView = { let view = UIImageView() view.contentMode = .scaleAspectFill return view }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -49,7 +49,7 @@ extension ImageBannerChildSectionCell: Inputable { var imagePath: String? var id: Int64 } - + func injection(with input: Input) { imageView.setPPImage(path: input.imagePath) } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift index 8a03671c..e60a5a6d 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct ImageBannerSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = ImageBannerSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -35,8 +35,7 @@ struct ImageBannerSection: Sectionable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - - return section + + return NSCollectionLayoutSection(group: group) } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index 025ec4a9..5f65f77d 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -7,26 +7,25 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class ImageBannerSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private var autoScrollTimer: Timer? - + private lazy var contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: compositionalLayout) view.contentInsetAdjustmentBehavior = .never return view }() - + var pageControl: CustomPageControl = { - let controller = CustomPageControl() - return controller + return CustomPageControl() }() let stopButton: UIButton = { @@ -34,18 +33,18 @@ final class ImageBannerSectionCell: UICollectionViewCell { button.setImage(UIImage(named: "icon_banner_stopButton"), for: .normal) return button }() - + let playButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_banner_playButton"), for: .normal) return button }() - + private var isAutoBannerPlay: Bool = false private var isFirstResponseAutoScroll: Bool = true - + var imageSection = ImageBannerChildSection(inputDataList: []) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -59,30 +58,30 @@ final class ImageBannerSectionCell: UICollectionViewCell { return getSection()[section].getSection(section: section, env: env) } }() - + let bannerTapped: PublishSubject = .init() - + private var currentIndex: Int = 1 private var isHiddenPauseButton: Bool = true - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUp() setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() isFirstResponseAutoScroll = false } - + // 자동 스크롤 중지 함수 func stopAutoScroll() { stopButton.isHidden = true @@ -91,7 +90,7 @@ final class ImageBannerSectionCell: UICollectionViewCell { autoScrollTimer?.invalidate() autoScrollTimer = nil } - + func startAutoScroll(interval: TimeInterval = 3.0) { stopAutoScroll() // 기존 타이머를 중지 stopButton.isHidden = false @@ -112,13 +111,13 @@ private extension ImageBannerSectionCell { func setUp() { contentCollectionView.delegate = self contentCollectionView.dataSource = self - + contentCollectionView.register( ImageBannerChildSectionCell.self, forCellWithReuseIdentifier: ImageBannerChildSectionCell.identifiers ) } - + func setUpConstraints() { contentView.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in @@ -135,7 +134,7 @@ private extension ImageBannerSectionCell { make.centerY.equalTo(pageControl) make.leading.equalTo(pageControl.snp.trailing).offset(6) } - + contentView.addSubview(playButton) playButton.snp.makeConstraints { make in make.size.equalTo(6) @@ -143,11 +142,11 @@ private extension ImageBannerSectionCell { make.leading.equalTo(pageControl.snp.trailing).offset(6) } } - + func getSection() -> [any Sectionable] { return [imageSection] } - + private func findViewController() -> BaseViewController? { var nextResponder = self.next while nextResponder != nil { @@ -158,7 +157,7 @@ private extension ImageBannerSectionCell { } return nil } - + func bind() { stopButton.rx.tap .withUnretained(self) @@ -170,7 +169,7 @@ private extension ImageBannerSectionCell { } } .disposed(by: disposeBag) - + playButton.rx.tap .withUnretained(self) .subscribe { (owner, _) in @@ -181,7 +180,7 @@ private extension ImageBannerSectionCell { } } .disposed(by: disposeBag) - + imageSection.currentPage .distinctUntilChanged() .withUnretained(self) @@ -202,7 +201,7 @@ extension ImageBannerSectionCell: Inputable { var idList: [Int64] var isHiddenPauseButton: Bool = false } - + func injection(with input: Input) { if imageSection.isEmpty { pageControl.setNumberOfPages(input.imagePaths.count) @@ -219,22 +218,22 @@ extension ImageBannerSectionCell: Inputable { ) } } - + contentCollectionView.reloadData() isHiddenPauseButton = input.isHiddenPauseButton if isFirstResponseAutoScroll { startAutoScroll() isFirstResponseAutoScroll = false } - + if input.isHiddenPauseButton { stopAutoScroll() stopButton.isHidden = true playButton.isHidden = true } - + bind() - + if input.imagePaths.count == 1 { playButton.isHidden = true stopButton.isHidden = true @@ -245,26 +244,25 @@ extension ImageBannerSectionCell: Inputable { // MARK: - UICollectionViewDelegate, UICollectionViewDataSource extension ImageBannerSectionCell: UICollectionViewDelegate, UICollectionViewDataSource { - + func numberOfSections(in collectionView: UICollectionView) -> Int { return getSection().count } func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return getSection()[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = getSection()[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return getSection()[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { bannerTapped.onNext(indexPath.row) } - + func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { if currentIndex == 0 { contentCollectionView.scrollToItem( @@ -281,7 +279,7 @@ extension ImageBannerSectionCell: UICollectionViewDelegate, UICollectionViewData if !isHiddenPauseButton { startAutoScroll() } - + } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift index 6a2c2396..300f838a 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift @@ -13,7 +13,7 @@ class SectionBackGroundDecorationView: UICollectionReusableView { super.init(frame: frame) self.backgroundColor = .g700 } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -23,7 +23,7 @@ extension SectionBackGroundDecorationView: Inputable { struct Input { var backgroundColor: UIColor } - + func injection(with input: Input) { self.backgroundColor = input.backgroundColor } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift index 02c2172b..0c7ae8ee 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct SpacingSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = SpacingSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -35,8 +35,7 @@ struct SpacingSection: Sectionable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - - return section + + return NSCollectionLayoutSection(group: group) } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift index db08e964..5cb66eb4 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift @@ -7,24 +7,24 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class SpacingSectionCell: UICollectionViewCell { - + // MARK: - Components let disposeBag = DisposeBag() - + private let spaceView: UIView = UIView() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -42,7 +42,7 @@ extension SpacingSectionCell: Inputable { var spacing: Float var backgroundColor: UIColor? = .clear } - + func injection(with input: Input) { spaceView.snp.removeConstraints() spaceView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift index c2b104a5..49a2a231 100644 --- a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift @@ -7,20 +7,20 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class ImageDetailController: BaseViewController, View { - + typealias Reactor = ImageDetailReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = ImageDetailView() - + private let cancelButton: UIButton = { let view = UIButton() view.setImage(UIImage(named: "icon_xmark_white"), for: .normal) @@ -56,7 +56,7 @@ private extension ImageDetailController { // MARK: - Methods extension ImageDetailController { func bind(reactor: Reactor) { - + cancelButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -64,7 +64,7 @@ extension ImageDetailController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift index 826253be..f94bb7bc 100644 --- a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift @@ -6,34 +6,34 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class ImageDetailReactor: Reactor { - + // MARK: - Reactor enum Action { case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case moveToRecentScene(controller: BaseViewController) } - + struct State { var imagePath: String? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init(imagePath: String?) { self.initialState = State(imagePath: imagePath) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -41,7 +41,7 @@ final class ImageDetailReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToRecentScene(let controller): diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift index 512c88d1..6092969d 100644 --- a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class ImageDetailView: UIView { - + // MARK: - Components let imageView: UIImageView = { let view = UIImageView() @@ -18,13 +18,13 @@ final class ImageDetailView: UIView { view.contentMode = .scaleAspectFit return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -32,7 +32,7 @@ final class ImageDetailView: UIView { // MARK: - SetUp private extension ImageDetailView { - + func setUpConstraints() { self.addSubview(imageView) imageView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift index 28c0ff4b..fb04c749 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift @@ -10,26 +10,26 @@ 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 @@ -37,34 +37,33 @@ final class LastLoginView: UIView { } } } - + // MARK: - Properties - + private let bgView: UIView = { - let view = UIView() - return view + 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 @@ -77,29 +76,29 @@ final class LastLoginView: UIView { 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: @@ -107,7 +106,7 @@ extension LastLoginView { make.leading.trailing.equalToSuperview().inset(16) make.bottom.equalToSuperview().inset(11) } - + case .pointDown: notificationLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(16) @@ -115,20 +114,20 @@ extension LastLoginView { } } } - + /// 툴팁을 방향에 맞춰 그리고 섀도우를 더하는 메서드 private func drawToolTip() { switch direction { case .pointUp: drawUpPointingToolTip() addShadow() - + case .pointDown: drawDownPointingTip() addShadow() } } - + /// 위를 가리키는 툴팁을 만듭니다 private func drawUpPointingToolTip() { let textLength = notificationLabel.frame.width @@ -137,10 +136,10 @@ extension LastLoginView { 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, @@ -149,25 +148,25 @@ extension LastLoginView { ), 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, @@ -176,19 +175,19 @@ extension LastLoginView { ), 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 @@ -200,9 +199,9 @@ 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 { diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift index f0fd9f61..47912023 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class LoginController: BaseViewController, View { - + typealias Reactor = LoginReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = LoginView() } @@ -28,7 +28,7 @@ extension LoginController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { @@ -57,12 +57,12 @@ private extension LoginController { // MARK: - Methods extension LoginController { func bind(reactor: Reactor) { - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.guestButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -70,7 +70,7 @@ extension LoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.kakaoButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -78,7 +78,7 @@ extension LoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.inquiryButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -86,7 +86,7 @@ extension LoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.appleButton.rx.tap .withUnretained(self) .map { (owner, _) in diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift index 74ef7572..1d3425a0 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class LoginReactor: Reactor { - + // MARK: - Reactor enum Action { case kakaoButtonTapped(controller: BaseViewController) @@ -19,7 +19,7 @@ final class LoginReactor: Reactor { case viewWillAppear case inquiryButtonTapped(controller: BaseViewController) } - + enum Mutation { case moveToSignUpScene(controller: BaseViewController) case moveToHomeScene(controller: BaseViewController) @@ -27,28 +27,28 @@ final class LoginReactor: Reactor { case resetService case moveToInquiryScene(controller: BaseViewController) } - + struct State { } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private var authrizationCode: String? - + private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) private let keyChainService = KeyChainService() let userDefaultService = UserDefaultService() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -57,8 +57,8 @@ final class LoginReactor: Reactor { case .appleButtonTapped(let controller): return loginWithApple(controller: controller) case .guestButtonTapped(let controller): - let _ = keyChainService.deleteToken(type: .accessToken) - let _ = keyChainService.deleteToken(type: .refreshToken) + _ = keyChainService.deleteToken(type: .accessToken) + _ = keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene(controller: controller)) case .viewWillAppear: return Observable.just(.resetService) @@ -66,7 +66,7 @@ final class LoginReactor: Reactor { return Observable.just(.moveToInquiryScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToSignUpScene(let controller): @@ -88,7 +88,7 @@ final class LoginReactor: Reactor { } return state } - + func loginWithKakao(controller: BaseViewController) -> Observable { return kakaoLoginService.fetchUserCredential() .withUnretained(self) @@ -115,7 +115,7 @@ final class LoginReactor: Reactor { } } } - + func loginWithApple(controller: BaseViewController) -> Observable { return appleLoginService.fetchUserCredential() .withUnretained(self) diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift index ed3830da..8333b589 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class LoginView: UIView { - + // MARK: - Components let guestButton: UIButton = { let button = UIButton(type: .system) @@ -19,43 +19,41 @@ final class LoginView: UIView { 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 = { - let button = PPButton(style: .kakao, text: "카카오톡으로 로그인") - return button + return PPButton(style: .kakao, text: "카카오톡으로 로그인") }() - + private let kakaoImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_login_kakao") return view }() - + let appleButton: PPButton = { - let button = PPButton(style: .apple, text: "Apple로 로그인") - return button + 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) @@ -63,13 +61,13 @@ final class LoginView: UIView { 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") } @@ -77,14 +75,14 @@ final class LoginView: UIView { // 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) @@ -92,41 +90,41 @@ private extension LoginView { 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) diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift index b35da1bb..a377e56b 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SubLoginController: BaseViewController, View { - + typealias Reactor = SubLoginReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SubLoginView() } @@ -28,7 +28,7 @@ extension SubLoginController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if let lastLogin = reactor?.userDefaultService.fetch(key: "lastLogin") { @@ -62,7 +62,7 @@ extension SubLoginController { .map { Reactor.Action.viewWillAppear} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.xmarkButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -70,7 +70,7 @@ extension SubLoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.kakaoButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -78,7 +78,7 @@ extension SubLoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.inquiryButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -86,7 +86,7 @@ extension SubLoginController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.appleButton.rx.tap .withUnretained(self) .map { (owner, _) in diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift index c9254e87..24b6d961 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SubLoginReactor: Reactor { - + // MARK: - Reactor enum Action { case kakaoButtonTapped(controller: BaseViewController) @@ -19,7 +19,7 @@ final class SubLoginReactor: Reactor { case viewWillAppear case inquiryButtonTapped(controller: BaseViewController) } - + enum Mutation { case moveToSignUpScene(controller: BaseViewController) case dismissScene(controller: BaseViewController) @@ -27,28 +27,28 @@ final class SubLoginReactor: Reactor { case resetService case moveToInquiryScene(controller: BaseViewController) } - + struct State { } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private var authrizationCode: String? - + private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) private let keyChainService = KeyChainService() let userDefaultService = UserDefaultService() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -64,7 +64,7 @@ final class SubLoginReactor: Reactor { return Observable.just(.moveToInquiryScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToSignUpScene(let controller): @@ -85,7 +85,7 @@ final class SubLoginReactor: Reactor { } return state } - + func loginWithKakao(controller: BaseViewController) -> Observable { return kakaoLoginService.fetchUserCredential() .withUnretained(self) @@ -112,7 +112,7 @@ final class SubLoginReactor: Reactor { } } } - + func loginWithApple(controller: BaseViewController) -> Observable { return appleLoginService.fetchUserCredential() .withUnretained(self) diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift index bcbe83c5..00e54ac7 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class SubLoginView: UIView { - + // MARK: - Components let xmarkButton: UIButton = { let button = UIButton(type: .system) @@ -18,14 +18,14 @@ final class SubLoginView: UIView { 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) @@ -33,29 +33,27 @@ final class SubLoginView: UIView { label.textAlignment = .center return label }() - + let kakaoButton: PPButton = { - let button = PPButton(style: .kakao, text: "카카오톡으로 로그인") - return button + return PPButton(style: .kakao, text: "카카오톡으로 로그인") }() - + private let kakaoImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_login_kakao") return view }() - + let appleButton: PPButton = { - let button = PPButton(style: .apple, text: "Apple로 로그인") - return button + 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) @@ -63,13 +61,13 @@ final class SubLoginView: UIView { 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") } @@ -77,7 +75,7 @@ final class SubLoginView: UIView { // MARK: - SetUp private extension SubLoginView { - + func setUpConstraints() { self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in @@ -85,7 +83,7 @@ private extension SubLoginView { make.trailing.equalToSuperview().inset(20) make.size.equalTo(32) } - + self.addSubview(logoImageView) logoImageView.snp.makeConstraints { make in make.height.equalTo(90) @@ -93,41 +91,41 @@ private extension SubLoginView { 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) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift index 9e52be99..ac172875 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift @@ -7,20 +7,20 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class BlockUserManageController: BaseViewController, View { - + typealias Reactor = BlockUserManageReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = BlockUserManageView() - + private var sections: [any Sectionable] = [] } @@ -30,7 +30,7 @@ extension BlockUserManageController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -52,12 +52,12 @@ private extension BlockUserManageController { mainView.contentCollectionView.register( CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( BlockUserListSectionCell.self, forCellWithReuseIdentifier: BlockUserListSectionCell.identifiers ) - + view.addSubview(mainView) mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) @@ -75,12 +75,12 @@ extension BlockUserManageController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -98,11 +98,11 @@ extension BlockUserManageController: UICollectionViewDelegate, UICollectionViewD func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index f714226b..99c16ac2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -8,35 +8,35 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class BlockUserManageReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case blockButtonTapped(row: Int) case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var isEmptyList: Bool = true } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -50,16 +50,16 @@ final class BlockUserManageReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var countSection = CommentListTitleSection(inputDataList: []) private var listSection = BlockUserListSection(inputDataList: []) private var spcing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -87,7 +87,7 @@ final class BlockUserManageReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -99,7 +99,7 @@ final class BlockUserManageReactor: Reactor { newState.isEmptyList = listSection.isEmpty return newState } - + func getSection() -> [any Sectionable] { return [ spcing16Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift index 28e01604..43dd9f31 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct BlockUserListSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = BlockUserListSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift index f784f85b..e7380373 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift @@ -7,15 +7,15 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class BlockUserListSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let profileImageView: UIImageView = { let view = UIImageView() view.layer.cornerRadius = 18 @@ -23,29 +23,28 @@ final class BlockUserListSectionCell: UICollectionViewCell { view.contentMode = .scaleAspectFill return view }() - + private let nickNameLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let blockButton: UIButton = { let button = UIButton() button.layer.cornerRadius = 4 return button }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -60,18 +59,18 @@ private extension BlockUserListSectionCell { make.size.equalTo(36) make.leading.centerY.equalToSuperview() } - + contentView.addSubview(nickNameLabel) nickNameLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(profileImageView.snp.trailing).offset(12) } - + contentView.addSubview(blockButton) blockButton.snp.makeConstraints { make in make.width.equalTo(75) make.height.equalTo(32) - + make.centerY.trailing.equalToSuperview() } } @@ -84,7 +83,7 @@ extension BlockUserListSectionCell: Inputable { var userID: String? var isBlocked: Bool } - + func injection(with input: Input) { profileImageView.setPPImage(path: input.profileImagePath) nickNameLabel.setLineHeightText(text: input.nickName, font: .KorFont(style: .bold, size: 14)) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift index f217bc90..f3fb7ef3 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift @@ -10,19 +10,18 @@ import UIKit import SnapKit final class BlockUserManageView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "차단한 사용자 관리", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + let emptyLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "차단한 사용자가 없어요") label.textColor = .g400 @@ -33,7 +32,7 @@ final class BlockUserManageView: UIView { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -41,19 +40,19 @@ final class BlockUserManageView: UIView { // MARK: - SetUp private extension BlockUserManageView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) make.leading.trailing.bottom.equalToSuperview() } - + self.addSubview(emptyLabel) emptyLabel.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom).offset(137) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift index 413df27f..5b24ba1c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift @@ -7,28 +7,28 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageBookmarkController: BaseViewController, View { - + typealias Reactor = MyPageBookmarkReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyPageBookmarkView() - + private var sections: [any Sectionable] = [] - + private var cellTapped: PublishSubject = .init() - + private var currentPageIndex: Int = 0 - + private var maxPageIndex: Int = 0 - + private var viewType: String? } @@ -38,7 +38,7 @@ extension MyPageBookmarkController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -64,7 +64,7 @@ private extension MyPageBookmarkController { mainView.contentCollectionView.register( ListCountButtonSectionCell.self, forCellWithReuseIdentifier: ListCountButtonSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( PopUpCardSectionCell.self, forCellWithReuseIdentifier: PopUpCardSectionCell.identifiers @@ -84,7 +84,7 @@ extension MyPageBookmarkController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -92,7 +92,7 @@ extension MyPageBookmarkController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -100,7 +100,7 @@ extension MyPageBookmarkController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.emptyButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -108,7 +108,7 @@ extension MyPageBookmarkController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.countButtonView.dropdownButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -116,7 +116,7 @@ extension MyPageBookmarkController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.contentCollectionView.rx.gesture(.swipe(direction: .up)) .skip(1) .withUnretained(self) @@ -139,12 +139,12 @@ extension MyPageBookmarkController { } owner.currentPageIndex += 1 owner.mainView.contentCollectionView.scrollToItem(at: .init(row: owner.currentPageIndex, section: 1), at: .top, animated: true) - + } } } .disposed(by: disposeBag) - + mainView.contentCollectionView.rx.gesture(.swipe(direction: .down)) .skip(1) .withUnretained(self) @@ -157,7 +157,7 @@ extension MyPageBookmarkController { } } .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -168,11 +168,11 @@ extension MyPageBookmarkController { owner.mainView.emptyButton.isHidden = !state.isEmptyCase owner.mainView.countButtonView.buttonTitleLabel.setLineHeightText(text: state.buttonTitle, font: .KorFont(style: .regular, size: 13)) owner.mainView.countButtonView.countLabel.setLineHeightText(text: "총 \(state.count)개", font: .KorFont(style: .regular, size: 13)) - + if state.buttonTitle != owner.viewType { owner.mainView.contentCollectionView.scrollsToTop = true } - + if state.buttonTitle == "크게보기" { owner.mainView.contentCollectionView.isScrollEnabled = false if owner.viewType == "모아서보기" { @@ -182,7 +182,7 @@ extension MyPageBookmarkController { } else { owner.mainView.contentCollectionView.isScrollEnabled = true } - + owner.maxPageIndex = Int(state.count) owner.viewType = state.buttonTitle } @@ -195,11 +195,11 @@ extension MyPageBookmarkController: UICollectionViewDelegate, UICollectionViewDa func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -212,7 +212,7 @@ extension MyPageBookmarkController: UICollectionViewDelegate, UICollectionViewDa .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? DetailSimilarSectionCell { cell.bookMarkButton.rx.tap .map { Reactor.Action.bookMarkButtonTapped(row: indexPath.row) } @@ -221,7 +221,7 @@ extension MyPageBookmarkController: UICollectionViewDelegate, UICollectionViewDa } return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height @@ -230,7 +230,7 @@ extension MyPageBookmarkController: UICollectionViewDelegate, UICollectionViewDa reactor?.action.onNext(.changePage) } } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 1 { cellTapped.onNext(indexPath.row) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index a9ffdfee..077b4506 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyPageBookmarkReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -23,7 +23,7 @@ final class MyPageBookmarkReactor: Reactor { case emptyButtonTapped(controller: BaseViewController) case bookMarkButtonTapped(row: Int) } - + enum Mutation { case loadView case skip @@ -32,7 +32,7 @@ final class MyPageBookmarkReactor: Reactor { case presentModal(controller: BaseViewController) case moveToSuggestScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var isReloadView: Bool = false @@ -40,9 +40,9 @@ final class MyPageBookmarkReactor: Reactor { var count: Int32 = 0 var buttonTitle: String? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private var isLoading: Bool = false @@ -51,9 +51,9 @@ final class MyPageBookmarkReactor: Reactor { private var currentPage: Int32 = 0 private var size: Int32 = 10 private var viewType: String = "크게보기" - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -67,17 +67,17 @@ final class MyPageBookmarkReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var listSection = RecentPopUpSection(inputDataList: []) private var cardListSection = PopUpCardSection(inputDataList: []) private var spacing12Section = SpacingSection(inputDataList: [.init(spacing: 12)]) private var spacing150Section = SpacingSection(inputDataList: [.init(spacing: 150)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -165,7 +165,7 @@ final class MyPageBookmarkReactor: Reactor { } } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -204,7 +204,7 @@ final class MyPageBookmarkReactor: Reactor { newState.buttonTitle = viewType return newState } - + func getSection() -> [any Sectionable] { return [ spacing12Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift index e6cb99f7..8c5a7f1d 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift @@ -10,14 +10,14 @@ import UIKit import SnapKit final class MyPageBookmarkView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "찜한 팝업", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 @@ -25,39 +25,38 @@ final class MyPageBookmarkView: UIView { view.isPrefetchingEnabled = true return view }() - + let emptyLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "앗! 아직 찜해둔 팝업이 없어요") label.textColor = .g400 label.isHidden = true return label }() - + let countButtonView: CountButtonView = { - let view = CountButtonView() - return view + return CountButtonView() }() - + let emptyButton: UIButton = { let button = UIButton() let buttonTitle = NSAttributedString( string: "추천 팝업 보러가기", attributes: [ - .font : UIFont.KorFont(style: .regular, size: 13)!, - .underlineStyle : NSUnderlineStyle.single.rawValue, - .foregroundColor : UIColor.g1000 + .font: UIFont.KorFont(style: .regular, size: 13)!, + .underlineStyle: NSUnderlineStyle.single.rawValue, + .foregroundColor: UIColor.g1000 ] ) button.setAttributedTitle(buttonTitle, for: .normal) return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -65,7 +64,7 @@ final class MyPageBookmarkView: UIView { // MARK: - SetUp private extension MyPageBookmarkView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in @@ -82,13 +81,13 @@ private extension MyPageBookmarkView { make.top.equalTo(countButtonView.snp.bottom).offset(16) make.leading.trailing.bottom.equalToSuperview() } - + self.addSubview(emptyLabel) emptyLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview().inset(245) } - + self.addSubview(emptyButton) emptyButton.snp.makeConstraints { make in make.top.equalTo(emptyLabel.snp.bottom).offset(26) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift index b36c7257..7d2168e8 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift @@ -10,35 +10,33 @@ import UIKit import SnapKit final class CountButtonView: UIView { - + // MARK: - Components let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g400 return label }() - + private let dropDownImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_dropdown") return view }() - + let buttonTitleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let dropdownButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -46,27 +44,27 @@ final class CountButtonView: UIView { // MARK: - SetUp private extension CountButtonView { - + func setUpConstraints() { self.addSubview(countLabel) countLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() } - + dropdownButton.addSubview(dropDownImageView) dropDownImageView.snp.makeConstraints { make in make.size.equalTo(22) make.top.trailing.bottom.equalToSuperview() } - + dropdownButton.addSubview(buttonTitleLabel) buttonTitleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.trailing.equalTo(dropDownImageView.snp.leading).offset(-6) make.centerY.equalToSuperview() } - + self.addSubview(dropdownButton) dropdownButton.snp.makeConstraints { make in make.trailing.equalToSuperview() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift index d71ea811..bb6347c8 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct PopUpCardSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = PopUpCardSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift index 8812d5b0..a957011f 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift @@ -7,12 +7,12 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class PopUpCardSectionCell: UICollectionViewCell { - + // MARK: - Components let imageView: UIImageView = { let view = UIImageView() @@ -20,35 +20,34 @@ final class PopUpCardSectionCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) label.font = .EngFont(style: .regular, size: 11) label.textColor = .g1000 return label }() - + private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 12) label.numberOfLines = 2 return label }() - + private let addressLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 12) label.textColor = .g400 return label }() - + let bookMarkButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let trailingView: UIView = UIView() - + var disposeBag = DisposeBag() - + // MARK: - init override init(frame: CGRect) { super.init(frame: frame) @@ -59,40 +58,40 @@ final class PopUpCardSectionCell: UICollectionViewCell { addHolesToCell() setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() } - + private func addHolesToCell() { // 전체 영역 경로 let fullPath = UIBezierPath(roundedRect: contentView.bounds, cornerRadius: 4) - + // 왼쪽 아래와 오른쪽 아래 구멍을 뚫을 위치 설정 (이미지뷰의 frame 위치 고려) let leftHoleCenter = CGPoint(x: contentView.bounds.minX, y: 423) let rightHoleCenter = CGPoint(x: contentView.bounds.maxX, y: 423) - + // 구멍을 만드는 경로 생성 (반지름 6) let leftHolePath = UIBezierPath(arcCenter: leftHoleCenter, radius: 12, startAngle: -.pi / 2, endAngle: .pi / 2, clockwise: true) let rightHolePath = UIBezierPath(arcCenter: rightHoleCenter, radius: 12, startAngle: .pi / 2, endAngle: -.pi / 2, clockwise: true) - + // 구멍 경로를 전체 경로에서 빼기 fullPath.append(leftHolePath) fullPath.append(rightHolePath) fullPath.usesEvenOddFillRule = true - + // 기존에 구멍을 뚫을 경로를 추가하는 레이어 let holeLayer = CAShapeLayer() holeLayer.path = fullPath.cgPath holeLayer.fillRule = .evenOdd holeLayer.fillColor = UIColor.black.cgColor trailingView.layer.mask = holeLayer - + // 그림자 Layer let shadowLayer = CAShapeLayer() shadowLayer.path = fullPath.cgPath @@ -109,7 +108,7 @@ final class PopUpCardSectionCell: UICollectionViewCell { // MARK: - SetUp private extension PopUpCardSectionCell { func setUpConstraints() { - + contentView.addSubview(trailingView) trailingView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -119,26 +118,26 @@ private extension PopUpCardSectionCell { make.top.leading.trailing.equalToSuperview() make.height.equalTo(423) } - + trailingView.addSubview(bookMarkButton) bookMarkButton.snp.makeConstraints { make in make.size.equalTo(36) make.top.trailing.equalToSuperview().inset(20) } - + trailingView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.top.equalTo(imageView.snp.bottom).offset(24) make.centerX.equalToSuperview() } - + trailingView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(dateLabel.snp.bottom).offset(20) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(44) } - + trailingView.addSubview(addressLabel) addressLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) @@ -156,7 +155,7 @@ extension PopUpCardSectionCell: Inputable { var address: String? var isBookMark: Bool } - + func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) @@ -165,7 +164,7 @@ extension PopUpCardSectionCell: Inputable { titleLabel.textAlignment = .center addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 14)) addressLabel.textAlignment = .center - + if input.isBookMark { bookMarkButton.setImage(UIImage(named: "icon_bookmark_fill"), for: .normal) } else { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift index 7b4378b6..41ffc78c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift @@ -7,12 +7,12 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class PopUpCardView: UIView { - + // MARK: - Components let imageView: UIImageView = { let view = UIImageView() @@ -20,36 +20,34 @@ final class PopUpCardView: UIView { view.clipsToBounds = true return view }() - + let contentView: UIView = UIView() - + private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) label.font = .EngFont(style: .regular, size: 11) label.textColor = .g1000 return label }() - + private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 12) - return label + return PPLabel(style: .bold, fontSize: 12) }() - + private let addressLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 12) label.textColor = .g400 return label }() - + let bookMarkButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let trailingView: UIView = UIView() - + var disposeBag = DisposeBag() - + // MARK: - init init() { super.init(frame: .zero) @@ -59,40 +57,40 @@ final class PopUpCardView: UIView { trailingView.layer.cornerRadius = 4 setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func layoutSubviews() { super.layoutSubviews() addHolesToCell() } - + private func addHolesToCell() { // 전체 영역 경로 let fullPath = UIBezierPath(roundedRect: bounds, cornerRadius: 4) - + // 왼쪽 아래와 오른쪽 아래 구멍을 뚫을 위치 설정 (이미지뷰의 frame 위치 고려) let leftHoleCenter = CGPoint(x: bounds.minX, y: 423) let rightHoleCenter = CGPoint(x: bounds.maxX, y: 423) - + // 구멍을 만드는 경로 생성 (반지름 6) let leftHolePath = UIBezierPath(arcCenter: leftHoleCenter, radius: 12, startAngle: -.pi / 2, endAngle: .pi / 2, clockwise: true) let rightHolePath = UIBezierPath(arcCenter: rightHoleCenter, radius: 12, startAngle: .pi / 2, endAngle: -.pi / 2, clockwise: true) - + // 구멍 경로를 전체 경로에서 빼기 fullPath.append(leftHolePath) fullPath.append(rightHolePath) fullPath.usesEvenOddFillRule = true - + // 기존에 구멍을 뚫을 경로를 추가하는 레이어 let holeLayer = CAShapeLayer() holeLayer.path = fullPath.cgPath holeLayer.fillRule = .evenOdd holeLayer.fillColor = UIColor.black.cgColor trailingView.layer.mask = holeLayer - + // 그림자 Layer let shadowLayer = CAShapeLayer() shadowLayer.path = fullPath.cgPath @@ -109,7 +107,7 @@ final class PopUpCardView: UIView { // MARK: - SetUp private extension PopUpCardView { func setUpConstraints() { - + self.addSubview(trailingView) trailingView.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -119,25 +117,25 @@ private extension PopUpCardView { make.top.leading.trailing.equalToSuperview() make.height.equalTo(423) } - + trailingView.addSubview(bookMarkButton) bookMarkButton.snp.makeConstraints { make in make.size.equalTo(36) make.top.trailing.equalToSuperview().inset(20) } - + trailingView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.top.equalTo(imageView.snp.bottom).offset(24) make.centerX.equalToSuperview() } - + trailingView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(dateLabel.snp.bottom).offset(20) } - + trailingView.addSubview(addressLabel) addressLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(16) @@ -156,14 +154,14 @@ extension PopUpCardView: Inputable { var address: String? var isBookMark: Bool } - + func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) dateLabel.setLineHeightText(text: date, font: .EngFont(style: .regular, size: 13)) titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 14)) - + if input.isBookMark { bookMarkButton.setImage(UIImage(named: "icon_bookmark_fill"), for: .normal) } else { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift index 9616ae96..67a61a01 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class BookMarkPopUpViewTypeModalController: BaseViewController, View { - + typealias Reactor = BookMarkPopUpViewTypeModalReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = BookMarkPopUpViewTypeModalView() } @@ -48,13 +48,13 @@ extension BookMarkPopUpViewTypeModalController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.sortedSegmentControl.rx.selectedSegmentIndex .skip(1) .map { Reactor.Action.selectedSegmentControl(row: $0) } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .compactMap { (owner, _) in @@ -62,7 +62,7 @@ extension BookMarkPopUpViewTypeModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.xmarkButton.rx.tap .withUnretained(self) .compactMap { (owner, _) in @@ -70,7 +70,7 @@ extension BookMarkPopUpViewTypeModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -92,7 +92,7 @@ extension BookMarkPopUpViewTypeModalController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } - + var longFormHeight: PanModalHeight { return .contentHeight(250) } @@ -106,4 +106,3 @@ extension BookMarkPopUpViewTypeModalController: PanModalPresentable { return 20 } } - diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift index 8bd33888..8d879bdd 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class BookMarkPopUpViewTypeModalReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -18,13 +18,13 @@ final class BookMarkPopUpViewTypeModalReactor: Reactor { case saveButtonTapped(controller: BaseViewController) case xmarkButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case setSelectedIndex(row: Int) case dismissScene(controller: BaseViewController, isSave: Bool) } - + struct State { var isSetView: Bool = false var originSortedCode: String @@ -32,18 +32,17 @@ final class BookMarkPopUpViewTypeModalReactor: Reactor { var saveButtonIsEnabled: Bool = false var isSave: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - - + // MARK: - init init(sortedCode: String) { self.initialState = State(originSortedCode: sortedCode) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -57,7 +56,7 @@ final class BookMarkPopUpViewTypeModalReactor: Reactor { return Observable.just(.dismissScene(controller: controller, isSave: false)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isSetView = false diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift index 8604d089..f57b0988 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift @@ -10,35 +10,32 @@ import UIKit import SnapKit final class BookMarkPopUpViewTypeModalView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") }() - + let xmarkButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let sortedSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .base, segments: ["크게보기","모아서보기"]) - return control + return PPSegmentedControl(type: .base, segments: ["크게보기", "모아서보기"]) }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -46,33 +43,33 @@ final class BookMarkPopUpViewTypeModalView: UIView { // MARK: - SetUp private extension BookMarkPopUpViewTypeModalView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) } - + self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(sortedSegmentControl) sortedSegmentControl.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(48) } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(50) } - + } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift index dbdb455d..3057e6d8 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class FAQController: BaseViewController, View { - + typealias Reactor = FAQReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = FAQView() private var sections: [any Sectionable] = [] } @@ -29,7 +29,7 @@ extension FAQController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -51,11 +51,11 @@ private extension FAQController { mainView.contentCollectionView.register( MyPageMyCommentTitleSectionCell.self, forCellWithReuseIdentifier: MyPageMyCommentTitleSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyPageListSectionCell.self, forCellWithReuseIdentifier: MyPageListSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( FAQDropdownSectionCell.self, forCellWithReuseIdentifier: FAQDropdownSectionCell.identifiers @@ -78,12 +78,12 @@ extension FAQController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -99,11 +99,11 @@ extension FAQController: UICollectionViewDelegate, UICollectionViewDataSource { func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -118,7 +118,7 @@ extension FAQController: UICollectionViewDelegate, UICollectionViewDataSource { } return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let cell = collectionView.cellForItem(at: indexPath) as? MyPageListSectionCell { reactor?.action.onNext(.mailInquiryCellTapped(controller: self)) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift index 1f32d7e2..ba9947a4 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class FAQReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -20,22 +20,22 @@ final class FAQReactor: Reactor { case backButtonTapped(controller: BaseViewController) case mailInquiryCellTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController) case moveToMailApp(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -80,7 +80,7 @@ final class FAQReactor: Reactor { title: "고객센터 상담은 어디서 할 수 있나요?", content: "[마이페이지 > 고객문의 > 메일로 문의]에서 할 수 있으며, 주말, 공휴일을 제외한 평일 오전 9시부터 오후 6시까지 운영해요.", isOpen: false - ), + ) ]) private let qnaTitleSection = MyPageMyCommentTitleSection(inputDataList: [.init(title: "직접 문의하기")]) @@ -94,7 +94,7 @@ final class FAQReactor: Reactor { init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -109,7 +109,7 @@ final class FAQReactor: Reactor { return Observable.just(.moveToMailApp(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -128,27 +128,27 @@ final class FAQReactor: Reactor { } return newState } - + func showMailAppRecoveryAlert(controller: BaseViewController) { - + let alert = UIAlertController( title: "'Mail' 앱을 복원하겠습니까?", message: "계속하려면 App Store에서 'Mail' 앱을\n다운로드하십시오", preferredStyle: .alert ) - + alert.addAction(UIAlertAction(title: "App Store로 이동", style: .default, handler: { _ in // 📌 App Store의 메일 앱 복구 페이지 열기 if let mailAppURL = URL(string: "itms-apps://itunes.apple.com/app/id1108187098") { UIApplication.shared.open(mailAppURL, options: [:], completionHandler: nil) } })) - + alert.addAction(UIAlertAction(title: "취소", style: .cancel, handler: nil)) - + controller.present(alert, animated: true, completion: nil) } - + func getSection() -> [any Sectionable] { return [ spacing24Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift index 715c815e..2ba04ea5 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct FAQDropdownSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = FAQDropdownSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct FAQDropdownSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 0, bottom: 0, trailing: 0) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift index b8910b60..57a7e833 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift @@ -7,43 +7,41 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class FAQDropdownSectionCell: UICollectionViewCell { - + // MARK: - Components let contentStackView: UIStackView = { let view = UIStackView() view.axis = .vertical return view }() - + let listContentButton = UIButton() - + let qLabel: UILabel = { let label = UILabel() label.setLineHeightText(text: "Q", font: .EngFont(style: .bold, size: 16), lineHeight: 1) label.textColor = .blu500 return label }() - + let titleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let dropDownImageView: UIImageView = { - let view = UIImageView() - return view + return UIImageView() }() - + let dropContentView: UIView = { let view = UIView() view.backgroundColor = .pb4 return view }() - + let dropContentLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 @@ -51,16 +49,16 @@ final class FAQDropdownSectionCell: UICollectionViewCell { }() var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -74,7 +72,7 @@ private extension FAQDropdownSectionCell { contentStackView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + listContentButton.snp.makeConstraints { make in make.height.equalTo(59).priority(.high) } @@ -94,7 +92,7 @@ private extension FAQDropdownSectionCell { make.centerY.equalToSuperview() make.trailing.equalToSuperview().inset(20) } - + dropContentView.addSubview(dropContentLabel) dropContentLabel.snp.makeConstraints { make in make.top.bottom.equalToSuperview().inset(16) @@ -112,7 +110,7 @@ extension FAQDropdownSectionCell: Inputable { var content: String? var isOpen: Bool } - + func injection(with input: Input) { titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .medium, size: 14)) dropContentLabel.setLineHeightText(text: input.content, font: .KorFont(style: .regular, size: 14), lineHeight: 1.5) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift index 0ecc6ab2..93a270ec 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift @@ -10,26 +10,26 @@ import UIKit import SnapKit final class FAQView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "고객문의", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,13 +37,13 @@ final class FAQView: UIView { // MARK: - SetUp private extension FAQView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift index 14b45244..e699c2d3 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift @@ -7,42 +7,42 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageController: BaseViewController, View { - + typealias Reactor = MyPageReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyPageView() - + private let headerView: UIView = { let view = UIView() view.backgroundColor = .w100 view.alpha = 0 return view }() - + private let settingButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "icon_gear_white"), for: .normal) - button.tintColor = .g1000 + button.tintColor = .g1000 return button }() - + private var sections: [any Sectionable] = [] private var commentCellTapped: PublishSubject = .init() private var listCellTapped: PublishSubject = .init() - + private var isBrightImage: Bool = false - + private var scrollAlpha: CGFloat = 0 - + } // MARK: - Life Cycle @@ -51,7 +51,7 @@ extension MyPageController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = false @@ -61,7 +61,7 @@ extension MyPageController { // MARK: - SetUp private extension MyPageController { func setUp() { - + if let layout = reactor?.compositionalLayout { mainView.contentCollectionView.collectionViewLayout = layout } @@ -74,19 +74,19 @@ private extension MyPageController { mainView.contentCollectionView.register( MyPageProfileSectionCell.self, forCellWithReuseIdentifier: MyPageProfileSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyPageMyCommentTitleSectionCell.self, forCellWithReuseIdentifier: MyPageMyCommentTitleSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyPageCommentSectionCell.self, forCellWithReuseIdentifier: MyPageCommentSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyPageListSectionCell.self, forCellWithReuseIdentifier: MyPageListSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( MyPageLogoutSectionCell.self, forCellWithReuseIdentifier: MyPageLogoutSectionCell.identifiers @@ -95,13 +95,13 @@ private extension MyPageController { mainView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + view.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.bottom.equalTo(view.safeAreaLayoutGuide.snp.top).offset(44) } - + view.addSubview(settingButton) settingButton.snp.makeConstraints { make in make.trailing.equalTo(headerView.snp.trailing).inset(16) @@ -118,7 +118,7 @@ extension MyPageController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + commentCellTapped .withUnretained(self) .map { (owner, index) in @@ -126,7 +126,7 @@ extension MyPageController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + listCellTapped .withUnretained(self) .map { (owner, title) in @@ -134,7 +134,7 @@ extension MyPageController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + settingButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -142,18 +142,17 @@ extension MyPageController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in owner.settingButton.isHidden = !state.isLogin owner.sections = state.sections owner.mainView.contentCollectionView.reloadData() - } .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -171,7 +170,7 @@ extension MyPageController { owner.settingButton.tintColor = .w100 owner.systemStatusBarIsDark.accept(false) } - + } } } @@ -185,18 +184,18 @@ extension MyPageController: UICollectionViewDelegate, UICollectionViewDataSource func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) guard let reactor = reactor else { return cell } - + if let cell = cell as? MyPageProfileSectionCell { let originHeight = 162 + 49 + 44 + view.safeAreaInsets.top cell.updateHeight(height: originHeight) @@ -209,7 +208,7 @@ extension MyPageController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? MyPageMyCommentTitleSectionCell { cell.button.rx.tap .withUnretained(self) @@ -219,7 +218,7 @@ extension MyPageController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? MyPageLogoutSectionCell { cell.logoutButton.rx.tap .map { Reactor.Action.logoutButtonTapped } @@ -228,7 +227,7 @@ extension MyPageController: UICollectionViewDelegate, UICollectionViewDataSource } return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if let cell = collectionView.cellForItem(at: indexPath) as? MyPageCommentSectionCell { commentCellTapped.onNext(indexPath.row) @@ -238,7 +237,7 @@ extension MyPageController: UICollectionViewDelegate, UICollectionViewDataSource listCellTapped.onNext(title) } } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { if let cell = mainView.contentCollectionView.cellForItem(at: IndexPath(row: 0, section: 0)) as? MyPageProfileSectionCell { let contentOffsetY = scrollView.contentOffset.y diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift index b15851c9..7ac24f0b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -5,10 +5,10 @@ // Created by SeoJunYoung on 12/30/24. // -import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift +import UIKit final class MyPageReactor: Reactor { @@ -129,7 +129,7 @@ final class MyPageReactor: Reactor { ) ] // 내가 댓글 단 팝업 리스트 - owner.commentSection.inputDataList = response.myCommentedPopUpList.map { + owner.commentSection.inputDataList = response.myCommentedPopUpList.map { .init(popUpImagePath: $0.mainImageUrl, title: $0.popUpStoreName, popUpID: $0.popUpStoreId) } if !owner.commentSection.inputDataList.isEmpty { @@ -185,8 +185,8 @@ final class MyPageReactor: Reactor { case .logout: let service = KeyChainService() - let _ = service.deleteToken(type: .accessToken) - let _ = service.deleteToken(type: .refreshToken) + _ = service.deleteToken(type: .accessToken) + _ = service.deleteToken(type: .refreshToken) ToastMaker.createToast(message: "로그아웃 되었어요") DispatchQueue.main.async { [weak self] in self?.action.onNext(.viewWillAppear) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift index 274f90fb..f92f51fc 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyPageCommentSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyPageCommentSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute(68), diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift index 2f17bf4c..72b73d0a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyPageCommentSectionCell: UICollectionViewCell { - + // MARK: - Components private let firstBackgroundView: UIView = { let view = UIView() @@ -20,20 +20,20 @@ final class MyPageCommentSectionCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + private let imageBackgroundView: UIView = { let view = UIView() view.backgroundColor = .w100 view.layer.cornerRadius = 31 return view }() - + private let gradientView: AnimatedGradientView = { let view = AnimatedGradientView() view.isHidden = true return view }() - + private let imageView: UIImageView = { let view = UIImageView() view.layer.cornerRadius = 28 @@ -41,20 +41,19 @@ final class MyPageCommentSectionCell: UICollectionViewCell { view.contentMode = .scaleAspectFill return view }() - + private let titleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 11) - return label + return PPLabel(style: .regular, fontSize: 11) }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -68,24 +67,24 @@ private extension MyPageCommentSectionCell { make.leading.trailing.top.equalToSuperview() make.height.equalTo(68) } - + firstBackgroundView.addSubview(gradientView) gradientView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + firstBackgroundView.addSubview(imageBackgroundView) imageBackgroundView.snp.makeConstraints { make in make.size.equalTo(62) make.center.equalToSuperview() } - + imageBackgroundView.addSubview(imageView) imageView.snp.makeConstraints { make in make.size.equalTo(56) make.center.equalToSuperview() } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview() @@ -100,12 +99,12 @@ extension MyPageCommentSectionCell: Inputable { var popUpID: Int64 var isFirstCell: Bool = false } - + func injection(with input: Input) { imageView.setPPImage(path: input.popUpImagePath) titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 11)) titleLabel.textAlignment = .center - + if input.isFirstCell { gradientView.isHidden = false } else { @@ -115,39 +114,39 @@ extension MyPageCommentSectionCell: Inputable { } class AnimatedGradientView: UIView { - + private let gradientLayer = CAGradientLayer() - + override init(frame: CGRect) { super.init(frame: frame) setupGradient() } - + required init?(coder: NSCoder) { super.init(coder: coder) setupGradient() } - + private func setupGradient() { // 초기 그라디언트 색상 설정 gradientLayer.colors = [ UIColor.init(hexCode: "#1570FC").cgColor, UIColor.init(hexCode: "#00E6BD").cgColor ] - + gradientLayer.startPoint = CGPoint(x: 0, y: 0) gradientLayer.endPoint = CGPoint(x: 1, y: 1) gradientLayer.frame = bounds layer.insertSublayer(gradientLayer, at: 0) - + animateGradient() } - + override func layoutSubviews() { super.layoutSubviews() gradientLayer.frame = bounds // 레이아웃 변경 시 반영 } - + private func animateGradient() { let animation = CABasicAnimation(keyPath: "colors") animation.fromValue = gradientLayer.colors @@ -158,7 +157,7 @@ class AnimatedGradientView: UIView { animation.duration = 1 // 색이 부드럽게 바뀌는 시간 animation.autoreverses = true // 원래 색으로 돌아가게 함 animation.repeatCount = .infinity // 무한 반복 - + gradientLayer.add(animation, forKey: "colorChange") } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift index b31885e8..441344c6 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyPageListSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyPageListSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct MyPageListSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift index faa94ae6..31d71249 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift @@ -7,33 +7,32 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyPageListSectionCell: UICollectionViewCell { - + // MARK: - Components let titleLabel = UILabel() - + private let rightImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_right_gray") return view }() - + private let subTitleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -47,13 +46,13 @@ private extension MyPageListSectionCell { make.centerY.equalToSuperview() make.leading.equalToSuperview() } - + contentView.addSubview(rightImageView) rightImageView.snp.makeConstraints { make in make.trailing.centerY.equalToSuperview() make.size.equalTo(22) } - + contentView.addSubview(subTitleLabel) subTitleLabel.snp.makeConstraints { make in make.centerY.trailing.equalToSuperview() @@ -66,10 +65,10 @@ extension MyPageListSectionCell: Inputable { var title: String? var subTitle: String? } - + func injection(with input: Input) { titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 15)) - + if input.subTitle == nil { rightImageView.isHidden = false subTitleLabel.isHidden = true diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift index be5246e8..89795be7 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyPageLogoutSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyPageLogoutSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct MyPageLogoutSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift index dc46e212..3c737be7 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift @@ -7,30 +7,29 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyPageLogoutSectionCell: UICollectionViewCell { - + // MARK: - Components let logoutButton: PPButton = { - let button = PPButton(style: .secondary, text: "로그아웃") - return button + return PPButton(style: .secondary, text: "로그아웃") }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -51,7 +50,7 @@ extension MyPageLogoutSectionCell: Inputable { struct Input { } - + func injection(with input: Input) { } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift index e87b6626..20d2a2b2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyPageMyCommentTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyPageMyCommentTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct MyPageMyCommentTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift index f9820f1c..e6ccae2a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift @@ -7,34 +7,32 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyPageMyCommentTitleSectionCell: UICollectionViewCell { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16) - return label + return PPLabel(style: .bold, fontSize: 16) }() - + let button: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -48,7 +46,7 @@ private extension MyPageMyCommentTitleSectionCell { titleLabel.snp.makeConstraints { make in make.centerY.leading.equalToSuperview() } - + contentView.addSubview(button) button.snp.makeConstraints { make in make.centerY.trailing.equalToSuperview() @@ -61,17 +59,17 @@ extension MyPageMyCommentTitleSectionCell: Inputable { var title: String? var buttonTitle: String? } - + func injection(with input: Input) { titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) - + if input.buttonTitle != nil { let buttonTitle = NSAttributedString( string: input.buttonTitle ?? "", attributes: [ - .font : UIFont.KorFont(style: .regular, size: 13)!, - .underlineStyle : NSUnderlineStyle.single.rawValue, - .foregroundColor : UIColor.g600 + .font: UIFont.KorFont(style: .regular, size: 13)!, + .underlineStyle: NSUnderlineStyle.single.rawValue, + .foregroundColor: UIColor.g600 ] ) button.setAttributedTitle(buttonTitle, for: .normal) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift index 8276831c..430fae01 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyPageProfileSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyPageProfileSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -35,8 +35,7 @@ struct MyPageProfileSection: Sectionable { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - - return section + + return NSCollectionLayoutSection(group: group) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift index c7edecae..642118cf 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift @@ -7,34 +7,30 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyPageProfileSectionCell: UICollectionViewCell { - + // MARK: - Components private let backGroundTrailingView: UIView = { - let view = UIView() - return view + return UIView() }() - + private let backGroundImageView: UIImageView = { - let view = UIImageView() - return view + return UIImageView() }() - + lazy var blurView: UIVisualEffectView = { let blurEffect = UIBlurEffect(style: .regular) - let view = UIVisualEffectView(effect: blurEffect) - return view + return UIVisualEffectView(effect: blurEffect) }() - + private let profileView: UIView = { - let view = UIView() - return view + return UIView() }() - + private let profileImageView: UIImageView = { let view = UIImageView() view.layer.cornerRadius = 32 @@ -42,42 +38,39 @@ final class MyPageProfileSectionCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + private let bottomView: UIView = { let view = UIView() view.backgroundColor = .w100 return view }() - + let nickNameLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + private let descriptionLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + private let bottomHoleView: UIView = { let view = UIView() view.backgroundColor = .w100 view.alpha = 0 return view }() - + private let loginView: UIView = { - let view = UIView() - return view + return UIView() }() - + private let loginLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18, text: "나에게 딱 맞는\n팝업스토어 만나러 가기") label.textColor = .w100 label.numberOfLines = 2 return label }() - + let loginButton: UIButton = { let button = UIButton() button.setTitle("로그인/회원가입", for: .normal) @@ -87,28 +80,28 @@ final class MyPageProfileSectionCell: UICollectionViewCell { button.layer.cornerRadius = 4 return button }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() addHolesToCell() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() } - + var cellHeight: Constraint? var containerTopInset: Constraint? - + var isBright: PublishSubject = .init() } @@ -121,62 +114,62 @@ private extension MyPageProfileSectionCell { make.edges.equalToSuperview() cellHeight = make.height.equalTo(162 + 49 + 44).priority(.high).constraint } - + backGroundTrailingView.addSubview(backGroundImageView) backGroundImageView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.size.equalTo(UIScreen.main.bounds.width) } - + backGroundTrailingView.addSubview(blurView) blurView.snp.makeConstraints { make in make.edges.equalToSuperview() } - + backGroundTrailingView.addSubview(profileView) profileView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.height.equalTo(162) containerTopInset = make.top.equalToSuperview().constraint } - + backGroundTrailingView.addSubview(bottomView) bottomView.snp.makeConstraints { make in make.bottom.leading.trailing.equalToSuperview() make.height.equalTo(49) } - + backGroundTrailingView.addSubview(loginView) loginView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.height.equalTo(162) make.bottom.equalTo(bottomView.snp.top) } - + profileView.addSubview(profileImageView) profileImageView.snp.makeConstraints { make in make.size.equalTo(64) make.top.equalToSuperview().inset(17) make.leading.equalToSuperview().inset(20) } - + profileView.addSubview(nickNameLabel) nickNameLabel.snp.makeConstraints { make in make.centerY.equalTo(profileImageView) make.leading.equalTo(profileImageView.snp.trailing).offset(10) } - + profileView.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(profileImageView.snp.bottom).offset(25) make.leading.equalToSuperview().inset(20) } - + contentView.addSubview(bottomHoleView) bottomHoleView.snp.makeConstraints { make in make.edges.equalTo(bottomView) } - + contentView.addSubview(loginButton) loginButton.snp.makeConstraints { make in make.width.equalTo(120) @@ -184,32 +177,32 @@ private extension MyPageProfileSectionCell { make.bottom.equalToSuperview().inset(97) make.leading.equalToSuperview().inset(20) } - + contentView.addSubview(loginLabel) loginLabel.snp.makeConstraints { make in make.bottom.equalTo(loginButton.snp.top).offset(-16) make.leading.equalToSuperview().inset(20) } } - + private func addHolesToCell() { // 전체 영역 경로 let fullPath = UIBezierPath(rect: bounds) - + // 왼쪽 아래와 오른쪽 아래 구멍을 뚫을 위치 설정 (이미지뷰의 frame 위치 고려) let holeCenter = CGPoint(x: bounds.maxX / 2, y: bounds.minY) - + // 구멍을 만드는 경로 생성 (반지름 6) let holePath = UIBezierPath(arcCenter: holeCenter, radius: 12, startAngle: 0, endAngle: .pi, clockwise: true) - + // 구멍 경로를 전체 경로에서 빼기 fullPath.append(holePath) - + // 기존에 구멍을 뚫을 경로를 추가하는 레이어 let holeLayer = CAShapeLayer() holeLayer.path = fullPath.cgPath holeLayer.fillRule = .evenOdd - + // 구멍을 추가하는 서브 레이어로 삽입 bottomView.layer.mask = holeLayer } @@ -222,7 +215,7 @@ extension MyPageProfileSectionCell: Inputable { var nickName: String? var description: String? } - + func injection(with input: Input) { if input.isLogin { profileView.isHidden = false @@ -255,19 +248,19 @@ extension MyPageProfileSectionCell: Inputable { loginLabel.isHidden = false loginButton.isHidden = false blurView.isHidden = true - + } } - + func updateHeight(height: CGFloat) { cellHeight?.update(offset: height) layoutIfNeeded() } - + func updateAlpha(alpha: CGFloat) { bottomHoleView.alpha = alpha } - + func updateContentTopInset(inset: CGFloat) { containerTopInset?.update(offset: inset) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift index 967bc557..832979d9 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class MyPageView: UIView { - + // MARK: - Components let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) @@ -22,7 +22,7 @@ final class MyPageView: UIView { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -30,7 +30,7 @@ final class MyPageView: UIView { // MARK: - SetUp private extension MyPageView { - + func setUpConstraints() { self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift index d886c21b..1673c99e 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift @@ -7,22 +7,22 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyCommentController: BaseViewController, View { - + typealias Reactor = MyCommentReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyCommentView() - + private var sections: [any Sectionable] = [] - + private var cellTapped: PublishSubject = .init() } @@ -46,7 +46,7 @@ private extension MyCommentController { } mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self - + mainView.contentCollectionView.register( CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers @@ -59,7 +59,7 @@ private extension MyCommentController { MyCommentedPopUpGridSectionCell.self, forCellWithReuseIdentifier: MyCommentedPopUpGridSectionCell.identifiers ) - + view.backgroundColor = .g50 view.addSubview(mainView) mainView.snp.makeConstraints { make in @@ -75,7 +75,7 @@ extension MyCommentController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -83,7 +83,7 @@ extension MyCommentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -91,7 +91,7 @@ extension MyCommentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -107,19 +107,18 @@ extension MyCommentController: UICollectionViewDelegate, UICollectionViewDataSou func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 3 { cellTapped.onNext(indexPath.row) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index bc297806..83445aac 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -8,37 +8,37 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyCommentReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case listTapped(controller: BaseViewController, row: Int) case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case skip case moveToDetailScene(controller: BaseViewController, row: Int) case moveToRecentScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -52,17 +52,17 @@ final class MyCommentReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var listCountSection = CommentListTitleSection(inputDataList: []) private var listSection = MyCommentedPopUpGridSection(inputDataList: []) private var spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private var spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -93,7 +93,7 @@ final class MyCommentReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -113,7 +113,7 @@ final class MyCommentReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ spacing16Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift index 2b287e57..bbe9f9af 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct ListCountButtonSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = ListCountButtonSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct ListCountButtonSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift index de479782..41fb7421 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift @@ -7,47 +7,45 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class ListCountButtonSectionCell: UICollectionViewCell { - + // MARK: - Components - + private let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g400 return label }() - + private let dropDownImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_dropdown") return view }() - + private let buttonTitleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let dropdownButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -62,20 +60,20 @@ private extension ListCountButtonSectionCell { make.leading.equalToSuperview() make.centerY.equalToSuperview() } - + dropdownButton.addSubview(dropDownImageView) dropDownImageView.snp.makeConstraints { make in make.size.equalTo(22) make.top.trailing.bottom.equalToSuperview() } - + dropdownButton.addSubview(buttonTitleLabel) buttonTitleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.trailing.equalTo(dropDownImageView.snp.leading).offset(-6) make.centerY.equalToSuperview() } - + self.addSubview(dropdownButton) dropdownButton.snp.makeConstraints { make in make.trailing.equalToSuperview() @@ -89,7 +87,7 @@ extension ListCountButtonSectionCell: Inputable { var count: Int64 var buttonTitle: String? } - + func injection(with input: Input) { countLabel.setLineHeightText(text: "총 \(input.count)개", font: .KorFont(style: .regular, size: 13)) buttonTitleLabel.setLineHeightText(text: input.buttonTitle, font: .KorFont(style: .regular, size: 13)) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift index 25bc49e9..8d0b9c95 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift @@ -10,26 +10,26 @@ import UIKit import SnapKit final class MyCommentView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "내가 코멘트한 팝업", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,13 +37,13 @@ final class MyCommentView: UIView { // MARK: - SetUp private extension MyCommentView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift index 3b41bf85..96612580 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct MyCommentedPopUpGridSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = MyCommentedPopUpGridSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute((UIScreen.main.bounds.width - 40 - 8) / 2), @@ -34,7 +34,7 @@ struct MyCommentedPopUpGridSection: Sectionable { ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(8) - + // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift index 29a04bdf..0c88ef82 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift @@ -7,11 +7,11 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class MyCommentedPopUpGridSectionCell: UICollectionViewCell { - + // MARK: - Components private let contentImageView: UIImageView = { let view = UIImageView() @@ -19,32 +19,31 @@ final class MyCommentedPopUpGridSectionCell: UICollectionViewCell { view.clipsToBounds = true return view }() - + private let titleLabel: UILabel = { let label = UILabel() label.textColor = .blu500 return label }() - + private let contentLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + private let dateLabel: UILabel = { let label = UILabel() label.textColor = .g400 return label }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -60,25 +59,25 @@ private extension MyCommentedPopUpGridSectionCell { contentView.layer.shadowOpacity = 1 contentView.layer.shadowRadius = 8 contentView.layer.shadowOffset = CGSize(width: 0, height: 2) - + contentView.addSubview(contentImageView) contentImageView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(contentView.bounds.width) } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(contentImageView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(12) } - + contentView.addSubview(contentLabel) contentLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(12) } - + contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.top.equalTo(contentLabel.snp.bottom).offset(8) @@ -97,7 +96,7 @@ extension MyCommentedPopUpGridSectionCell: Inputable { var startDate: String? var endDate: String? } - + func injection(with input: Input) { contentImageView.setPPImage(path: input.imageURL) titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 11)) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift index b107a650..04030d12 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class MyCommentSortedModalController: BaseViewController, View { - + typealias Reactor = MyCommentSortedModalReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyCommentSortedModalView() } @@ -48,13 +48,13 @@ extension MyCommentSortedModalController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.sortedSegmentControl.rx.selectedSegmentIndex .skip(1) .map { Reactor.Action.selectedSegmentControl(row: $0) } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .compactMap { (owner, _) in @@ -62,7 +62,7 @@ extension MyCommentSortedModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.xmarkButton.rx.tap .withUnretained(self) .compactMap { (owner, _) in @@ -70,7 +70,7 @@ extension MyCommentSortedModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -92,7 +92,7 @@ extension MyCommentSortedModalController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } - + var longFormHeight: PanModalHeight { return .contentHeight(250) } @@ -106,4 +106,3 @@ extension MyCommentSortedModalController: PanModalPresentable { return 20 } } - diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift index ec940c78..60106273 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyCommentSortedModalReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -18,13 +18,13 @@ final class MyCommentSortedModalReactor: Reactor { case saveButtonTapped(controller: BaseViewController) case xmarkButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case setSelectedIndex(row: Int) case dismissScene(controller: BaseViewController, isSave: Bool) } - + struct State { var isSetView: Bool = false var originSortedCode: String @@ -32,18 +32,17 @@ final class MyCommentSortedModalReactor: Reactor { var saveButtonIsEnabled: Bool = false var isSave: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - - + // MARK: - init init(sortedCode: String) { self.initialState = State(originSortedCode: sortedCode) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -57,7 +56,7 @@ final class MyCommentSortedModalReactor: Reactor { return Observable.just(.dismissScene(controller: controller, isSave: false)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isSetView = false diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift index 625953d9..f5bf99ee 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift @@ -10,35 +10,32 @@ import UIKit import SnapKit final class MyCommentSortedModalView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") }() - + let xmarkButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let sortedSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .base, segments: ["최신순","반응순"]) - return control + return PPSegmentedControl(type: .base, segments: ["최신순", "반응순"]) }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -46,33 +43,33 @@ final class MyCommentSortedModalView: UIView { // MARK: - SetUp private extension MyCommentSortedModalView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) } - + self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(sortedSegmentControl) sortedSegmentControl.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(48) } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(50) } - + } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift index abcfb3d0..535b6b12 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageNoticeDetailController: BaseViewController, View { - + typealias Reactor = MyPageNoticeDetailReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyPageNoticeDetailView() } @@ -48,7 +48,7 @@ extension MyPageNoticeDetailController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -56,7 +56,7 @@ extension MyPageNoticeDetailController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift index 7fc3d1c1..02bb5176 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift @@ -6,44 +6,44 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyPageNoticeDetailReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController) } - + struct State { var title: String? var date: String? var content: String? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var title: String? var date: String? var content: String? var noticeID: Int64 - + let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) // MARK: - init init(noticeID: Int64) { self.noticeID = noticeID self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -60,7 +60,7 @@ final class MyPageNoticeDetailReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift index 2ae6a1d2..a4ae7b9d 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift @@ -10,17 +10,16 @@ import UIKit import SnapKit final class MyPageNoticeDetailView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "공지사항", font: .KorFont(style: .regular, size: 15)) return view }() - + let titleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() let dateLabel: UILabel = { let label = UILabel() @@ -32,16 +31,16 @@ final class MyPageNoticeDetailView: UIView { label.numberOfLines = 0 return label }() - + private let scrollView: UIScrollView = UIScrollView() private let contentView: UIView = UIView() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -49,26 +48,26 @@ final class MyPageNoticeDetailView: UIView { // MARK: - SetUp private extension MyPageNoticeDetailView { - + func setUpConstraints() { self.backgroundColor = .g50 self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(scrollView) scrollView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) make.leading.trailing.bottom.equalToSuperview() } - + scrollView.addSubview(contentView) contentView.snp.makeConstraints { make in make.edges.equalToSuperview() make.width.equalToSuperview() } - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(24) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift index f09947a0..10a2ffd7 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift @@ -7,22 +7,22 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageNoticeController: BaseViewController, View { - + typealias Reactor = MyPageNoticeReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyPageNoticeView() - + private var sections: [any Sectionable] = [] - + private var cellTapped: PublishSubject = .init() } @@ -32,7 +32,7 @@ extension MyPageNoticeController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -50,11 +50,11 @@ private extension MyPageNoticeController { mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( NoticeListSectionCell.self, forCellWithReuseIdentifier: NoticeListSectionCell.identifiers @@ -70,7 +70,7 @@ private extension MyPageNoticeController { // MARK: - Methods extension MyPageNoticeController { func bind(reactor: Reactor) { - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -78,12 +78,12 @@ extension MyPageNoticeController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -91,7 +91,7 @@ extension MyPageNoticeController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -107,11 +107,11 @@ extension MyPageNoticeController: UICollectionViewDelegate, UICollectionViewData func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -120,7 +120,7 @@ extension MyPageNoticeController: UICollectionViewDelegate, UICollectionViewData guard let reactor = reactor else { return cell } return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 3 { cellTapped.onNext(indexPath.row) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index 5b24879d..ab4da90c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -8,34 +8,34 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyPageNoticeReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case listCellTapped(controller: BaseViewController, row: Int) case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToDetailScene(controller: BaseViewController, row: Int) case moveToRecentScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -53,12 +53,11 @@ final class MyPageNoticeReactor: Reactor { private var listSection = NoticeListSection(inputDataList: []) private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -78,7 +77,7 @@ final class MyPageNoticeReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -93,7 +92,7 @@ final class MyPageNoticeReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ spacing16Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift index cb08ae4f..bb0af74b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift @@ -10,26 +10,26 @@ import UIKit import SnapKit final class MyPageNoticeView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "공지사항", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -37,18 +37,18 @@ final class MyPageNoticeView: UIView { // MARK: - SetUp private extension MyPageNoticeView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) make.leading.trailing.bottom.equalToSuperview() } - + } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift index c95268f7..c1c6f4a9 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct NoticeListSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = NoticeListSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct NoticeListSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift index 7f6eede7..c06f9e4a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift @@ -7,23 +7,22 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class NoticeListSectionCell: UICollectionViewCell { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 14) - return label + return PPLabel(style: .medium, fontSize: 14) }() - + private let dateLabel: UILabel = { let label = UILabel() label.textColor = .g400 return label }() - + private let arrowImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_right_gray") @@ -31,12 +30,12 @@ final class NoticeListSectionCell: UICollectionViewCell { }() let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -50,14 +49,13 @@ private extension NoticeListSectionCell { make.leading.equalToSuperview() make.top.equalToSuperview().inset(20) } - + contentView.addSubview(dateLabel) dateLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.bottom.equalToSuperview().inset(20) } - - + contentView.addSubview(arrowImageView) arrowImageView.snp.makeConstraints { make in make.size.equalTo(22) @@ -73,7 +71,7 @@ extension NoticeListSectionCell: Inputable { var date: String? var noticeID: Int64 } - + func injection(with input: Input) { titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .medium, size: 14)) dateLabel.setLineHeightText(text: input.date, font: .EngFont(style: .regular, size: 12)) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift index a17e4108..1ecd1e02 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift @@ -7,21 +7,21 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class CategoryEditModalController: BaseViewController, View { - + typealias Reactor = CategoryEditModalReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = CategoryEditModalView() - + private var sections: [any Sectionable] = [] } @@ -42,7 +42,7 @@ private extension CategoryEditModalController { mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self mainView.contentCollectionView.register(TagSectionCell.self, forCellWithReuseIdentifier: TagSectionCell.identifiers) - + view.addSubview(mainView) mainView.snp.makeConstraints { make in make.edges.equalTo(view.safeAreaLayoutGuide) @@ -57,7 +57,7 @@ extension CategoryEditModalController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.xmarkButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -65,7 +65,7 @@ extension CategoryEditModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -73,7 +73,7 @@ extension CategoryEditModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -90,19 +90,18 @@ extension CategoryEditModalController: UICollectionViewDelegate, UICollectionVie func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { reactor?.action.onNext(.cellTapped(row: indexPath.row)) } @@ -113,7 +112,7 @@ extension CategoryEditModalController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } - + var longFormHeight: PanModalHeight { return .contentHeight(360) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index f4b209f7..d02975cd 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class CategoryEditModalReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -20,24 +20,24 @@ final class CategoryEditModalReactor: Reactor { case cellTapped(row: Int) case saveButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController, isEdit: Bool) } - + struct State { var sections: [any Sectionable] = [] var originSelectedID: [Int64] var saveButtonIsEnable: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let originSelectedID: [Int64] - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -51,17 +51,17 @@ final class CategoryEditModalReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var signUpUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private var userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) private var tagSection = TagSection(inputDataList: []) - + // MARK: - init init(selectedID: [Int64]) { self.originSelectedID = selectedID self.initialState = State(originSelectedID: selectedID) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -106,7 +106,7 @@ final class CategoryEditModalReactor: Reactor { .andThen(Observable.just(.moveToRecentScene(controller: controller, isEdit: true))) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state let originArray = originSelectedID.sorted(by: <) @@ -118,7 +118,7 @@ final class CategoryEditModalReactor: Reactor { if isEdit { ToastMaker.createToast(message: "수정사항을 반영했어요")} controller.dismiss(animated: true) } - + if currentArray.isEmpty { newState.saveButtonIsEnable = false } else { @@ -126,7 +126,7 @@ final class CategoryEditModalReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [tagSection] } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift index 13be5401..537ab835 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift @@ -10,36 +10,34 @@ import UIKit import SnapKit final class CategoryEditModalView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "관심 카테고리를 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "관심 카테고리를 선택해주세요") }() - + let xmarkButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.isScrollEnabled = false return view }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -47,28 +45,28 @@ final class CategoryEditModalView: UIView { // MARK: - SetUp private extension CategoryEditModalView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) } - + self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(32) make.leading.trailing.equalToSuperview() make.height.equalTo(195) } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift index e4467054..92b2c880 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class InfoEditModalController: BaseViewController, View { - + typealias Reactor = InfoEditModalReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = InfoEditModalView() } @@ -51,12 +51,12 @@ extension InfoEditModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.genderSegmentControl.rx.selectedSegmentIndex .map { Reactor.Action.changeGender(index: $0)} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.ageButton.button.rx.tap .withUnretained(self) .map { (owner, _) in @@ -64,7 +64,7 @@ extension InfoEditModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -72,7 +72,7 @@ extension InfoEditModalController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -92,7 +92,7 @@ extension InfoEditModalController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } - + var longFormHeight: PanModalHeight { return .contentHeight(390) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift index 252ca754..df0ae566 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class InfoEditModalReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -22,7 +22,7 @@ final class InfoEditModalReactor: Reactor { case changeAge(age: Int32) case saveButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case setGender(index: Int) @@ -30,24 +30,24 @@ final class InfoEditModalReactor: Reactor { case moveToAgeSelectedScene(controller: BaseViewController) case moveToRecentScene(controller: BaseViewController, isEdit: Bool) } - + struct State { var age: Int32 var gender: String? var isLoadView: Bool = true var saveButtonEnable: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + var originAge: Int32 var originGender: String? var currentAge: Int32 = 0 var currentGender: String? - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) // MARK: - init init(age: Int32, gender: String?) { @@ -55,7 +55,7 @@ final class InfoEditModalReactor: Reactor { self.originGender = gender self.initialState = State(age: age, gender: gender) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -74,7 +74,7 @@ final class InfoEditModalReactor: Reactor { .andThen(Observable.just(.moveToRecentScene(controller: controller, isEdit: true))) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isLoadView = false @@ -96,7 +96,6 @@ final class InfoEditModalReactor: Reactor { .disposed(by: nextController.disposeBag) } - case .moveToRecentScene(let controller, let isEdit): if isEdit { ToastMaker.createToast(message: "수정사항을 반영했어요") } controller.dismiss(animated: true) @@ -106,7 +105,7 @@ final class InfoEditModalReactor: Reactor { case .setAge(let age): newState.age = age } - + if newState.gender == originGender && newState.age == originAge { newState.saveButtonEnable = false } else { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift index 0d48161e..71d05ee9 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift @@ -10,50 +10,44 @@ import UIKit import SnapKit final class InfoEditModalView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "사용자 정보를 설정해주세요.") - return label + return PPLabel(style: .bold, fontSize: 18, text: "사용자 정보를 설정해주세요.") }() - + let xmarkButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + private let genderTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "성별") - return label + return PPLabel(style: .regular, fontSize: 13, text: "성별") }() - + let genderSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .base, segments: ["남성", "여성", "선택안함"]) - return control + return PPSegmentedControl(type: .base, segments: ["남성", "여성", "선택안함"]) }() - + private let ageTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "나이") - return label + return PPLabel(style: .regular, fontSize: 13, text: "나이") }() - + let ageButton: AgeSelectedButton = { - let label = AgeSelectedButton() - return label + return AgeSelectedButton() }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -61,46 +55,46 @@ final class InfoEditModalView: UIView { // MARK: - SetUp private extension InfoEditModalView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) } - + self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(genderTitleLabel) genderTitleLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) make.leading.equalToSuperview().inset(20) } - + self.addSubview(genderSegmentControl) genderSegmentControl.snp.makeConstraints { make in make.top.equalTo(genderTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(ageTitleLabel) ageTitleLabel.snp.makeConstraints { make in make.top.equalTo(genderSegmentControl.snp.bottom).offset(36) make.leading.equalToSuperview().inset(20) } - + self.addSubview(ageButton) ageButton.snp.makeConstraints { make in make.top.equalTo(ageTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(72) } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index 3b71886f..a6cee295 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -5,24 +5,24 @@ // Created by SeoJunYoung on 1/4/25. // -import UIKit import PhotosUI +import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxGesture +import RxSwift +import SnapKit final class ProfileEditController: BaseViewController, View { - + typealias Reactor = ProfileEditReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = ProfileEditView() - + var isFirstResponse: Bool = true } @@ -32,7 +32,7 @@ extension ProfileEditController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -53,60 +53,60 @@ private extension ProfileEditController { // MARK: - Methods extension ProfileEditController { func bind(reactor: Reactor) { - + mainView.rx.tapGesture() .withUnretained(self) .subscribe { (owner, _) in owner.mainView.endEditing(true) } .disposed(by: disposeBag) - + rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.nickNameTextField.rx.text .skip(1) .debounce(.milliseconds(300), scheduler: MainScheduler.asyncInstance) .map { Reactor.Action.changeNickName(nickName: $0)} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.nickNameTextField.rx.controlEvent(.editingDidBegin) .map { Reactor.Action.beginNickName } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.nickNameTextField.rx.controlEvent(.editingDidEnd) .map { Reactor.Action.endNickName } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.introTextView.rx.text .distinctUntilChanged() .skip(1) .map { Reactor.Action.changeIntro(intro: $0)} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.introTextView.rx.didBeginEditing .map { Reactor.Action.beginIntro } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.introTextView.rx.didEndEditing .map { Reactor.Action.endIntro } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.profileImageButton.rx.tap .withUnretained(self) .subscribe(onNext: { (owner, _) in owner.showActionSheet() }) .disposed(by: disposeBag) - + mainView.categoryButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -114,7 +114,7 @@ extension ProfileEditController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.infoButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -122,18 +122,17 @@ extension ProfileEditController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .map { Reactor.Action.saveButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - - + mainView.nickNameDuplicatedCheckButton.rx.tap .map { Reactor.Action.nickNameCheckButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -141,18 +140,18 @@ extension ProfileEditController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in guard let originProfileData = state.originProfileData else { return } - + if owner.isFirstResponse { owner.mainView.profileImageView.setPPImage(path: originProfileData.profileImageUrl) owner.mainView.nickNameTextField.text = originProfileData.nickname owner.mainView.introTextView.text = originProfileData.intro } - + let categoryTitleList = originProfileData.interestCategoryList.map { $0.category } let categoryTitle: String if let firstTitle = categoryTitleList.first { @@ -166,9 +165,8 @@ extension ProfileEditController { .setLineHeightText(text: categoryTitle, font: .KorFont(style: .regular, size: 13), lineHeight: 1) let userInfoTitle = "\(originProfileData.gender ?? "")・\(originProfileData.age)세" owner.mainView.infoButton.subTitleLabel - .setLineHeightText(text: userInfoTitle, font: .KorFont(style: .regular, size: 13) ,lineHeight: 1) - - + .setLineHeightText(text: userInfoTitle, font: .KorFont(style: .regular, size: 13), lineHeight: 1) + // NickName TextField 설정 owner.mainView.nickNameTextFieldTrailingView.layer.borderColor = state.nickNameState.borderColor?.cgColor owner.mainView.nickNameClearButton.isHidden = state.nickNameState.isHiddenClearButton @@ -178,7 +176,7 @@ extension ProfileEditController { owner.mainView.nickNameTextField.textColor = state.nickNameState.textFieldTextColor owner.mainView.nickNameTextCountLabel.text = "\(owner.mainView.nickNameTextField.text?.count ?? 0) / 10자" owner.mainView.nickNameDuplicatedCheckButton.isEnabled = state.nickNameState.duplicatedCheckButtonIsEnabled - + // Intro TextView 설정 owner.mainView.introTextCountLabel.text = "\(owner.mainView.introTextView.text?.count ?? 0) / 30자" owner.mainView.introTextTrailingView.layer.borderColor = state.introState.borderColor?.cgColor @@ -187,7 +185,7 @@ extension ProfileEditController { owner.mainView.introTextCountLabel.textColor = state.introState.textColor owner.mainView.introTextView.textColor = state.introState.textFieldTextColor owner.mainView.introPlaceHolderLabel.isHidden = state.introState.placeHolderIsHidden - + owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable owner.isFirstResponse = false } @@ -199,7 +197,7 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo func showActionSheet() { // ActionSheet 생성 let actionSheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet) - + // 버튼 추가 let takePhotoAction = UIAlertAction(title: "촬영하기", style: .default) { [weak self] _ in self?.showCamera() @@ -212,32 +210,32 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo self?.reactor?.action.onNext(.changeDefaultImage) } let cancelAction = UIAlertAction(title: "취소", style: .cancel) - + // 버튼 스타일 변경 (기본 이미지는 빨간색으로 표시) changeToDefaultImageAction.setValue(UIColor.red, forKey: "titleTextColor") - + // 버튼 추가 actionSheet.addAction(takePhotoAction) actionSheet.addAction(selectFromAlbumAction) actionSheet.addAction(changeToDefaultImageAction) actionSheet.addAction(cancelAction) - + present(actionSheet, animated: true) } - + func showCamera() { guard UIImagePickerController.isSourceTypeAvailable(.camera) else { Logger.log(message: "카메라를 사용할 수 없습니다.", category: .error) return } - + let imagePicker = UIImagePickerController() imagePicker.sourceType = .camera - + imagePicker.delegate = self present(imagePicker, animated: true) } - + // MARK: - PHPicker 실행 func showPHPicker() { var configuration = PHPickerConfiguration() @@ -247,26 +245,26 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo picker.delegate = self present(picker, animated: true) } - + // MARK: - UIImagePickerControllerDelegate - func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { if let selectedImage = info[.originalImage] as? UIImage { handleSelectedImage(selectedImage) } picker.dismiss(animated: true) } - + func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } - + // MARK: - PHPickerViewControllerDelegate func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { picker.dismiss(animated: true) - + for result in results { if result.itemProvider.canLoadObject(ofClass: UIImage.self) { - result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (image, error) in + result.itemProvider.loadObject(ofClass: UIImage.self) { [weak self] (image, _) in if let selectedImage = image as? UIImage { DispatchQueue.main.async { self?.handleSelectedImage(selectedImage) @@ -276,7 +274,7 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo } } } - + // MARK: - 이미지 처리 func handleSelectedImage(_ image: UIImage) { // 선택한 이미지 처리 diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 80f0740e..fef97600 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -5,15 +5,15 @@ // Created by SeoJunYoung on 1/4/25. // -import UIKit import PhotosUI +import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class ProfileEditReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -31,7 +31,7 @@ final class ProfileEditReactor: Reactor { case saveButtonTapped case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToInfoEditScene(controller: BaseViewController) @@ -40,39 +40,39 @@ final class ProfileEditReactor: Reactor { case moveToRecentScene(controller: BaseViewController) case changeNickNameState } - + struct State { var originProfileData: GetMyProfileResponse? var saveButtonIsEnable: Bool = false var nickNameState: NickNameState = .myNickName var introState: IntroState = .validate } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var originProfileData: GetMyProfileResponse? - + var currentImage: UIImage? var isChangeImage: Bool = false var currentImagePath: String? - + var currentNickName: String? var nickNameIsActive: Bool = false - + var currentIntro: String? var introIsActive: Bool = false - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private let imageService = PreSignedService() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -124,7 +124,7 @@ final class ProfileEditReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -150,12 +150,12 @@ final class ProfileEditReactor: Reactor { case .changeNickNameState: newState.nickNameState = checkNickNameState(text: currentNickName, isActive: nickNameIsActive) } - + let originNickName = originProfileData?.nickname ?? "" let currentNickName = currentNickName ?? "" let originIntro = originProfileData?.intro ?? "" let currentIntro = currentIntro ?? "" - + if isChangeImage || originNickName != currentNickName || originIntro != currentIntro { if newState.nickNameState == .validate || newState.nickNameState == .validateActive || newState.nickNameState == .myNickName || newState.nickNameState == .myNickNameActive { if newState.introState == .validate || newState.introState == .validateActive || newState.introState == .empty || newState.introState == .emptyActive { @@ -169,10 +169,10 @@ final class ProfileEditReactor: Reactor { } else { newState.saveButtonIsEnable = false } - + return newState } - + func uploadS3() -> Observable { if let changeImage = currentImage { let newPath = "ProfileImage/\(UUID().uuidString).jpg" @@ -206,7 +206,7 @@ final class ProfileEditReactor: Reactor { } } } - + func editProfile() -> Observable { isChangeImage = false ToastMaker.createToast(message: "내용을 저장했어요") @@ -219,7 +219,7 @@ final class ProfileEditReactor: Reactor { ) .andThen(Observable.just(.loadView)) } - + func loadProfileData() -> Observable { return userAPIUseCase.getMyProfile() .withUnretained(self) @@ -231,27 +231,27 @@ final class ProfileEditReactor: Reactor { return .loadView } } - + func checkNickNameState(text: String?, isActive: Bool) -> NickNameState { guard let text = text, let originNickName = originProfileData?.nickname else { return isActive ? .emptyActive : .empty } if originNickName == text { return isActive ? .myNickNameActive : .myNickName } - + // textEmpty Check if text.isEmpty { return isActive ? .emptyActive : .empty } - + // kor and end Check let pattern = "^[가-힣a-zA-Z\\s]+$" // 허용하는 문자만 검사 let regex = try! NSRegularExpression(pattern: pattern) let range = NSRange(location: 0, length: text.utf16.count) if regex.firstMatch(in: text, options: [], range: range) == nil { return isActive ? .korAndEngActive : .korAndEng } - + // textLength Check - + if text.count < 2 { return isActive ? .shortLengthActive : .shortLength } if text.count > 10 { return isActive ? .longLengthActive : .longLength } return isActive ? .checkActive : .check } - + func checkIntroState(text: String?, isActive: Bool) -> IntroState { guard let text = text else { return isActive ? .emptyActive : .empty } if text.isEmpty { return isActive ? .emptyActive : .empty } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift index aa83db1a..844b3d7c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift @@ -10,31 +10,30 @@ import UIKit import SnapKit final class ProfileEditListButton: UIButton { - + // MARK: - Components let mainTitleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let subTitleLabel: UILabel = { let label = UILabel() label.textColor = .g400 return label }() - + let iconImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_right_gray") return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -42,19 +41,19 @@ final class ProfileEditListButton: UIButton { // MARK: - SetUp private extension ProfileEditListButton { - + func setUpConstraints() { self.addSubview(mainTitleLabel) mainTitleLabel.snp.makeConstraints { make in make.leading.centerY.equalToSuperview() } - + self.addSubview(iconImageView) iconImageView.snp.makeConstraints { make in make.size.equalTo(22) make.top.bottom.trailing.equalToSuperview() } - + self.addSubview(subTitleLabel) subTitleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift index c1faa431..c2adb140 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class ProfileEditView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() @@ -18,13 +18,12 @@ final class ProfileEditView: UIView { return view }() let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + private let scrollView: UIScrollView = UIScrollView() private let contentView: UIView = UIView() - + let profileImageView: UIImageView = { let view = UIImageView() view.layer.cornerRadius = 48 @@ -51,10 +50,9 @@ final class ProfileEditView: UIView { view.contentMode = .scaleAspectFit return view }() - + private let nickNameTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "별명") - return label + return PPLabel(style: .regular, fontSize: 13, text: "별명") }() let nickNameTextFieldTrailingView: UIStackView = { let view = UIStackView() @@ -116,10 +114,9 @@ final class ProfileEditView: UIView { button.setAttributedTitle(disabledAttributedTitle, for: .disabled) return button }() - + private let introTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "자기소개") - return label + return PPLabel(style: .regular, fontSize: 13, text: "자기소개") }() let introTextTrailingView: UIView = { let view = UIView() @@ -143,38 +140,36 @@ final class ProfileEditView: UIView { return label }() let introDescriptionLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 12) - return label + return PPLabel(style: .medium, fontSize: 12) }() let introPlaceHolderLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "자기소개를 입력해주세요") label.textColor = .g200 return label }() - + private let customInfoTitlelabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 16, text: "맞춤정보") - return label + return PPLabel(style: .bold, fontSize: 16, text: "맞춤정보") }() - + let categoryButton: ProfileEditListButton = { let button = ProfileEditListButton() button.mainTitleLabel.setLineHeightText(text: "관심 카테고리", font: .KorFont(style: .regular, size: 15)) return button }() - + let infoButton: ProfileEditListButton = { let button = ProfileEditListButton() button.mainTitleLabel.setLineHeightText(text: "사용자 정보", font: .KorFont(style: .regular, size: 15)) return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -182,7 +177,7 @@ final class ProfileEditView: UIView { // MARK: - SetUp private extension ProfileEditView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in @@ -209,7 +204,7 @@ private extension ProfileEditView { setUpIntroView() setUpCustomInfoView() } - + func setUpProfileImageView() { contentView.addSubview(profileImageView) profileImageView.snp.makeConstraints { make in @@ -228,7 +223,7 @@ private extension ProfileEditView { make.center.equalToSuperview() } } - + func setUpNickNameView() { contentView.addSubview(nickNameTitleLabel) nickNameTitleLabel.snp.makeConstraints { make in @@ -261,7 +256,7 @@ private extension ProfileEditView { make.trailing.equalToSuperview().inset(24) } } - + func setUpIntroView() { contentView.addSubview(introTitleLabel) introTitleLabel.snp.makeConstraints { make in @@ -294,21 +289,21 @@ private extension ProfileEditView { make.leading.equalToSuperview().inset(20) } } - + func setUpCustomInfoView() { contentView.addSubview(customInfoTitlelabel) customInfoTitlelabel.snp.makeConstraints { make in make.top.equalTo(introTextTrailingView.snp.bottom).offset(27) make.leading.equalToSuperview().inset(20) } - + contentView.addSubview(categoryButton) categoryButton.snp.makeConstraints { make in make.top.equalTo(customInfoTitlelabel.snp.bottom).offset(32) make.leading.equalToSuperview().inset(22) make.trailing.equalToSuperview().inset(20) } - + contentView.addSubview(infoButton) infoButton.snp.makeConstraints { make in make.top.equalTo(categoryButton.snp.bottom).offset(32) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift index a21e7549..12bb9237 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift @@ -7,22 +7,22 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageRecentController: BaseViewController, View { - + typealias Reactor = MyPageRecentReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = MyPageRecentView() - + private var sections: [any Sectionable] = [] - + private var cellTapped: PublishSubject = .init() } @@ -32,7 +32,7 @@ extension MyPageRecentController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -50,11 +50,11 @@ private extension MyPageRecentController { mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( DetailSimilarSectionCell.self, forCellWithReuseIdentifier: DetailSimilarSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers @@ -74,7 +74,7 @@ extension MyPageRecentController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -82,7 +82,7 @@ extension MyPageRecentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, row) in @@ -90,13 +90,13 @@ extension MyPageRecentController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in owner.sections = state.sections if state.isReloadView { owner.mainView.contentCollectionView.reloadData() } - + } .disposed(by: disposeBag) } @@ -107,11 +107,11 @@ extension MyPageRecentController: UICollectionViewDelegate, UICollectionViewData func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -120,7 +120,7 @@ extension MyPageRecentController: UICollectionViewDelegate, UICollectionViewData guard let reactor = reactor else { return cell } return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { let contentHeight = scrollView.contentSize.height let scrollViewHeight = scrollView.frame.size.height @@ -129,7 +129,7 @@ extension MyPageRecentController: UICollectionViewDelegate, UICollectionViewData reactor?.action.onNext(.changePage) } } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { if indexPath.section == 3 { cellTapped.onNext(indexPath.row) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index d7d85365..300b6bcb 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyPageRecentReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -20,30 +20,30 @@ final class MyPageRecentReactor: Reactor { case backButtonTapped(controller: BaseViewController) case cellTapped(controller: BaseViewController, row: Int) } - + enum Mutation { case loadView case skip case moveToRecentScene(controller: BaseViewController) case moveToDetailScene(controller: BaseViewController, row: Int) } - + struct State { var sections: [any Sectionable] = [] var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private var isLoading: Bool = false private var totalPage: Int32 = 0 private var currentPage: Int32 = 0 private var size: Int32 = 100 - + private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -57,16 +57,16 @@ final class MyPageRecentReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var countSection = CommentListTitleSection(inputDataList: []) private var listSection = RecentPopUpSection(inputDataList: []) private var spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -111,7 +111,7 @@ final class MyPageRecentReactor: Reactor { return Observable.just(.moveToDetailScene(controller: controller, row: row)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -130,7 +130,7 @@ final class MyPageRecentReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ spacing16Section, diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift index 0f585389..a608b4e9 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift @@ -10,14 +10,14 @@ import UIKit import SnapKit final class MyPageRecentView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "최근 본 팝업", font: .KorFont(style: .regular, size: 15)) return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 @@ -28,7 +28,7 @@ final class MyPageRecentView: UIView { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -36,13 +36,13 @@ final class MyPageRecentView: UIView { // MARK: - SetUp private extension MyPageRecentView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift index 70afc695..a744e56e 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct RecentPopUpSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = DetailSimilarSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .absolute((UIScreen.main.bounds.width - 40 - 8) / 2), @@ -38,8 +38,7 @@ struct RecentPopUpSection: Sectionable { let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 12 - + return section } } - diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift index fbea50cf..68883315 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift @@ -7,24 +7,24 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class MyPageTermsController: BaseViewController, View { - + typealias Reactor = MyPageTermsReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "약관", font: .KorFont(style: .regular, size: 15)) return view }() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -38,15 +38,15 @@ final class MyPageTermsController: BaseViewController, View { return sections[section].getSection(section: section, env: env) } }() - + private lazy var contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: self.compositionalLayout) view.backgroundColor = .g50 return view }() - + private var sections: [any Sectionable] = [] - + private let cellTapped: PublishSubject = .init() } @@ -56,7 +56,7 @@ extension MyPageTermsController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -76,7 +76,7 @@ private extension MyPageTermsController { make.top.equalTo(headerView.snp.bottom) make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide) } - + contentCollectionView.delegate = self contentCollectionView.dataSource = self contentCollectionView.register(CommentListTitleSectionCell.self, forCellWithReuseIdentifier: CommentListTitleSectionCell.identifiers) @@ -92,7 +92,7 @@ extension MyPageTermsController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -100,7 +100,7 @@ extension MyPageTermsController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map { (owner, indexPath) in @@ -108,7 +108,7 @@ extension MyPageTermsController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -124,19 +124,18 @@ extension MyPageTermsController: UICollectionViewDelegate, UICollectionViewDataS func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { cellTapped.onNext(indexPath) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift index bc4fffc3..18bdf23e 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift @@ -7,47 +7,47 @@ import Foundation import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class MyPageTermsReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear - case cellTapped(indexPath: IndexPath, controller : BaseViewController) + case cellTapped(indexPath: IndexPath, controller: BaseViewController) case backButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) case moveToRecentScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private lazy var countSection = CommentListTitleSection(inputDataList: [.init(count: termsSection.dataCount)]) private var termsSection = MyPageListSection(inputDataList: [ .init(title: "서비스이용약관"), .init(title: "개인정보처리방침"), .init(title: "위치정보 이용약관") ]) - + private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -57,10 +57,10 @@ final class MyPageTermsReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) case .cellTapped(let indexPath, let controller): return Observable.just(.moveToDetailScene(controller: controller, indexPath: indexPath)) - + } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -75,7 +75,7 @@ final class MyPageTermsReactor: Reactor { } return newState } - + func getSections() -> [any Sectionable] { return [ spacing16Section, @@ -84,7 +84,7 @@ final class MyPageTermsReactor: Reactor { termsSection ] } - + func getContent(index: Int) -> String { if let path = Bundle.main.path(forResource: "Terms", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) as? [String: String], diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift index cf7f2d28..b8fbe817 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift @@ -6,28 +6,28 @@ // import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class WithdrawlCheckModalController: BaseViewController, View { - + typealias Reactor = WithdrawlCheckModalReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = WithdrawlCheckModalView() - + init(nickName: String?) { super.init() let title = "\(nickName ?? "")님, 팝풀 서비스를\n정말 탈퇴하시겠어요?" mainView.titleLabel.setLineHeightText(text: title, font: .KorFont(style: .bold, size: 18), lineHeight: 1.312) mainView.titleLabel.numberOfLines = 2 } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -58,7 +58,7 @@ extension WithdrawlCheckModalController { .map { Reactor.Action.cancelButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.agreeButton.rx.tap .map { Reactor.Action.appleyButtonTapped } .bind(to: reactor.action) @@ -71,7 +71,7 @@ extension WithdrawlCheckModalController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } - + var longFormHeight: PanModalHeight { return .contentHeight(370) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift index 81094df7..51b0c6ad 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift @@ -6,41 +6,41 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class WithdrawlCheckModalReactor: Reactor { - + enum ModalState { case none case cancel case apply } - + // MARK: - Reactor enum Action { case cancelButtonTapped case appleyButtonTapped } - + enum Mutation { case setModalState(state: ModalState) } - + struct State { var state: ModalState = .none } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -50,7 +50,7 @@ final class WithdrawlCheckModalReactor: Reactor { return Observable.just(.setModalState(state: .cancel)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift index 860c1739..5c72ed50 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift @@ -10,30 +10,27 @@ import UIKit import SnapKit final class WithdrawlCheckModalView: UIView { - + // MARK: - Components let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18) - return label + return PPLabel(style: .bold, fontSize: 18) }() - + private let trailingView: UIView = { let view = UIView() view.backgroundColor = .g50 view.layer.cornerRadius = 4 return view }() - + let cancelButton: PPButton = { - let button = PPButton(style: .secondary, text: "취소") - return button + return PPButton(style: .secondary, text: "취소") }() - + let agreeButton: PPButton = { - let button = PPButton(style: .primary, text: "동의 후 탈퇴하기") - return button + return PPButton(style: .primary, text: "동의 후 탈퇴하기") }() - + let firstLabel: UILabel = { let text = "서비스 탈퇴 시 회원 전용 서비스 이용이 불가하며 회원 데이터는 일괄 삭제 처리돼요." let label = UILabel() @@ -42,7 +39,7 @@ final class WithdrawlCheckModalView: UIView { label.textColor = .g600 return label }() - + let secondLabel: UILabel = { let text = "탈퇴 후에는 계정을 다시 살리거나 복구할 수 없어요." let label = UILabel() @@ -51,7 +48,7 @@ final class WithdrawlCheckModalView: UIView { label.textColor = .g600 return label }() - + let thirdLabel: PPLabel = { let text = "작성하신 코멘트나 댓글 등의 일부 정보는 계속 남아있을 수 있어요. " let label = PPLabel(style: .regular, fontSize: 13, text: text) @@ -59,20 +56,20 @@ final class WithdrawlCheckModalView: UIView { label.textColor = .g600 return label }() - + let textStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 12 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -80,20 +77,20 @@ final class WithdrawlCheckModalView: UIView { // MARK: - SetUp private extension WithdrawlCheckModalView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(32) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(trailingView) trailingView.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(cancelButton) cancelButton.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) @@ -101,7 +98,7 @@ private extension WithdrawlCheckModalView { make.height.equalTo(50) make.width.equalTo(108) } - + self.addSubview(agreeButton) agreeButton.snp.makeConstraints { make in make.bottom.equalToSuperview() @@ -109,13 +106,13 @@ private extension WithdrawlCheckModalView { make.trailing.equalToSuperview().inset(20) make.height.equalTo(50) } - + trailingView.addSubview(textStackView) textStackView.snp.makeConstraints { make in // make.top.leading.trailing.equalToSuperview().inset(20) make.edges.equalToSuperview().inset(20) } - + textStackView.addArrangedSubview(firstLabel) textStackView.addArrangedSubview(secondLabel) textStackView.addArrangedSubview(thirdLabel) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift index 75be91ad..8bc6c8c8 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift @@ -7,16 +7,16 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class WithdrawlCompleteController: BaseViewController { - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = WithdrawlCompleteView() } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift index 3538fc0a..e85f5a9a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift @@ -10,14 +10,14 @@ import UIKit import SnapKit final class WithdrawlCompleteView: UIView { - + // MARK: - Components private let imageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_check_fill_blue") return view }() - + private let titleLabel: PPLabel = { let text = "탈퇴 완료\n다음에 또 만나요" let label = PPLabel(style: .bold, fontSize: 20, text: text) @@ -25,7 +25,7 @@ final class WithdrawlCompleteView: UIView { label.textAlignment = .center return label }() - + private let descriptionLabel: PPLabel = { let text = "고객님이 만족하실 수 있는\n팝풀이 되도록 앞으로도 노력할게요 :)" let label = PPLabel(style: .regular, fontSize: 15, text: text) @@ -35,18 +35,17 @@ final class WithdrawlCompleteView: UIView { label.textColor = .g600 return label }() - + let checkButton: PPButton = { - let button = PPButton(style: .primary, text: "확인") - return button + return PPButton(style: .primary, text: "확인") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -54,7 +53,7 @@ final class WithdrawlCompleteView: UIView { // MARK: - SetUp private extension WithdrawlCompleteView { - + func setUpConstraints() { self.addSubview(imageView) imageView.snp.makeConstraints { make in @@ -62,19 +61,19 @@ private extension WithdrawlCompleteView { make.top.equalToSuperview().inset(84) make.centerX.equalToSuperview() } - + self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(imageView.snp.bottom).offset(64) make.centerX.equalToSuperview() } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(16) make.centerX.equalToSuperview() } - + self.addSubview(checkButton) checkButton.snp.makeConstraints { make in make.leading.bottom.trailing.equalToSuperview().inset(20) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift index 862397ec..de4f14c0 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct WithdrawlCheckSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = WithdrawlCheckSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift index 9d308a9d..2e0f1eb5 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift @@ -7,23 +7,21 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class WithdrawlCheckSectionCell: UICollectionViewCell { - + // MARK: - Components private let checkImageView: UIImageView = { - let view = UIImageView() - return view + return UIImageView() }() - + private let titleLabel: UILabel = { - let label = UILabel() - return label + return UILabel() }() - + let textView: UITextView = { let view = UITextView() view.textContainerInset = .zero @@ -32,14 +30,13 @@ final class WithdrawlCheckSectionCell: UICollectionViewCell { view.backgroundColor = .clear return view }() - + let cellButton: UIButton = UIButton() - + private let trailingView: UIView = { - let view = UIView() - return view + return UIView() }() - + private let textTrailingView: UIView = { let view = UIView() view.layer.cornerRadius = 4 @@ -47,36 +44,36 @@ final class WithdrawlCheckSectionCell: UICollectionViewCell { view.layer.borderColor = UIColor.g100.cgColor view.backgroundColor = .w100 view.clipsToBounds = true - + return view }() - + private let contentStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 10 return view }() - + private let placeHolderLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "탈퇴 이유를 입력해주세요") label.textColor = .g200 return label }() - + var disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() bind() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -92,7 +89,7 @@ private extension WithdrawlCheckSectionCell { make.centerY.equalToSuperview() make.leading.equalToSuperview() } - + cellButton.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalTo(checkImageView.snp.trailing).offset(8) @@ -100,12 +97,12 @@ private extension WithdrawlCheckSectionCell { make.top.bottom.equalToSuperview() make.trailing.equalToSuperview() } - + contentView.addSubview(cellButton) cellButton.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + contentView.addSubview(contentStackView) contentStackView.addArrangedSubview(cellButton) contentStackView.addArrangedSubview(trailingView) @@ -113,31 +110,30 @@ private extension WithdrawlCheckSectionCell { make.edges.equalToSuperview() } - trailingView.snp.makeConstraints { make in make.height.equalTo(120) } - + trailingView.addSubview(textTrailingView) textTrailingView.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.trailing.equalToSuperview() make.top.bottom.equalToSuperview() } - + textTrailingView.addSubview(textView) textView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview().inset(16) make.height.equalTo(70) } - + textTrailingView.addSubview(placeHolderLabel) placeHolderLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(16) make.leading.equalToSuperview().inset(20) } } - + func bind() { textView.rx.text .withUnretained(self) @@ -156,7 +152,7 @@ extension WithdrawlCheckSectionCell: Inputable { var id: Int64 var text: String? } - + func injection(with input: Input) { let image = input.isSelected ? UIImage(named: "icon_check_fill") : UIImage(named: "icon_check") checkImageView.image = image diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift index 7692c604..de4f289b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift @@ -10,14 +10,14 @@ import UIKit import SnapKit final class WithdrawlReasonView: UIView { - + // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() view.headerLabel.setLineHeightText(text: "회원 탈퇴", font: .KorFont(style: .regular, size: 15)) return view }() - + private let titleLabel: UILabel = { let text = "탈퇴하려는 이유가\n무엇인가요?" let label = UILabel() @@ -25,7 +25,7 @@ final class WithdrawlReasonView: UIView { label.numberOfLines = 2 return label }() - + private let descriptionLabel: PPLabel = { let text = "알려주시는 내용을 참고해 더 나은 팝풀을\n만들어볼게요." let label = PPLabel(style: .regular, fontSize: 15, text: text) @@ -34,25 +34,24 @@ final class WithdrawlReasonView: UIView { label.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 15), lineHeight: 1.4) return label }() - + let skipButton: PPButton = { let button = PPButton(style: .secondary, text: "건너뛰기") button.setBackgroundColor(.w100, for: .normal) return button }() - + let checkButton: PPButton = { - let button = PPButton(style: .primary, text: "확인", disabledText: "확인") - return button + return PPButton(style: .primary, text: "확인", disabledText: "확인") }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.backgroundColor = .g50 @@ -63,7 +62,7 @@ final class WithdrawlReasonView: UIView { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -71,34 +70,34 @@ final class WithdrawlReasonView: UIView { // MARK: - SetUp private extension WithdrawlReasonView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() } - + self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalTo(headerView.snp.bottom).offset(64) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(buttonStackView) buttonStackView.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) make.height.equalTo(50) } - + buttonStackView.addArrangedSubview(skipButton) buttonStackView.addArrangedSubview(checkButton) - + self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalTo(descriptionLabel.snp.bottom).offset(48) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift index b584ab6b..c3ecb0f2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift @@ -7,22 +7,22 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit -import RxKeyboard +import RxCocoa import RxGesture +import RxKeyboard +import RxSwift +import SnapKit final class WithdrawlReasonController: BaseViewController, View { - + typealias Reactor = WithdrawlReasonReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = WithdrawlReasonView() - + private var sections: [any Sectionable] = [] } @@ -32,7 +32,7 @@ extension WithdrawlReasonController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -50,12 +50,12 @@ private extension WithdrawlReasonController { mainView.contentCollectionView.register( WithdrawlCheckSectionCell.self, forCellWithReuseIdentifier: WithdrawlCheckSectionCell.identifiers - ) + ) mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers ) - + view.backgroundColor = .g50 view.addSubview(mainView) mainView.snp.makeConstraints { make in @@ -73,7 +73,7 @@ extension WithdrawlReasonController { owner.view.endEditing(true) } .disposed(by: disposeBag) - + RxKeyboard.instance.visibleHeight .drive { [weak self] height in if height > 0 { @@ -93,7 +93,7 @@ extension WithdrawlReasonController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.headerView.backButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -101,7 +101,7 @@ extension WithdrawlReasonController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.checkButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -109,7 +109,7 @@ extension WithdrawlReasonController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.skipButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -117,7 +117,7 @@ extension WithdrawlReasonController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -136,24 +136,24 @@ extension WithdrawlReasonController: UICollectionViewDelegate, UICollectionViewD func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) guard let reactor = reactor else { return cell } - + if let cell = cell as? WithdrawlCheckSectionCell { cell.cellButton.rx.tap .map { Reactor.Action.cellTapped(row: indexPath.row) } .bind(to: reactor.action) .disposed(by: cell.disposeBag) - + cell.textView.rx.text .map { Reactor.Action.etcTextInput(text: $0)} .bind(to: reactor.action) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift index 29943ce2..ca08cbf5 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class WithdrawlReasonReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -22,25 +22,25 @@ final class WithdrawlReasonReactor: Reactor { case skipButtonTapped(controller: BaseViewController) case checkButtonTapped(controller: BaseViewController) } - + enum Mutation { case loadView case moveToRecentScene(controller: BaseViewController) case none case moveToCompleteScene(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var buttonIsEnabled: Bool = false var isReloadView: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -54,7 +54,7 @@ final class WithdrawlReasonReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var reasonSection = WithdrawlCheckSection(inputDataList: []) private var spacing156Section = SpacingSection(inputDataList: [.init(spacing: 156)]) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) @@ -64,7 +64,7 @@ final class WithdrawlReasonReactor: Reactor { init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -96,7 +96,7 @@ final class WithdrawlReasonReactor: Reactor { .andThen(Observable.just(.moveToCompleteScene(controller: controller))) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state newState.isReloadView = false @@ -125,9 +125,9 @@ final class WithdrawlReasonReactor: Reactor { .disposed(by: disposeBag) controller.navigationController?.pushViewController(nextController, animated: true) } - + let isEmpty = reasonSection.inputDataList.filter { $0.isSelected == true }.isEmpty - + if let etc = reasonSection.inputDataList.filter({ $0.title == "기타" }).first { if etc.isSelected { if etc.text?.isEmpty ?? true { @@ -143,7 +143,7 @@ final class WithdrawlReasonReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ reasonSection, diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift index 6db3bb92..92b01182 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SearchResultController: BaseViewController, View { - + typealias Reactor = SearchResultReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SearchResultView() private var sections: [any Sectionable] = [] private let cellTapped: PublishSubject = .init() @@ -30,7 +30,7 @@ extension SearchResultController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -45,7 +45,7 @@ private extension SearchResultController { } mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self - + mainView.contentCollectionView.register( SearchTitleSectionCell.self, forCellWithReuseIdentifier: SearchTitleSectionCell.identifiers @@ -72,7 +72,7 @@ private extension SearchResultController { // MARK: - Methods extension SearchResultController { func bind(reactor: Reactor) { - + cellTapped .withUnretained(self) .map({ (owner, indexPath) in @@ -80,7 +80,7 @@ extension SearchResultController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -103,11 +103,11 @@ extension SearchResultController: UICollectionViewDelegate, UICollectionViewData func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -122,7 +122,7 @@ extension SearchResultController: UICollectionViewDelegate, UICollectionViewData } return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { cellTapped.onNext(indexPath) } diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index 4dcde029..f4601985 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -8,31 +8,31 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SearchResultReactor: Reactor { - + // MARK: - Reactor enum Action { case returnSearch(text: String) case bookmarkButtonTapped(indexPath: IndexPath) case cellTapped(controller: BaseViewController, indexPath: IndexPath) } - + enum Mutation { case loadView case emptyView case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) } - + struct State { var sections: [any Sectionable] = [] var isEmptyResult: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private var popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) @@ -50,19 +50,19 @@ final class SearchResultReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var titleSection = SearchTitleSection(inputDataList: [.init(title: "포함된 팝업", buttonTitle: nil)]) private var searchCountSection = SearchResultCountSection(inputDataList: [.init(count: 65)]) private var searchListSection = HomeCardGridSection(inputDataList: []) private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -112,7 +112,7 @@ final class SearchResultReactor: Reactor { } } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -128,8 +128,7 @@ final class SearchResultReactor: Reactor { } return newState } - - + func getSection() -> [any Sectionable] { return [ spacing24Section, @@ -142,14 +141,14 @@ final class SearchResultReactor: Reactor { } func hasFinalConsonant(_ text: String) -> Bool { guard let lastCharacter = text.last else { return false } - + let unicodeValue = Int(lastCharacter.unicodeScalars.first!.value) - + // 한글 유니코드 범위 체크 let base = 0xAC00 let last = 0xD7A3 guard base...last ~= unicodeValue else { return false } - + // 종성 인덱스 계산 (받침이 있으면 1 이상) let finalConsonantIndex = (unicodeValue - base) % 28 return finalConsonantIndex != 0 diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift index 24ac0177..e3555ab5 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct SearchResultCountSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = SearchResultCountSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -38,7 +38,7 @@ struct SearchResultCountSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 5, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift index ffcf0156..d7c5d211 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift @@ -7,32 +7,32 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class SearchResultCountSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g600 return label }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -54,7 +54,7 @@ extension SearchResultCountSectionCell: Inputable { struct Input { var count: Int } - + func injection(with input: Input) { countLabel.text = "총 \(input.count)개를 찾았어요." } diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift index 74352be4..43bbd685 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift @@ -10,13 +10,12 @@ import UIKit import SnapKit final class SearchResultView: UIView { - + // MARK: - Components let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + let emptyLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "검색 결과가 없어요:(\n다른 키워드로 검색해주세요") label.textAlignment = .center @@ -24,13 +23,13 @@ final class SearchResultView: UIView { label.textColor = .g400 return label }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -38,14 +37,14 @@ final class SearchResultView: UIView { // MARK: - SetUp private extension SearchResultView { - + func setUpConstraints() { self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalToSuperview().offset(56) make.leading.trailing.bottom.equalToSuperview() } - + self.addSubview(emptyLabel) emptyLabel.snp.makeConstraints { make in make.top.equalTo(contentCollectionView.snp.top).inset(193) diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 7b00a9b1..9cac2f2b 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxGesture +import RxSwift +import SnapKit final class SearchController: BaseViewController, View { - + typealias Reactor = SearchReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SearchView() private var sections: [any Sectionable] = [] private let cellTapped: PublishSubject = .init() @@ -32,7 +32,7 @@ extension SearchController { super.viewDidLoad() setUp() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -81,7 +81,7 @@ extension SearchController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .withUnretained(self) .map({ (owner, indexPath) in @@ -89,13 +89,13 @@ extension SearchController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + pageChange .throttle(.milliseconds(1000), scheduler: MainScheduler.asyncInstance) .map { Reactor.Action.changePage } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -111,11 +111,11 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -130,7 +130,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? CancelableTagSectionCell { if searchList.isEmpty { cell.cancelButton.rx.tap @@ -151,7 +151,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource } } } - + if let cell = cell as? SearchCountTitleSectionCell { cell.sortedButton.rx.tap .withUnretained(self) @@ -161,7 +161,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } - + if let cell = cell as? HomeCardSectionCell { cell.bookmarkButton.rx.tap .map { Reactor.Action.bookmarkButtonTapped(indexPath: indexPath)} @@ -170,7 +170,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource } return cell } - + func scrollViewDidScroll(_ scrollView: UIScrollView) { mainView.endEditing(true) let contentHeight = scrollView.contentSize.height @@ -180,7 +180,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource pageChange.onNext(()) } } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { cellTapped.onNext(indexPath) } diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 396090d3..a8fa3122 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SearchReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -29,7 +29,7 @@ final class SearchReactor: Reactor { case bookmarkButtonTapped(indexPath: IndexPath) case resetSearchKeyWord } - + enum Mutation { case loadView case moveToCategoryScene(controller: BaseViewController) @@ -38,29 +38,29 @@ final class SearchReactor: Reactor { case setSearchKeyWord(text: String?) case resetSearchKeyWord } - + struct State { var sections: [any Sectionable] = [] var searchKeyWord: String? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private var sortedIndex: Int = 1 private var filterIndex: Int = 0 - + private var currentPage: Int32 = 0 private var lastAppendPage: Int32 = 0 private var lastPage: Int32 = 0 private var isLoading: Bool = false - + let userDefaultService = UserDefaultService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -74,10 +74,10 @@ final class SearchReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private let recentKeywordTitleSection = SearchTitleSection(inputDataList: [.init(title: "최근 검색어", buttonTitle: "모두삭제")]) private var recentKeywordSection = CancelableTagSection(inputDataList: []) - + private let searchTitleSection = SearchTitleSection(inputDataList: [.init(title: "팝업스토어 찾기")]) private var searchCategorySection = CancelableTagSection(inputDataList: [ .init(title: "카테고리", isSelected: false, isCancelAble: false) @@ -89,12 +89,12 @@ final class SearchReactor: Reactor { private let spacing18Section = SpacingSection(inputDataList: [.init(spacing: 18)]) private let spacing48Section = SpacingSection(inputDataList: [.init(spacing: 48)]) private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { let sort = sortedIndex == 0 ? "NEWEST" : "MOST_VIEWED,MOST_COMMENTED,MOST_BOOKMARKED" @@ -179,7 +179,7 @@ final class SearchReactor: Reactor { } } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -199,7 +199,7 @@ final class SearchReactor: Reactor { } else { owner.action.onNext(.changeCategory(categoryList: state.categoryIDList, categoryTitleList: state.categoryTitleList)) } - + } if state.isReset { owner.action.onNext(.resetCategory)} }) @@ -229,7 +229,7 @@ final class SearchReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] if searchList.isEmpty { @@ -262,19 +262,19 @@ final class SearchReactor: Reactor { ] } } - + func setSearchList() { let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] recentKeywordSection.inputDataList = searchList.map { return .init(title: $0) } } - + func appendSearchList(text: String?) { if let text = text { if !text.isEmpty { var searchList = userDefaultService.fetchArray(key: "searchList") ?? [] if searchList.contains(text) { let targetIndex = searchList.firstIndex(of: text)! - searchList.remove(at: targetIndex) + searchList.remove(at: targetIndex) } searchList = [text] + searchList userDefaultService.save(key: "searchList", value: searchList) @@ -282,19 +282,19 @@ final class SearchReactor: Reactor { } } } - + func removeSearchList(indexPath: IndexPath) { var searchList = userDefaultService.fetchArray(key: "searchList") ?? [] searchList.remove(at: indexPath.row) userDefaultService.save(key: "searchList", value: searchList) recentKeywordSection.inputDataList = searchList.map { return .init(title: $0) } } - + func resetSearchList() { userDefaultService.save(key: "searchList", value: []) recentKeywordSection.inputDataList = [] } - + func setBottomSearchList(sort: String?) -> Observable { let isOpen = filterIndex == 0 ? true : false let categorys = searchCategorySection.inputDataList.compactMap { $0.id } diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift index fb80227f..7a26b80f 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct CancelableTagSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = CancelableTagSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(100), diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift index 7a3afb15..e89783de 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift @@ -7,43 +7,41 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class CancelableTagSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let titleLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 11) - return label + return PPLabel(style: .medium, fontSize: 11) }() - + let cancelButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + private let contentStackView: UIStackView = { let view = UIStackView() view.alignment = .center view.spacing = 2 return view }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -56,7 +54,7 @@ private extension CancelableTagSectionCell { contentView.layer.cornerRadius = 15.5 contentView.clipsToBounds = true contentView.layer.borderWidth = 1 - + contentView.addSubview(contentStackView) contentStackView.snp.makeConstraints { make in make.top.bottom.equalToSuperview() @@ -65,7 +63,7 @@ private extension CancelableTagSectionCell { } contentStackView.addArrangedSubview(titleLabel) contentStackView.addArrangedSubview(cancelButton) - + titleLabel.snp.makeConstraints { make in make.height.equalTo(18) } @@ -82,7 +80,7 @@ extension CancelableTagSectionCell: Inputable { var isSelected: Bool = false var isCancelAble: Bool = true } - + func injection(with input: Input) { let xmarkImage = input.isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") cancelButton.setImage(xmarkImage, for: .normal) @@ -98,7 +96,7 @@ extension CancelableTagSectionCell: Inputable { contentView.layer.borderColor = UIColor.g200.cgColor } cancelButton.isHidden = !input.isCancelAble - + if input.isCancelAble { contentStackView.snp.updateConstraints { make in make.trailing.equalToSuperview().inset(8) diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift index 6feaa53b..41617011 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct SearchCountTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = SearchCountTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -38,7 +38,7 @@ struct SearchCountTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift index 7ae94994..bc765fe4 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift @@ -7,49 +7,47 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class SearchCountTitleSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let countLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.textColor = .g400 return label }() - + private let sortedTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13) - return label + return PPLabel(style: .regular, fontSize: 13) }() - + private let downImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_dropdown") view.isUserInteractionEnabled = false return view }() - + let sortedButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -64,19 +62,19 @@ private extension SearchCountTitleSectionCell { make.leading.equalToSuperview() make.centerY.equalToSuperview() } - + contentView.addSubview(sortedButton) sortedButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalToSuperview() } - + sortedButton.addSubview(sortedTitleLabel) sortedTitleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() } - + sortedButton.addSubview(downImageView) downImageView.snp.makeConstraints { make in make.size.equalTo(22) @@ -92,7 +90,7 @@ extension SearchCountTitleSectionCell: Inputable { var count: Int64 var sortedTitle: String? } - + func injection(with input: Input) { sortedTitleLabel.text = input.sortedTitle countLabel.text = "총 \(input.count)개" diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift index aa09c76a..3731bbaa 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct SearchTitleSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = SearchTitleSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1), @@ -37,7 +37,7 @@ struct SearchTitleSection: Sectionable { // 섹션 생성 let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift index adf7d9da..97d074a2 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift @@ -7,36 +7,35 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class SearchTitleSectionCell: UICollectionViewCell { - + // MARK: - Components var disposeBag = DisposeBag() - + private let sectionTitleLabel: UILabel = { let label = UILabel() label.font = .KorFont(style: .bold, size: 16) return label }() - + let titleButton: UIButton = { - let button = UIButton() - return button + return UIButton() }() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } - + override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() @@ -52,7 +51,7 @@ private extension SearchTitleSectionCell { make.centerY.equalToSuperview() make.height.equalTo(22) } - + self.addSubview(titleButton) titleButton.snp.makeConstraints { make in make.centerY.equalToSuperview() @@ -67,7 +66,7 @@ extension SearchTitleSectionCell: Inputable { var title: String? var buttonTitle: String? } - + func injection(with input: Input) { sectionTitleLabel.text = input.title if let buttonTitle = input.buttonTitle { diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift index f7925427..874e3b1a 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift @@ -10,19 +10,18 @@ import UIKit import SnapKit final class SearchView: UIView { - + // MARK: - Components let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - return view + return UICollectionView(frame: .zero, collectionViewLayout: .init()) }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -30,7 +29,7 @@ final class SearchView: UIView { // MARK: - SetUp private extension SearchView { - + func setUpConstraints() { self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift index da326158..c7ab66a9 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class SearchCategoryController: BaseViewController, View { - + typealias Reactor = SearchCategoryReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SearchCategoryView() private var sections: [any Sectionable] = [] private let cellTapped: PublishSubject = .init() @@ -59,12 +59,12 @@ extension SearchCategoryController { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + cellTapped .map { Reactor.Action.cellTapped(indexPath: $0)} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.resetButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -72,7 +72,7 @@ extension SearchCategoryController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.closeButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -80,7 +80,7 @@ extension SearchCategoryController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -88,7 +88,7 @@ extension SearchCategoryController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -105,11 +105,11 @@ extension SearchCategoryController: UICollectionViewDelegate, UICollectionViewDa func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath @@ -118,7 +118,7 @@ extension SearchCategoryController: UICollectionViewDelegate, UICollectionViewDa guard let reactor = reactor else { return cell } return cell } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { cellTapped.onNext(indexPath) } diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift index 8f5e4461..383d676f 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SearchCategoryReactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear @@ -21,14 +21,14 @@ final class SearchCategoryReactor: Reactor { case resetButtonTapped(controller: BaseViewController) case cellTapped(indexPath: IndexPath) } - + enum Mutation { case moveToRecentScene(controller: BaseViewController) case loadView case save(controller: BaseViewController) case reset(controller: BaseViewController) } - + struct State { var sections: [any Sectionable] = [] var categoryIDList: [Int64] = [] @@ -37,9 +37,9 @@ final class SearchCategoryReactor: Reactor { var isSave: Bool = false var isReset: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var originCategoryList: [Int64] @@ -58,13 +58,13 @@ final class SearchCategoryReactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + // MARK: - init init(originCategoryList: [Int64]) { self.initialState = State() self.originCategoryList = originCategoryList } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -89,7 +89,7 @@ final class SearchCategoryReactor: Reactor { return Observable.just(.reset(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -111,7 +111,7 @@ final class SearchCategoryReactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ tagSection diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift index 42ebb089..f8a452ca 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift @@ -10,48 +10,45 @@ import UIKit import SnapKit final class SearchCategoryView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "카테고리를 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "카테고리를 선택해주세요") }() - + let closeButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + let contentCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.isScrollEnabled = false return view }() - + let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + let resetButton: PPButton = { - let button = PPButton(style: .secondary, text: "초기화") - return button + return PPButton(style: .secondary, text: "초기화") }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - return button + return PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -59,14 +56,14 @@ final class SearchCategoryView: UIView { // MARK: - SetUp private extension SearchCategoryView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(12) } - + self.addSubview(closeButton) closeButton.snp.makeConstraints { make in make.size.equalTo(24) diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift index 9a16aa29..c9b9fe6f 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift @@ -7,39 +7,39 @@ import UIKit -import SnapKit +import Pageboy +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import Pageboy +import SnapKit import Tabman final class SearchMainController: BaseTabmanController, View { - + typealias Reactor = SearchMainReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SearchMainView() - + var beforeController: SearchController = { let controller = SearchController() controller.reactor = SearchReactor() return controller }() - + var afterController: SearchResultController = { let controller = SearchResultController() controller.reactor = SearchResultReactor() return controller }() - + lazy var controllers = [ beforeController, afterController ] - + var isResponseTextField: Bool = false } @@ -49,7 +49,7 @@ extension SearchMainController { super.viewDidLoad() setUp() } - + override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if !isResponseTextField { @@ -57,7 +57,7 @@ extension SearchMainController { isResponseTextField = true } } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) tabBarController?.tabBar.isHidden = true @@ -95,7 +95,7 @@ extension SearchMainController { } }) .disposed(by: disposeBag) - + // mainView.searchTextField.rx.controlEvent(.editingDidEndOnExit) // .withUnretained(self) // .map { (owner, _) in @@ -129,7 +129,6 @@ extension SearchMainController { }) .disposed(by: disposeBag) - mainView.searchTextField.rx.text .withUnretained(self) .subscribe(onNext: { (owner, text) in @@ -140,7 +139,7 @@ extension SearchMainController { } }) .disposed(by: disposeBag) - + mainView.cancelButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -156,7 +155,7 @@ extension SearchMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.clearButton.rx.tap .withUnretained(self) .subscribe { (owner, _) in @@ -164,7 +163,7 @@ extension SearchMainController { owner.mainView.clearButton.isHidden = true } .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -180,18 +179,18 @@ extension SearchMainController: PageboyViewControllerDataSource, TMBarDataSource func barItem(for bar: any Tabman.TMBar, at index: Int) -> any Tabman.TMBarItemable { return TMBarItem(title: "") } - + func numberOfViewControllers(in pageboyViewController: Pageboy.PageboyViewController) -> Int { return controllers.count } - + func viewController( for pageboyViewController: Pageboy.PageboyViewController, at index: Pageboy.PageboyViewController.PageIndex ) -> UIViewController? { return controllers[index] } - + func defaultPage( for pageboyViewController: Pageboy.PageboyViewController ) -> Pageboy.PageboyViewController.Page? { diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift index 29446caa..25c83527 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift @@ -6,33 +6,33 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SearchMainReactor: Reactor { - + // MARK: - Reactor enum Action { case returnSearchKeyWord(text: String?) } - + enum Mutation { case setSearchKeyWord(text: String?) } - + struct State { var searchKeyword: String? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -40,7 +40,7 @@ final class SearchMainReactor: Reactor { return Observable.just(.setSearchKeyWord(text: text)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift index d04eb79d..89364b85 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class SearchMainView: UIView { - + // MARK: - Components private let searchTrailingView: UIView = { let view = UIView() @@ -19,20 +19,20 @@ final class SearchMainView: UIView { view.clipsToBounds = true return view }() - + private let searchIconImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_search_gray") return view }() - + private let searchStackView: UIStackView = { let view = UIStackView() view.spacing = 4 view.alignment = .center return view }() - + let cancelButton: UIButton = { let button = UIButton(type: .system) button.setTitle("취소", for: .normal) @@ -41,33 +41,33 @@ final class SearchMainView: UIView { button.imageView?.contentMode = .scaleAspectFit return button }() - + let searchTextField: UITextField = { let view = UITextField() view.font = .KorFont(style: .regular, size: 14) view.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .KorFont(style: .regular, size: 14)!) return view }() - + let clearButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_clearButton"), for: .normal) return button }() - + private var headerStackView: UIStackView = { let view = UIStackView() view.alignment = .center view.spacing = 16 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -75,7 +75,7 @@ final class SearchMainView: UIView { // MARK: - SetUp private extension SearchMainView { - + func setUpConstraints() { searchTrailingView.snp.makeConstraints { make in make.height.equalTo(37) @@ -96,15 +96,15 @@ private extension SearchMainView { searchStackView.addArrangedSubview(searchIconImageView) searchStackView.addArrangedSubview(searchTextField) searchStackView.addArrangedSubview(clearButton) - + searchIconImageView.snp.makeConstraints { make in make.size.equalTo(20) } - + searchTextField.snp.makeConstraints { make in make.height.equalTo(21) } - + clearButton.snp.makeConstraints { make in make.size.equalTo(16) } diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift index 12c816d6..4fb31f8a 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class SearchSortedController: BaseViewController, View { - + typealias Reactor = SearchSortedReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SearchSortedView() } @@ -51,7 +51,7 @@ extension SearchSortedController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.filterSegmentControl.rx.controlEvent(.valueChanged) .withUnretained(self) .map({ (owner, _) in @@ -59,7 +59,7 @@ extension SearchSortedController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.sortedSegmentControl.rx.controlEvent(.valueChanged) .withUnretained(self) .map({ (owner, _) in @@ -67,7 +67,7 @@ extension SearchSortedController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.saveButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -75,7 +75,7 @@ extension SearchSortedController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift index 747b9bfa..12dfc2d5 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SearchSortedReactor: Reactor { - + // MARK: - Reactor enum Action { case closeButtonTapped(controller: BaseViewController) @@ -18,29 +18,29 @@ final class SearchSortedReactor: Reactor { case changeSortedIndex(index: Int) case saveButtonTapped(controller: BaseViewController) } - + enum Mutation { case moveToRecentScene(controller: BaseViewController) case loadView case save(controller: BaseViewController) } - + struct State { var filterIndex: Int var sortedIndex: Int var saveButtonIsEnable: Bool = false var isSave: Bool = false } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var originFilterIndex: Int var originSortedIndex: Int private var selectedFilterIndex: Int private var selectedSortedIndex: Int - + // MARK: - init init(filterIndex: Int, sortedIndex: Int) { self.initialState = State(filterIndex: filterIndex, sortedIndex: sortedIndex) @@ -49,7 +49,7 @@ final class SearchSortedReactor: Reactor { self.selectedFilterIndex = filterIndex self.selectedSortedIndex = sortedIndex } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -65,7 +65,7 @@ final class SearchSortedReactor: Reactor { return Observable.just(.save(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -74,7 +74,7 @@ final class SearchSortedReactor: Reactor { case .loadView: newState.filterIndex = selectedFilterIndex newState.sortedIndex = selectedSortedIndex - + if selectedFilterIndex != originFilterIndex || selectedSortedIndex != originSortedIndex { newState.saveButtonIsEnable = true } else { diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift index d528513c..243560f1 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift @@ -10,50 +10,44 @@ import UIKit import SnapKit final class SearchSortedView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "노출 순서를 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "노출 순서를 선택해주세요") }() - + let closeButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + private let filterTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "노출 조건") - return label + return PPLabel(style: .regular, fontSize: 13, text: "노출 조건") }() - + let filterSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .base, segments: ["오픈", "종료"], selectedSegmentIndex: 0) - return control + return PPSegmentedControl(type: .base, segments: ["오픈", "종료"], selectedSegmentIndex: 0) }() - + private let sortedTitleLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13, text: "팝업순서") - return label + return PPLabel(style: .regular, fontSize: 13, text: "팝업순서") }() - + let sortedSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) - return control + return PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) }() - + let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "저장", disabledText: "저장") - return button + return PPButton(style: .primary, text: "저장", disabledText: "저장") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -61,45 +55,45 @@ final class SearchSortedView: UIView { // MARK: - SetUp private extension SearchSortedView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) } - + self.addSubview(closeButton) closeButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(20) make.centerY.equalTo(titleLabel) } - + self.addSubview(filterTitleLabel) filterTitleLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) make.leading.equalToSuperview().inset(20) } - + self.addSubview(filterSegmentControl) filterSegmentControl.snp.makeConstraints { make in make.top.equalTo(filterTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(sortedTitleLabel) sortedTitleLabel.snp.makeConstraints { make in make.top.equalTo(filterSegmentControl.snp.bottom).offset(20) make.leading.equalToSuperview().inset(20) } - + self.addSubview(sortedSegmentControl) sortedSegmentControl.snp.makeConstraints { make in make.top.equalTo(sortedTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(saveButton) saveButton.snp.makeConstraints { make in make.top.equalTo(sortedSegmentControl.snp.bottom).offset(32) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift index 6580a573..6fd26f5c 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift @@ -7,46 +7,46 @@ import UIKit -import SnapKit +import Pageboy +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import Pageboy +import SnapKit import Tabman final class SignUpMainController: BaseTabmanController, View { - + typealias Reactor = SignUpMainReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SignUpMainView() - + var step1Controller: SignUpStep1Controller = { let controller = SignUpStep1Controller() controller.reactor = SignUpStep1Reactor() return controller }() - + var step2Controller: SignUpStep2Controller = { let controller = SignUpStep2Controller() controller.reactor = SignUpStep2Reactor() return controller }() - + var step3Controller: SignUpStep3Controller = { let controller = SignUpStep3Controller() controller.reactor = SignUpStep3Reactor() return controller }() - + var step4Controller: SignUpStep4Controller = { let controller = SignUpStep4Controller() controller.reactor = SignUpStep4Reactor() return controller }() - + lazy var controllers = [ step1Controller, step2Controller, @@ -78,7 +78,7 @@ private extension SignUpMainController { // MARK: - Methods extension SignUpMainController { func bind(reactor: Reactor) { - + // 취소버튼 이벤트 mainView.headerView.cancelButton.rx.tap .withUnretained(self) @@ -87,7 +87,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // 뒤로가기 버튼 mainView.headerView.backButton.rx.tap .withUnretained(self) @@ -97,7 +97,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step1 button tap 이벤트 step1Controller.mainView.completeButton.rx.tap .withUnretained(self) @@ -107,7 +107,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step2 button tap 이벤트 step2Controller.mainView.completeButton.rx.tap .withUnretained(self) @@ -117,7 +117,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step3 button tap 이벤트 step3Controller.mainView.completeButton.rx.tap .withUnretained(self) @@ -127,7 +127,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step3 Skip button tap 이벤트 step3Controller.mainView.skipButton.rx.tap .withUnretained(self) @@ -137,7 +137,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step4 button tap 이벤트 step4Controller.mainView.completeButton.rx.tap .withUnretained(self) @@ -148,7 +148,7 @@ extension SignUpMainController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + // step4 Skip button tap 이벤트 step4Controller.mainView.skipButton.rx.tap .withUnretained(self) @@ -160,7 +160,6 @@ extension SignUpMainController { .bind(to: reactor.action) .disposed(by: disposeBag) - // step1 Terms 이벤트 step1Controller.reactor?.state .map({ (state) in @@ -169,7 +168,7 @@ extension SignUpMainController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + // step2 nickName 이벤트 step2Controller.reactor?.state .map({ state in @@ -177,7 +176,7 @@ extension SignUpMainController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + // step3 category 이벤트 step3Controller.reactor?.state .map({ state in @@ -185,7 +184,7 @@ extension SignUpMainController { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + // step4 gender 이벤트 step4Controller.reactor?.state .map({ state in @@ -205,7 +204,6 @@ extension SignUpMainController { .bind(to: reactor.action) .disposed(by: disposeBag) - reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -225,18 +223,18 @@ extension SignUpMainController: PageboyViewControllerDataSource, TMBarDataSource func barItem(for bar: any Tabman.TMBar, at index: Int) -> any Tabman.TMBarItemable { return TMBarItem(title: "") } - + func numberOfViewControllers(in pageboyViewController: Pageboy.PageboyViewController) -> Int { return controllers.count } - + func viewController( for pageboyViewController: Pageboy.PageboyViewController, at index: Pageboy.PageboyViewController.PageIndex ) -> UIViewController? { return controllers[index] } - + func defaultPage( for pageboyViewController: Pageboy.PageboyViewController ) -> Pageboy.PageboyViewController.Page? { diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index 238105b7..363a7ec0 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -6,11 +6,11 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpMainReactor: Reactor { - + // MARK: - Reactor enum Action { case cancelButtonTapped(controller: BaseTabmanController) @@ -27,7 +27,7 @@ final class SignUpMainReactor: Reactor { case changeGender(gender: String?) case changeAge(age: Int?) } - + enum Mutation { case moveToLoginScene(controller: BaseTabmanController) case increasePageIndex(controller: BaseTabmanController, currentIndex: Int) @@ -41,7 +41,7 @@ final class SignUpMainReactor: Reactor { case setGender(gender: String?) case setAge(age: Int?) } - + struct State { var currentIndex: Int = 0 var isMarketingAgree: Bool = false @@ -52,25 +52,25 @@ final class SignUpMainReactor: Reactor { var categoryIDList: [Int64] = [] var age: Int? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + private var authrizationCode: String? - + private var signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private let userDefaultService = UserDefaultService() var isFirstResponderCase: Bool - + // MARK: - init init(isFirstResponderCase: Bool, authrizationCode: String?) { self.initialState = State() self.authrizationCode = authrizationCode self.isFirstResponderCase = isFirstResponderCase } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -105,7 +105,7 @@ final class SignUpMainReactor: Reactor { ]) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -121,9 +121,9 @@ final class SignUpMainReactor: Reactor { guard let socialType = userDefaultService.fetch(key: "socialType"), let nickName = newState.nickName, let gender = newState.gender else { return newState } - + signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) - + signUpAPIUseCase.trySignUp( nickName: nickName, gender: gender, @@ -146,7 +146,7 @@ final class SignUpMainReactor: Reactor { ToastMaker.createToast(message: "회원가입 실패:\(error.localizedDescription)") } .disposed(by: disposeBag) - + case .skipStep3(let controller, let currentIndex): if newState.categoryIDList.count >= 5 { newState.categorys = Array(newState.categoryIDList.shuffled().prefix(5)) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift index e3b9d5c4..a4928637 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift @@ -10,18 +10,18 @@ import UIKit import SnapKit final class SignUpMainView: UIView { - + // MARK: - Components let headerView: PPCancelHeaderView = PPCancelHeaderView() - + let progressIndicator: PPProgressIndicator = PPProgressIndicator(totalStep: 4, startPoint: 1) - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -29,7 +29,7 @@ final class SignUpMainView: UIView { // MARK: - SetUp private extension SignUpMainView { - + func setUpConstraints() { self.addSubview(headerView) headerView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift index f0ab226f..b7f26876 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SignUpCompleteController: BaseViewController, View { - + typealias Reactor = SignUpCompleteReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SignUpCompleteView() } @@ -43,7 +43,7 @@ private extension SignUpCompleteController { // MARK: - Methods extension SignUpCompleteController { func bind(reactor: Reactor) { - + mainView.bottomButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -51,7 +51,7 @@ extension SignUpCompleteController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -65,7 +65,7 @@ extension SignUpCompleteController { let categoryString = state.categoryTitles.enumerated() .map { "#\($0.element)" } .joined(separator: ", ") - + let attributedText = NSMutableAttributedString( string: categoryString, attributes: [ diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift index 7c5e14a1..9269d52a 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift @@ -6,27 +6,27 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpCompleteReactor: Reactor { - + // MARK: - Reactor enum Action { case completeButtonTapped(controller: BaseViewController) } - + enum Mutation { case moveToHomeScene(controller: BaseViewController) } - + struct State { var nickName: String var categoryTitles: [String] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() var isFirstResponderCase: Bool @@ -35,7 +35,7 @@ final class SignUpCompleteReactor: Reactor { self.initialState = State(nickName: nickName, categoryTitles: categoryTitles) self.isFirstResponderCase = isFirstResponderCase } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -43,7 +43,7 @@ final class SignUpCompleteReactor: Reactor { return Observable.just(.moveToHomeScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { switch mutation { case .moveToHomeScene(let controller): diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift index 73b5739c..b0ee7a2a 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift @@ -10,47 +10,43 @@ import UIKit import SnapKit final class SignUpCompleteView: UIView { - + // MARK: - Components private let imageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "image_signUp_complete") return view }() - + private let titleTopLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 20, text: "가입완료") - return label + return PPLabel(style: .bold, fontSize: 20, text: "가입완료") }() - + private let titleMiddleStackView: UIStackView = { - let view = UIStackView() - return view + return UIStackView() }() - + let nickNameLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) label.textColor = .blu500 return label }() - + private let nickNameSubLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 20, text: "님의") - return label + return PPLabel(style: .bold, fontSize: 20, text: "님의") }() - + private let titleBottomLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 20, text: "피드를 확인해보세요") - return label + return PPLabel(style: .bold, fontSize: 20, text: "피드를 확인해보세요") }() - + private let titleStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.alignment = .center return view }() - + let descriptionLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 15) label.textColor = .g600 @@ -59,18 +55,17 @@ final class SignUpCompleteView: UIView { label.textAlignment = .center return label }() - + let bottomButton: PPButton = { - let button = PPButton(style: .primary, text: "바로가기") - return button + return PPButton(style: .primary, text: "바로가기") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -78,7 +73,7 @@ final class SignUpCompleteView: UIView { // MARK: - SetUp private extension SignUpCompleteView { - + func setUpConstraints() { self.addSubview(imageView) imageView.snp.makeConstraints { make in @@ -86,27 +81,27 @@ private extension SignUpCompleteView { make.size.equalTo(80) make.top.equalToSuperview().inset(124) } - + titleMiddleStackView.addArrangedSubview(nickNameLabel) titleMiddleStackView.addArrangedSubview(nickNameSubLabel) - + titleStackView.addArrangedSubview(titleTopLabel) titleStackView.addArrangedSubview(titleMiddleStackView) titleStackView.addArrangedSubview(titleBottomLabel) - + self.addSubview(titleStackView) titleStackView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(imageView.snp.bottom).offset(32) } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleStackView.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(20) - + } - + self.addSubview(bottomButton) bottomButton.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift index 80cc81d2..1fd6c671 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SignUpStep1Controller: BaseViewController, View { - + typealias Reactor = SignUpStep1Reactor - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = SignUpStep1View() } @@ -44,13 +44,13 @@ private extension SignUpStep1Controller { // MARK: - Methods extension SignUpStep1Controller { func bind(reactor: Reactor) { - + // totalButton Tap 이벤트 mainView.totalButton.button.rx.tap .map { Reactor.Action.totalButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + // terms Tap 이벤트 mainView.terms1Button.button.rx.tap .map { Reactor.Action.termsButtonTapped(index: 1)} @@ -68,7 +68,7 @@ extension SignUpStep1Controller { .map { Reactor.Action.termsButtonTapped(index: 4)} .bind(to: reactor.action) .disposed(by: disposeBag) - + // terms Detail Button 이벤트 mainView.terms1Button.righticonButton.rx.tap .withUnretained(self) @@ -98,18 +98,18 @@ extension SignUpStep1Controller { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in - + // selectedIndex가 4일 경우 전체 선택 버튼 활성화 및 비활성화 if state.selectedIndex.count == 4 { owner.mainView.totalButton.isSelected.accept(true) } else { owner.mainView.totalButton.isSelected.accept(false) } - + // 현 selectedIndex에 따라 view 변경 let termsViews = [ owner.mainView.terms1Button, @@ -121,7 +121,7 @@ extension SignUpStep1Controller { let isSelected = state.selectedIndex.contains(index + 1) view.isSelected.accept(isSelected) } - + // selectedIndex가 1,2,3을 포함할 시 completeButton 활성화 if state.selectedIndex.contains(1) && state.selectedIndex.contains(2) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift index 467e416f..b4f838db 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift @@ -7,38 +7,38 @@ import Foundation import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpStep1Reactor: Reactor { - + // MARK: - Reactor enum Action { case totalButtonTapped case termsButtonTapped(index: Int) case termsRightButtonTapped(index: Int, controller: BaseViewController) } - + enum Mutation { case setTotalSelected case setSelectedIndex(index: Int) case moveToTermsDetailScene(index: Int, controller: BaseViewController) } - + struct State { var selectedIndex: [Int] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -50,7 +50,7 @@ final class SignUpStep1Reactor: Reactor { return Observable.just(.moveToTermsDetailScene(index: index, controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -75,7 +75,7 @@ final class SignUpStep1Reactor: Reactor { } return newState } - + func getTitle(index: Int) -> String { if index == 1 { return "[필수] 이용약관" @@ -85,7 +85,7 @@ final class SignUpStep1Reactor: Reactor { return "[선택] 위치정보 이용약관" } } - + func getContent(index: Int) -> String { if let path = Bundle.main.path(forResource: "Terms", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) as? [String: String], diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift index 0fead2c6..3a5e5a4b 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift @@ -7,33 +7,31 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class SignUpCheckBoxButton: UIView { - + // MARK: - Components private let checkImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_checkBox") return view }() - + private let buttonLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 15, text: "약관에 모두 동의할게요") - return label + return PPLabel(style: .bold, fontSize: 15, text: "약관에 모두 동의할게요") }() - + let button: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let disposeBag = DisposeBag() - + let isSelected: BehaviorRelay = .init(value: false) - + // MARK: - init init() { super.init(frame: .zero) @@ -43,7 +41,7 @@ final class SignUpCheckBoxButton: UIView { self.layer.cornerRadius = 4 self.clipsToBounds = true } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -51,7 +49,7 @@ final class SignUpCheckBoxButton: UIView { // MARK: - SetUp private extension SignUpCheckBoxButton { - + func setUpConstraints() { self.addSubview(checkImageView) checkImageView.snp.makeConstraints { make in @@ -59,20 +57,20 @@ private extension SignUpCheckBoxButton { make.centerY.equalToSuperview() make.leading.equalTo(20) } - + self.addSubview(buttonLabel) buttonLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(checkImageView.snp.trailing).offset(8) make.trailing.equalToSuperview().inset(20) } - + self.addSubview(button) button.snp.makeConstraints { make in make.edges.equalToSuperview() } } - + func bind() { button.rx.tap .withUnretained(self) @@ -80,7 +78,7 @@ private extension SignUpCheckBoxButton { owner.isSelected.accept(!owner.isSelected.value) } .disposed(by: disposeBag) - + isSelected .withUnretained(self) .subscribe { (owner, isSelected) in diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift index abd7ef42..5bbab051 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift @@ -10,42 +10,40 @@ import UIKit import SnapKit final class SignUpStep1View: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20, text: "서비스 이용을 위한\n약관을 확인해주세요") label.numberOfLines = 0 return label }() - + let totalButton: SignUpCheckBoxButton = { - let button = SignUpCheckBoxButton() - return button + return SignUpCheckBoxButton() }() - + let terms1Button: SignUpTermsView = SignUpTermsView(title: "[필수] 이용약관") let terms2Button: SignUpTermsView = SignUpTermsView(title: "[필수] 개인정보 수집 및 이용") let terms3Button: SignUpTermsView = SignUpTermsView(title: "[필수] 만 14세 이상") let terms4Button: SignUpTermsView = SignUpTermsView(title: "[선택] 위치정보 이용약관") - + let termsStackView: UIStackView = { let view = UIStackView() view.axis = .vertical view.spacing = 16 return view }() - + let completeButton: PPButton = { - let button = PPButton(style: .primary, text: "확인", disabledText: "확인") - return button + return PPButton(style: .primary, text: "확인", disabledText: "확인") }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -53,7 +51,7 @@ final class SignUpStep1View: UIView { // MARK: - SetUp private extension SignUpStep1View { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in @@ -61,14 +59,14 @@ private extension SignUpStep1View { make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(56) } - + self.addSubview(totalButton) totalButton.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(48) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(49) } - + self.addSubview(termsStackView) termsStackView.snp.makeConstraints { make in make.top.equalTo(totalButton.snp.bottom).offset(36) @@ -78,14 +76,14 @@ private extension SignUpStep1View { termsStackView.addArrangedSubview(terms2Button) termsStackView.addArrangedSubview(terms3Button) termsStackView.addArrangedSubview(terms4Button) - + self.addSubview(completeButton) completeButton.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) make.bottom.equalToSuperview() make.height.equalTo(52) } - + terms3Button.righticonButton.isHidden = true } } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift index 8230c466..cdfd3a56 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift @@ -7,40 +7,39 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class SignUpTermsView: UIView { - + // MARK: - Components private let imageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_check") return view }() - + private let titleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 14) label.text = "some" return label }() - + let righticonButton: UIButton = { let view = UIButton() view.setImage(UIImage(named: "icon_right_gray"), for: .normal) return view }() - + let button: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + let isSelected: BehaviorRelay = .init(value: false) - + let disposeBag = DisposeBag() - + // MARK: - init init(title: String?) { super.init(frame: .zero) @@ -48,7 +47,7 @@ final class SignUpTermsView: UIView { bind() titleLabel.text = title } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -56,7 +55,7 @@ final class SignUpTermsView: UIView { // MARK: - SetUp private extension SignUpTermsView { - + func setUpConstraints() { self.addSubview(imageView) imageView.snp.makeConstraints { make in @@ -64,27 +63,27 @@ private extension SignUpTermsView { make.centerY.equalToSuperview() make.leading.equalToSuperview() } - + self.addSubview(righticonButton) righticonButton.snp.makeConstraints { make in make.size.equalTo(22) make.top.bottom.trailing.equalToSuperview() } - + self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(imageView.snp.trailing).offset(8) make.trailing.equalTo(righticonButton.snp.leading) } - + self.addSubview(button) button.snp.makeConstraints { make in make.leading.top.bottom.equalToSuperview() make.trailing.equalTo(righticonButton.snp.leading) } } - + func bind() { button.rx.tap .withUnretained(self) @@ -92,7 +91,7 @@ private extension SignUpTermsView { owner.isSelected.accept(!owner.isSelected.value) } .disposed(by: disposeBag) - + isSelected .withUnretained(self) .subscribe { (owner, isSelected) in diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift index 1828b8a6..ce9e6152 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift @@ -14,7 +14,7 @@ enum IntroState { case validateActive case longLength case longLengthActive - + var borderColor: UIColor? { switch self { case .empty, .validate: @@ -25,7 +25,7 @@ enum IntroState { return .re500 } } - + var description: String? { switch self { case .empty, .emptyActive, .validate, .validateActive: @@ -34,7 +34,7 @@ enum IntroState { return "최대 30글자까지 입력해주세요" } } - + var textColor: UIColor? { switch self { case .empty, .emptyActive, .validate, .validateActive: @@ -43,7 +43,7 @@ enum IntroState { return .re500 } } - + var textFieldTextColor: UIColor? { switch self { case .longLength, .longLengthActive: @@ -52,7 +52,7 @@ enum IntroState { return .g1000 } } - + var placeHolderIsHidden: Bool { switch self { case .empty, .emptyActive: diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift index 964d5894..7693833f 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift @@ -26,10 +26,10 @@ enum NickNameState { case shortLengthActive case longLength case longLengthActive - + var borderColor: UIColor? { switch self { - case .empty, .duplicated, .validate, .check , .myNickName: + case .empty, .duplicated, .validate, .check, .myNickName: return .g200 case .emptyActive, .duplicatedActive, .validateActive, .checkActive, .myNickNameActive: return .g1000 @@ -37,7 +37,7 @@ enum NickNameState { return .re500 } } - + var description: String? { switch self { case .empty, .emptyActive: @@ -62,18 +62,18 @@ enum NickNameState { return nil } } - + var textColor: UIColor? { switch self { case .empty, .emptyActive, .check, .checkActive: return .g500 case .length, .lengthActive, .korAndEng, .korAndEngActive, .duplicated, .duplicatedActive, .shortLength, .shortLengthActive, .longLength, .longLengthActive: return .re500 - case .validate, .validateActive , .myNickName ,.myNickNameActive: + case .validate, .validateActive, .myNickName, .myNickNameActive: return .blu500 } } - + var textFieldTextColor: UIColor? { switch self { case .length, .lengthActive, .korAndEng, .korAndEngActive, .duplicated, .duplicatedActive, .shortLength, .shortLengthActive, .longLength, .longLengthActive: @@ -82,25 +82,25 @@ enum NickNameState { return .g1000 } } - + var isHiddenClearButton: Bool { switch self { - case .lengthActive , .korAndEngActive, .duplicatedActive, .validateActive, .checkActive, .myNickNameActive, .shortLengthActive, .longLengthActive: + case .lengthActive, .korAndEngActive, .duplicatedActive, .validateActive, .checkActive, .myNickNameActive, .shortLengthActive, .longLengthActive: return false default: return true } } - + var isHiddenCheckButton: Bool { switch self { - case .length , .korAndEng, .duplicated, .validate, .check, .shortLength, .longLength ,.myNickName: + case .length, .korAndEng, .duplicated, .validate, .check, .shortLength, .longLength, .myNickName: return false default: return true } } - + var isShakeAnimation: Bool { switch self { case .lengthActive, .korAndEngActive, .duplicatedActive: @@ -109,7 +109,7 @@ enum NickNameState { return false } } - + var duplicatedCheckButtonIsEnabled: Bool { switch self { case .check, .checkActive: diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift index 3b588360..42293868 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxGesture +import RxSwift +import SnapKit final class SignUpStep2Controller: BaseViewController, View { - + typealias Reactor = SignUpStep2Reactor - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = SignUpStep2View() } @@ -45,29 +45,29 @@ private extension SignUpStep2Controller { // MARK: - Methods extension SignUpStep2Controller { func bind(reactor: Reactor) { - + mainView.rx.tapGesture() .withUnretained(self) .subscribe { (owner, _) in owner.view.endEditing(true) } .disposed(by: disposeBag) - + mainView.textField.rx.text .map { Reactor.Action.inputNickName(text: $0)} .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.textField.rx.controlEvent(.editingDidBegin) .map { Reactor.Action.beginNickNameInput } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.textField.rx.controlEvent(.editingDidEnd) .map { Reactor.Action.endNickNameInput } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.clearButton.rx.tap .withUnretained(self) .map({ (owner, _) in @@ -76,16 +76,16 @@ extension SignUpStep2Controller { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.duplicatedCheckButton.rx.tap .map { Reactor.Action.duplicatedButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in - + // duplicatedButton Active set switch state.nickNameState { case .check, .checkActive: @@ -98,11 +98,11 @@ extension SignUpStep2Controller { owner.mainView.textFieldTrailingView.layer.borderColor = state.nickNameState.borderColor?.cgColor owner.mainView.textDescriptionLabel.text = state.nickNameState.description owner.mainView.textDescriptionLabel.textColor = state.nickNameState.textColor - + // clearButton, Duplicated Button set owner.mainView.duplicatedCheckButton.isHidden = state.nickNameState.isHiddenCheckButton owner.mainView.clearButton.isHidden = state.nickNameState.isHiddenClearButton - + // count Label set if let nickName = state.nickName { owner.mainView.textCountLabel.text = "\(nickName.count) / 10자" @@ -114,8 +114,7 @@ extension SignUpStep2Controller { default: owner.mainView.textCountLabel.textColor = .g500 } - - + // completeButton isActive set switch state.nickNameState { case .validate, .validateActive: diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift index c46fd041..13b7df79 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift @@ -8,11 +8,11 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpStep2Reactor: Reactor { - + // MARK: - Reactor enum Action { case inputNickName(text: String?) @@ -21,32 +21,32 @@ final class SignUpStep2Reactor: Reactor { case clearButtonTapped case duplicatedButtonTapped } - + enum Mutation { case setNickNameState(text: String?) case setActiveState(isActive: Bool) case setDuplicatedSet(isDuplicated: Bool) case resetNickName } - + struct State { var nickNameState: NickNameState = .empty var isActiveInput: Bool = false var nickName: String? = nil } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private var nickName: String? - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -66,7 +66,7 @@ final class SignUpStep2Reactor: Reactor { return Observable.just(.resetNickName) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -78,7 +78,7 @@ final class SignUpStep2Reactor: Reactor { newState.isActiveInput = isActive newState.nickNameState = checkNickNameState(text: newState.nickName, isActive: newState.isActiveInput) case .setDuplicatedSet(let isDuplicated): - newState.nickNameState = isDuplicated + newState.nickNameState = isDuplicated ? newState.isActiveInput ? .duplicatedActive : .duplicated : newState.isActiveInput ? .validateActive : .validate case .resetNickName: @@ -87,24 +87,22 @@ final class SignUpStep2Reactor: Reactor { } return newState } - + func checkNickNameState(text: String?, isActive: Bool) -> NickNameState { guard let text = text else { return isActive ? .emptyActive : .empty } // textEmpty Check if text.isEmpty { return isActive ? .emptyActive : .empty } - - + // textLength Check if text.count < 2 { return isActive ? .shortLengthActive : .shortLength } if text.count > 10 { return isActive ? .longLengthActive : .longLength } - + // kor and end Check let pattern = "^[가-힣a-zA-Z\\s]+$" // 허용하는 문자만 검사 let regex = try! NSRegularExpression(pattern: pattern) let range = NSRange(location: 0, length: text.utf16.count) if regex.firstMatch(in: text, options: [], range: range) == nil { return isActive ? .korAndEngActive : .korAndEng } - return isActive ? .checkActive : .check } } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift index 1cb8caed..b40c3bc2 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift @@ -10,26 +10,26 @@ import UIKit import SnapKit final class SignUpStep2View: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20, text: "팝풀에서 사용할\n별명을 설정해볼까요?") label.numberOfLines = 0 return label }() - + private let descriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 15, text: "이후 이 별명으로 팝풀에서 활동할 예정이에요.") label.textColor = .g600 return label }() - + let completeButton: PPButton = { let button = PPButton(style: .primary, text: "확인", disabledText: "다음") button.isEnabled = false return button }() - + let textFieldTrailingView: UIStackView = { let view = UIStackView() view.layoutMargins = .init(top: 0, left: 20, bottom: 0, right: 20) @@ -41,33 +41,33 @@ final class SignUpStep2View: UIView { view.layer.borderWidth = 1 return view }() - + let textField: UITextField = { let textField = UITextField() textField.placeholder = "별명을 입력해주세요" textField.font = .KorFont(style: .medium, size: 14) return textField }() - + let clearButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_clearButton"), for: .normal) return button }() - + let textDescriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.text = "temptemp" return label }() - + let textCountLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.text = "0/10자" label.textColor = .g500 return label }() - + let duplicatedCheckButton: UIButton = { let button = UIButton() let title = "중복체크" @@ -93,13 +93,13 @@ final class SignUpStep2View: UIView { button.setAttributedTitle(disabledAttributedTitle, for: .disabled) return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -107,7 +107,7 @@ final class SignUpStep2View: UIView { // MARK: - SetUp private extension SignUpStep2View { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in @@ -115,13 +115,13 @@ private extension SignUpStep2View { make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(56) } - + self.addSubview(descriptionLabel) descriptionLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(16) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(textFieldTrailingView) textFieldTrailingView.snp.makeConstraints { make in make.top.equalTo(descriptionLabel.snp.bottom).offset(48) @@ -134,26 +134,26 @@ private extension SignUpStep2View { clearButton.snp.makeConstraints { make in make.size.equalTo(16) } - + self.addSubview(textDescriptionLabel) textDescriptionLabel.snp.makeConstraints { make in make.top.equalTo(textFieldTrailingView.snp.bottom).offset(6) make.leading.equalToSuperview().inset(24) } - + self.addSubview(textCountLabel) textCountLabel.snp.makeConstraints { make in make.centerY.equalTo(textDescriptionLabel) make.trailing.equalToSuperview().inset(24) } - + self.addSubview(completeButton) completeButton.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) make.bottom.equalToSuperview() make.height.equalTo(52) } - + textField.snp.makeConstraints { make in make.height.equalTo(52) } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift index 0260f4d3..4b85d7c3 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift @@ -7,23 +7,23 @@ import UIKit -import SnapKit -import RxCocoa -import RxSwift import ReactorKit +import RxCocoa import RxGesture +import RxSwift +import SnapKit final class SignUpStep3Controller: BaseViewController, View { - + typealias Reactor = SignUpStep3Reactor - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = SignUpStep3View() - + private var sections: [any Sectionable] = [] - + private let selectedTag: PublishSubject = .init() } @@ -63,12 +63,12 @@ extension SignUpStep3Controller { .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - + selectedTag .map { Reactor.Action.selectedTag(indexPath: $0) } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -85,19 +85,18 @@ extension SignUpStep3Controller: UICollectionViewDelegate, UICollectionViewDataS func numberOfSections(in collectionView: UICollectionView) -> Int { return sections.count } - + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return sections[section].dataCount } - + func collectionView( _ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - return cell + return sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) } - + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { selectedTag.onNext(indexPath) } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 50aff05f..30fbc30b 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -8,35 +8,35 @@ import UIKit import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpStep3Reactor: Reactor { - + // MARK: - Reactor enum Action { case viewWillAppear case selectedTag(indexPath: IndexPath) } - + enum Mutation { case loadView } - + struct State { var sections: [any Sectionable] = [] var selectedCategory: [Int64] = [] var selectedCategoryTitle: [String] = [] var categoryIDList: [Int64] = [] } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private var cetegoryIDList: [Int64] = [] - + lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -50,14 +50,14 @@ final class SignUpStep3Reactor: Reactor { return getSection()[section].getSection(section: section, env: env) } }() - + private var categorySection: TagSection = TagSection(inputDataList: []) - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -79,14 +79,14 @@ final class SignUpStep3Reactor: Reactor { } else { ToastMaker.createToast(message: "최대 5개까지 선택할 수 있어요") } - + } else { categorySection.inputDataList[indexPath.row].isSelected.toggle() } return Observable.just(.loadView) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { @@ -98,7 +98,7 @@ final class SignUpStep3Reactor: Reactor { } return newState } - + func getSection() -> [any Sectionable] { return [ categorySection diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift index 5de5d4b5..04c18968 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class SignUpStep3View: UIView { - + // MARK: - Components let nickNameLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) @@ -18,64 +18,62 @@ final class SignUpStep3View: UIView { label.text = "하이" return label }() - + private let titleTopLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) label.text = "님에 대해" return label }() - + private let titleBottomLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) label.text = "조금 더 알려주시겠어요?" return label }() - + private let subTitleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 16) label.text = "관심이 있는 카테고리를 선택해주세요" return label }() - + private let subTitleDescriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.text = "최대 5개까지 선택할 수 있어요." return label }() - + let categoryCollectionView: UICollectionView = { let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) view.isScrollEnabled = false return view }() - + let skipButton: PPButton = { - let button = PPButton(style: .secondary, text: "건너뛰기") - return button + return PPButton(style: .secondary, text: "건너뛰기") }() - + let completeButton: PPButton = { - let button = PPButton(style: .primary, text: "다음", disabledText: "다음") - return button + return PPButton(style: .primary, text: "다음", disabledText: "다음") }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func setNickName(nickName: String?) { nickNameLabel.text = nickName } @@ -83,7 +81,7 @@ final class SignUpStep3View: UIView { // MARK: - SetUp private extension SignUpStep3View { - + func setUpConstraints() { self.addSubview(nickNameLabel) nickNameLabel.snp.makeConstraints { make in @@ -91,35 +89,35 @@ private extension SignUpStep3View { make.leading.equalToSuperview().inset(20) make.height.equalTo(28) } - + self.addSubview(titleTopLabel) titleTopLabel.snp.makeConstraints { make in make.top.equalTo(nickNameLabel) make.leading.equalTo(nickNameLabel.snp.trailing) make.height.equalTo(28) } - + self.addSubview(titleBottomLabel) titleBottomLabel.snp.makeConstraints { make in make.top.equalTo(titleTopLabel.snp.bottom) make.leading.equalToSuperview().inset(20) make.height.equalTo(28) } - + self.addSubview(subTitleLabel) subTitleLabel.snp.makeConstraints { make in make.top.equalTo(titleBottomLabel.snp.bottom).offset(48) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(22) } - + self.addSubview(subTitleDescriptionLabel) subTitleDescriptionLabel.snp.makeConstraints { make in make.top.equalTo(subTitleLabel.snp.bottom) make.leading.equalToSuperview().inset(20) make.height.equalTo(18) } - + self.addSubview(buttonStackView) buttonStackView.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) @@ -127,7 +125,7 @@ private extension SignUpStep3View { } buttonStackView.addArrangedSubview(skipButton) buttonStackView.addArrangedSubview(completeButton) - + self.addSubview(categoryCollectionView) categoryCollectionView.snp.makeConstraints { make in make.top.equalTo(subTitleDescriptionLabel.snp.bottom).offset(36) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift index 58f00723..2f508831 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift @@ -10,17 +10,17 @@ import UIKit import RxSwift struct TagSection: Sectionable { - + var currentPage: PublishSubject = .init() - + typealias CellType = TagSectionCell - + var inputDataList: [CellType.Input] - + var supplementaryItems: [any SectionSupplementaryItemable]? - + var decorationItems: [any SectionDecorationItemable]? - + func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), @@ -38,7 +38,7 @@ struct TagSection: Sectionable { let section = NSCollectionLayoutSection(group: group) section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 16 - + return section } } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift index 6712e0d5..ed6bb5a6 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift @@ -7,25 +7,24 @@ import UIKit -import SnapKit import RxSwift +import SnapKit final class TagSectionCell: UICollectionViewCell { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 13) - return label + return PPLabel(style: .medium, fontSize: 13) }() - + let disposeBag = DisposeBag() // MARK: - init - + override init(frame: CGRect) { super.init(frame: frame) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError() } @@ -37,7 +36,7 @@ private extension TagSectionCell { contentView.clipsToBounds = true contentView.layer.cornerRadius = 18 contentView.layer.borderColor = UIColor.g200.cgColor - + contentView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(14).priority(.high) @@ -52,7 +51,7 @@ extension TagSectionCell: Inputable { var isSelected: Bool var id: Int64? } - + func injection(with input: Input) { titleLabel.text = input.title if input.isSelected { diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift index b3bbf5b0..2a2d3d8b 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift @@ -7,19 +7,19 @@ import UIKit -import SnapKit +import PanModal +import ReactorKit import RxCocoa import RxSwift -import ReactorKit -import PanModal +import SnapKit final class AgeSelectedController: BaseViewController, View { - + typealias Reactor = AgeSelectedReactor - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = AgeSelectedView() } @@ -51,7 +51,7 @@ extension AgeSelectedController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.completeButton.rx.tap .withUnretained(self) .map { (owner, _) in @@ -60,7 +60,7 @@ extension AgeSelectedController { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift index 8e1e2aef..08a1c326 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift @@ -6,36 +6,36 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class AgeSelectedReactor: Reactor { - + // MARK: - Reactor enum Action { case cancelButtonTapped(controller: BaseViewController) case completeButtonTapped(selectedAge: Int, controller: BaseViewController) } - + enum Mutation { case setSelectedAge(selectedAge: Int, controller: BaseViewController) case moveToRecentScene(controller: BaseViewController) } - + struct State { var selectedAge: Int? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init(age: Int?) { self.initialState = State(selectedAge: age) } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -45,7 +45,7 @@ final class AgeSelectedReactor: Reactor { return Observable.just(.setSelectedAge(selectedAge: selectedAge, controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift index 6732f881..6322c58c 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift @@ -10,29 +10,25 @@ import UIKit import SnapKit final class AgeSelectedView: UIView { - + // MARK: - Components private let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "나이를 선택해주세요") - return label + return PPLabel(style: .bold, fontSize: 18, text: "나이를 선택해주세요") }() - + let picker: PPPicker = { let ageRange = (0...100).map { "\($0)세"} - let picker = PPPicker(components: ageRange) - return picker + return PPPicker(components: ageRange) }() - + let cancelButton: PPButton = { - let button = PPButton(style: .secondary, text: "취소") - return button + return PPButton(style: .secondary, text: "취소") }() - + let completeButton: PPButton = { - let button = PPButton(style: .primary, text: "확인", disabledText: "확인") - return button + return PPButton(style: .primary, text: "확인", disabledText: "확인") }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually @@ -44,7 +40,7 @@ final class AgeSelectedView: UIView { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -52,21 +48,21 @@ final class AgeSelectedView: UIView { // MARK: - SetUp private extension AgeSelectedView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.top.equalToSuperview().inset(24) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(picker) picker.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(208) } - + self.addSubview(buttonStackView) buttonStackView.snp.makeConstraints { make in make.top.equalTo(picker.snp.bottom).offset(24) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift index 17d64901..772bf195 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift @@ -7,18 +7,18 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SignUpStep4Controller: BaseViewController, View { - + typealias Reactor = SignUpStep4Reactor - + // MARK: - Properties var disposeBag = DisposeBag() - + var mainView = SignUpStep4View() } @@ -50,7 +50,7 @@ extension SignUpStep4Controller { }) .bind(to: reactor.action) .disposed(by: disposeBag) - + mainView.ageSelectedButton.button.rx.tap .withUnretained(self) .map { (owner, _) in @@ -58,7 +58,7 @@ extension SignUpStep4Controller { } .bind(to: reactor.action) .disposed(by: disposeBag) - + reactor.state .withUnretained(self) .subscribe { (owner, state) in diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift index 50d87d2f..02d72a7f 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift @@ -6,39 +6,39 @@ // import ReactorKit -import RxSwift import RxCocoa +import RxSwift final class SignUpStep4Reactor: Reactor { - + // MARK: - Reactor enum Action { case selectedGender(index: Int) case ageSelectedButtonTapped(controller: BaseViewController) case ageSelected(age: Int?) } - + enum Mutation { case setGender(index: Int) case setAge(age: Int?) case moveToAgeSelectedScene(controller: BaseViewController) } - + struct State { var selectedGenderIndex: Int = 2 var age: Int? } - + // MARK: - properties - + var initialState: State var disposeBag = DisposeBag() - + // MARK: - init init() { self.initialState = State() } - + // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { @@ -50,7 +50,7 @@ final class SignUpStep4Reactor: Reactor { return Observable.just(.moveToAgeSelectedScene(controller: controller)) } } - + func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift index 55ec1d8a..43774bdf 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift @@ -10,38 +10,38 @@ import UIKit import SnapKit final class AgeSelectedButton: UIView { - + // MARK: - Components private let contentStackView: UIStackView = { let view = UIStackView() view.alignment = .center return view }() - + private let defaultLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "나이를 선택해주세요") label.textColor = .g400 return label }() - + private let rightImageView: UIImageView = { let view = UIImageView() view.image = UIImage(named: "icon_dropdown") return view }() - + private let ageTitleLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 11, text: "나이") label.textColor = .g400 return label }() - + private let ageLabel: PPLabel = { let label = PPLabel(style: .medium, fontSize: 14, text: "") label.textColor = .g1000 return label }() - + private let verticalStackView: UIStackView = { let view = UIStackView() view.axis = .vertical @@ -50,18 +50,17 @@ final class AgeSelectedButton: UIView { view.spacing = 4 return view }() - + let button: UIButton = { - let button = UIButton() - return button + return UIButton() }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -69,12 +68,12 @@ final class AgeSelectedButton: UIView { // MARK: - SetUp private extension AgeSelectedButton { - + func setUpConstraints() { self.layer.borderWidth = 1 self.layer.cornerRadius = 4 self.layer.borderColor = UIColor.g200.cgColor - + self.addSubview(contentStackView) contentStackView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(20) @@ -85,11 +84,11 @@ private extension AgeSelectedButton { } verticalStackView.addArrangedSubview(ageTitleLabel) verticalStackView.addArrangedSubview(ageLabel) - + contentStackView.addArrangedSubview(defaultLabel) contentStackView.addArrangedSubview(verticalStackView) contentStackView.addArrangedSubview(rightImageView) - + self.addSubview(button) button.snp.makeConstraints { make in make.edges.equalToSuperview() @@ -101,7 +100,7 @@ extension AgeSelectedButton: Inputable { struct Input { var age: Int? } - + func injection(with input: Input) { if let age = input.age { verticalStackView.isHidden = false diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift index c9e19f9c..52453a36 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift @@ -10,7 +10,7 @@ import UIKit import SnapKit final class SignUpStep4View: UIView { - + // MARK: - Components let nickNameLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) @@ -18,84 +18,80 @@ final class SignUpStep4View: UIView { label.text = "하이" return label }() - + private let titleTopLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) label.text = "님에 대해" return label }() - + private let titleBottomLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 20) label.text = "조금 더 알려주시겠어요?" return label }() - + private let subTitleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 16) label.text = "해당되시는 성별 / 나이대를 알려주세요" return label }() - + private let subTitleDescriptionLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 12) label.text = "가장 잘 맞는 팝업스토어를 소개해드릴게요." return label }() - + private let genderTitleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.text = "성별" return label }() - + let genderSegmentControl: PPSegmentedControl = { - let control = PPSegmentedControl( + return PPSegmentedControl( type: .base, segments: ["남성", "여성", "선택안함"], selectedSegmentIndex: 2 ) - return control }() - + private let ageTitleLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 13) label.text = "나이" return label }() - + let ageSelectedButton: AgeSelectedButton = { - let button = AgeSelectedButton() - return button + return AgeSelectedButton() }() - + let skipButton: PPButton = { - let button = PPButton(style: .secondary, text: "건너뛰기") - return button + return PPButton(style: .secondary, text: "건너뛰기") }() - + let completeButton: PPButton = { - let button = PPButton(style: .primary, text: "확인", disabledText: "확인") - return button + return PPButton(style: .primary, text: "확인", disabledText: "확인") }() - + private let buttonStackView: UIStackView = { let view = UIStackView() view.distribution = .fillEqually view.spacing = 12 return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + func setNickName(nickName: String?) { nickNameLabel.text = nickName } @@ -103,7 +99,7 @@ final class SignUpStep4View: UIView { // MARK: - SetUp private extension SignUpStep4View { - + func setUpConstraints() { self.addSubview(nickNameLabel) nickNameLabel.snp.makeConstraints { make in @@ -111,62 +107,62 @@ private extension SignUpStep4View { make.leading.equalToSuperview().inset(20) make.height.equalTo(28) } - + self.addSubview(titleTopLabel) titleTopLabel.snp.makeConstraints { make in make.top.equalTo(nickNameLabel) make.leading.equalTo(nickNameLabel.snp.trailing) make.height.equalTo(28) } - + self.addSubview(titleBottomLabel) titleBottomLabel.snp.makeConstraints { make in make.top.equalTo(titleTopLabel.snp.bottom) make.leading.equalToSuperview().inset(20) make.height.equalTo(28) } - + self.addSubview(subTitleLabel) subTitleLabel.snp.makeConstraints { make in make.top.equalTo(titleBottomLabel.snp.bottom).offset(48) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(22) } - + self.addSubview(subTitleDescriptionLabel) subTitleDescriptionLabel.snp.makeConstraints { make in make.top.equalTo(subTitleLabel.snp.bottom) make.leading.equalToSuperview().inset(20) make.height.equalTo(18) } - + self.addSubview(genderTitleLabel) genderTitleLabel.snp.makeConstraints { make in make.top.equalTo(subTitleDescriptionLabel.snp.bottom).offset(36) make.leading.equalToSuperview().inset(20) make.height.equalTo(20) } - + self.addSubview(genderSegmentControl) genderSegmentControl.snp.makeConstraints { make in make.top.equalTo(genderTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) } - + self.addSubview(ageTitleLabel) ageTitleLabel.snp.makeConstraints { make in make.top.equalTo(genderSegmentControl.snp.bottom).offset(36) make.leading.equalToSuperview().inset(20) make.height.equalTo(20) } - + self.addSubview(ageSelectedButton) ageSelectedButton.snp.makeConstraints { make in make.top.equalTo(ageTitleLabel.snp.bottom).offset(8) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(72) } - + self.addSubview(buttonStackView) buttonStackView.snp.makeConstraints { make in make.leading.trailing.bottom.equalToSuperview().inset(20) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift index 987ddee1..fdfa8afe 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift @@ -7,17 +7,17 @@ import UIKit -import SnapKit import RxCocoa import RxSwift +import SnapKit final class TermsDetailController: BaseViewController { - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = TermsDetailView() - + init(title: String?, content: String?) { super.init() let paragraphStyle = NSMutableParagraphStyle() @@ -30,9 +30,9 @@ final class TermsDetailController: BaseViewController { mainView.contentTextView.attributedText = NSAttributedString(string: content ?? "", attributes: attributes) mainView.titleLabel.text = title - + } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -55,7 +55,7 @@ private extension TermsDetailController { make.edges.equalTo(view.safeAreaLayoutGuide) } } - + func bind() { mainView.xmarkButton.rx.tap .withUnretained(self) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift index 9a12dc81..6c5b7d7c 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift @@ -10,13 +10,12 @@ import UIKit import SnapKit final class TermsDetailView: UIView { - + // MARK: - Components let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 15) - return label + return PPLabel(style: .bold, fontSize: 15) }() - + let contentTextView: UITextView = { let view = UITextView() view.isSelectable = false @@ -25,19 +24,18 @@ final class TermsDetailView: UIView { return view }() - let xmarkButton: UIButton = { let button = UIButton() button.setImage(UIImage(named: "icon_xmark"), for: .normal) return button }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -45,7 +43,7 @@ final class TermsDetailView: UIView { // MARK: - SetUp private extension TermsDetailView { - + func setUpConstraints() { self.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in @@ -53,14 +51,14 @@ private extension TermsDetailView { make.top.equalToSuperview().inset(25) make.height.equalTo(21) } - + self.addSubview(xmarkButton) xmarkButton.snp.makeConstraints { make in make.size.equalTo(24) make.trailing.equalToSuperview().inset(16) make.centerY.equalTo(titleLabel) } - + self.addSubview(contentTextView) contentTextView.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(43) diff --git a/Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift b/Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift index f0eb9ac8..c4fb9af4 100644 --- a/Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift @@ -7,20 +7,20 @@ import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class SplashController: BaseViewController { - + // MARK: - Properties var disposeBag = DisposeBag() - + private var mainView = SplashView() private let authAPIUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) private let keyChainService = KeyChainService() - + private var rootViewController: UIViewController? } @@ -43,7 +43,7 @@ private extension SplashController { make.edges.equalTo(view.safeAreaLayoutGuide) } } - + func playAnimation() { mainView.animationView.play { [weak self] _ in DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { @@ -51,15 +51,15 @@ private extension SplashController { } } } - + func setRootview() { authAPIUseCase.postTokenReissue() .withUnretained(self) .subscribe(onNext: { (owner, response) in let newAccessToken = response.accessToken ?? "" let newRefreshToken = response.refreshToken ?? "" - let _ = owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) - let _ = owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) + _ = owner.keyChainService.saveToken(type: .accessToken, value: newAccessToken) + _ = owner.keyChainService.saveToken(type: .refreshToken, value: newRefreshToken) let navigationController = WaveTabBarController() owner.rootViewController = navigationController }, onError: { [weak self] _ in @@ -71,7 +71,7 @@ private extension SplashController { }) .disposed(by: disposeBag) } - + func changeRootView() { view.window?.rootViewController = rootViewController view.window?.makeKeyAndVisible() diff --git a/Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift b/Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift index 08b21aee..db657eb4 100644 --- a/Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift +++ b/Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift @@ -7,25 +7,25 @@ import UIKit -import SnapKit import Lottie +import SnapKit final class SplashView: UIView { - + // MARK: - Components - + let animationView: LottieAnimationView = { let view = LottieAnimationView(name: "PP_splash") view.contentMode = .scaleAspectFit return view }() - + // MARK: - init init() { super.init(frame: .zero) setUpConstraints() } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -33,7 +33,7 @@ final class SplashView: UIView { // MARK: - SetUp private extension SplashView { - + func setUpConstraints() { addSubview(animationView) animationView.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift index 35d94f0c..14c76db0 100644 --- a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift @@ -8,9 +8,9 @@ import UIKit class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { - + private let waveLayer = CAShapeLayer() - + private let dotView: UIView = { let view = UIView() view.layer.borderWidth = 0.6 @@ -18,7 +18,7 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { view.backgroundColor = .blu500 return view }() - + override func viewDidLoad() { super.viewDidLoad() addSomeTabItems() @@ -26,36 +26,36 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { setupWaveTabBar() delegate = self } - + private func setupWaveTabBar() { // TabBar의 배경 투명 설정 tabBar.backgroundImage = UIImage() tabBar.shadowImage = UIImage() tabBar.isTranslucent = true - + // Wave Layer 설정 waveLayer.fillColor = UIColor.white.cgColor tabBar.layer.insertSublayer(waveLayer, at: 0) - + // Dot 설정 dotView.frame.size = CGSize(width: 12, height: 12) dotView.layer.cornerRadius = 6 tabBar.addSubview(dotView) } - + override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() updateWavePath() updateDotPosition(animated: false) updateItem() } - + private func updateItem() { let tabBarItemViews = tabBar.subviews.filter { $0.isUserInteractionEnabled } - + if selectedIndex < tabBarItemViews.count { let selectedView = tabBarItemViews[selectedIndex] - + // 애니메이션 적용 UIView.animate( withDuration: 0.3, @@ -71,7 +71,7 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { ) } } - + private func updateWavePath(animated: Bool = false) { guard let items = tabBar.items else { return } let tabWidth = tabBar.bounds.width / CGFloat(items.count) @@ -139,12 +139,12 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { guard let items = tabBar.items else { return } let tabWidth = tabBar.bounds.width / CGFloat(items.count) let selectedTabX = CGFloat(selectedIndex) * tabWidth - + let targetCenter = CGPoint( x: selectedTabX + tabWidth / 2, y: -12 ) - + if animated { UIView.animate(withDuration: 1, delay: 0, @@ -158,20 +158,20 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { dotView.center = targetCenter } } - + // 탭 선택 시 애니메이션 적용 func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { updateWavePath(animated: true) updateDotPosition(animated: true) updateItem() } - + func setUp() { self.selectedIndex = 1 self.tabBar.barTintColor = .g200 self.tabBar.tintColor = .blu500 } - + func resizeImage(image: UIImage?, targetSize: CGSize) -> UIImage? { guard let image = image else { return nil } let size = image.size @@ -180,32 +180,32 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let heightRatio = targetSize.height / size.height let scaleFactor = min(widthRatio, heightRatio) - + let scaledImageSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor) UIGraphicsBeginImageContextWithOptions(scaledImageSize, false, 0.0) image.draw(in: CGRect(origin: .zero, size: scaledImageSize)) - + let resizedImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() return resizedImage } - + func addSomeTabItems() { let provider = ProviderImpl() let mapController = MapViewController() let mapUseCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) - let directionRepository = DefaultMapDirectionRepository(provider: provider) + let directionRepository = DefaultMapDirectionRepository(provider: provider) mapController.reactor = MapReactor(useCase: mapUseCase, directionRepository: directionRepository) let homeController = HomeController() homeController.reactor = HomeReactor() - + let myPageController = MyPageController() myPageController.reactor = MyPageReactor() - + let iconSize = CGSize(width: 32, height: 32) // 탭바 아이템 생성 mapController.tabBarItem = UITabBarItem( @@ -223,17 +223,17 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { image: resizeImage(image: UIImage(named: "icon_tabbar_menu"), targetSize: iconSize), selectedImage: resizeImage(image: UIImage(named: "icon_tabbar_menu"), targetSize: iconSize) ) - + // 네비게이션 컨트롤러 설정 let map = UINavigationController(rootViewController: mapController) let home = UINavigationController(rootViewController: homeController) let myPage = UINavigationController(rootViewController: myPageController) - + viewControllers = [map, home, myPage] - + let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.2 // 기본 값보다 높은 라인 간격을 설정 - + // 폰트 설정 let appearance = UITabBarAppearance() appearance.stackedLayoutAppearance.normal.titleTextAttributes = [ @@ -244,12 +244,12 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { .font: UIFont.KorFont(style: .bold, size: 11)!, .paragraphStyle: paragraphStyle ] - + let verticalOffset: CGFloat = 4 // 원하는 간격 (양수: 아래로 이동, 음수: 위로 이동) appearance.stackedLayoutAppearance.normal.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: verticalOffset) appearance.stackedLayoutAppearance.selected.titlePositionAdjustment = UIOffset(horizontal: 0, vertical: verticalOffset) - + tabBar.standardAppearance = appearance } - + } diff --git a/Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift index 330cad1e..639a66e5 100644 --- a/Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift @@ -7,8 +7,8 @@ import UIKit -import Tabman import Pageboy +import Tabman class BaseTabmanController: TabmanViewController { init() { @@ -20,17 +20,17 @@ class BaseTabmanController: TabmanViewController { line: #line ) } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .systemBackground self.navigationController?.navigationBar.isHidden = true } - + deinit { Logger.log( message: "\(self) deinit", diff --git a/Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift index 8c2b573f..2184b672 100644 --- a/Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift @@ -7,14 +7,14 @@ import UIKit -import RxSwift import RxCocoa +import RxSwift class BaseViewController: UIViewController { - + var systemStatusBarIsDark: BehaviorRelay = .init(value: true) var systemStatusBarDisposeBag = DisposeBag() - + init() { super.init(nibName: nil, bundle: nil) Logger.log( @@ -24,23 +24,23 @@ class BaseViewController: UIViewController { line: #line ) } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .white self.navigationController?.navigationBar.isHidden = true systemStatusBarIsDarkBind() } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) systemStatusBarIsDark.accept(systemStatusBarIsDark.value) } - + deinit { Logger.log( message: "\(self) deinit", @@ -49,7 +49,7 @@ class BaseViewController: UIViewController { line: #line ) } - + func systemStatusBarIsDarkBind() { systemStatusBarIsDark .withUnretained(self) diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift index 6a2abf42..5b37a557 100644 --- a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift +++ b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift @@ -11,13 +11,13 @@ import UIKit /// 제네릭 타입 `View`는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. struct SectionDecorationItem: SectionDecorationItemable { typealias ReusableView = View - + /// 데코레이션 뷰의 종류를 나타내는 문자열입니다. var elementKind: String - + /// 데코레이션 뷰의 인스턴스입니다. var reusableView: ReusableView - + /// 데코레이션 뷰에 주입될 데이터입니다. var viewInput: ReusableView.Input } @@ -25,16 +25,16 @@ struct SectionDecorationItem: Sectio /// `SectionDecorationItemable` 프로토콜은 데코레이션 뷰에 대한 인터페이스를 정의합니다. /// 이 프로토콜을 준수하는 타입은 데코레이션 뷰를 설정하고 반환하는 기능을 가져야 합니다. protocol SectionDecorationItemable { - + /// 데코레이션 뷰의 타입을 정의합니다. 이 뷰는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. associatedtype ReusableView: UICollectionReusableView & Inputable - + /// 데코레이션 뷰의 종류를 나타내는 문자열입니다. var elementKind: String { get set } - + /// 데코레이션 뷰의 인스턴스입니다. var reusableView: ReusableView { get set } - + /// 데코레이션 뷰에 주입될 데이터입니다. var viewInput: ReusableView.Input { get set } } diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index a8c41655..2837db0c 100644 --- a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -22,31 +22,31 @@ struct SectionSupplementaryItem: Sec /// `SectionSupplementaryItemable` 프로토콜은 Supplementary View에 대한 인터페이스를 정의합니다. /// 해당 프로토콜을 준수하는 타입은 Supplementary View를 설정하고 반환하는 기능을 가져야 합니다. protocol SectionSupplementaryItemable { - + /// Supplementary View의 타입을 정의합니다. 이 뷰는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. associatedtype ReusableView: UICollectionReusableView, Inputable - + /// Supplementary View의 너비를 정의합니다. var widthDimension: NSCollectionLayoutDimension { get set } - + /// Supplementary View의 높이를 정의합니다. var heightDimension: NSCollectionLayoutDimension { get set } - + /// Supplementary View의 종류를 나타내는 문자열입니다. var elementKind: String { get set } - + /// Supplementary View의 정렬 방법을 정의합니다. var alignment: NSRectAlignment { get set } - + /// Supplementary View의 인스턴스입니다. var reusableView: ReusableView { get set } - + /// Supplementary View에 주입될 데이터입니다. var viewInput: ReusableView.Input { get set } } extension SectionSupplementaryItemable { - + /// 주어진 인덱스 경로에 해당하는 Supplementary View를 생성하고 반환합니다. /// /// - Parameters: @@ -59,13 +59,13 @@ extension SectionSupplementaryItemable { viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath ) -> UICollectionReusableView { - + collectionView.register( ReusableView.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: reusableView.identifiers ) - + guard let view = collectionView.dequeueReusableSupplementaryView( ofKind: kind, withReuseIdentifier: reusableView.identifiers, diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift index dd23e99e..158f9092 100644 --- a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift +++ b/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift @@ -13,22 +13,22 @@ import SnapKit /// `Sectionable` 프로토콜은 컬렉션 뷰의 섹션 및 셀을 설정하기 위한 인터페이스를 정의합니다. /// 해당 프로토콜은 제네릭 타입 `CellType`을 사용하여 유연하게 컬렉션 뷰 셀을 처리할 수 있습니다. protocol Sectionable { - + /// 컬렉션 뷰 셀의 타입을 정의합니다. 이 셀은 `UICollectionViewCell`과 `Inputable` 프로토콜을 준수해야 합니다. associatedtype CellType: UICollectionViewCell, Inputable - + /// 셀에 입력될 데이터의 리스트를 정의합니다. var inputDataList: [CellType.Input] { get set } - + /// 섹션에 추가될 Supplementary View의 리스트를 정의합니다. var supplementaryItems: [any SectionSupplementaryItemable]? { get set } - + /// 섹션에 추가될 Decoration View의 리스트를 정의합니다. var decorationItems: [any SectionDecorationItemable]? { get set } - + /// 섹션의 페이지 var currentPage: PublishSubject { get set } - + /// 주어진 섹션 인덱스와 레이아웃 환경을 바탕으로 `NSCollectionLayoutSection`을 반환합니다. /// /// - Parameters: @@ -36,7 +36,7 @@ protocol Sectionable { /// - env: 레이아웃 환경 /// - Returns: 섹션 레이아웃을 반환합니다. func getSection(section: Int, env: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection - + /// 섹션의 레이아웃을 설정합니다. /// /// - Parameters: @@ -44,7 +44,7 @@ protocol Sectionable { /// - env: 레이아웃 환경 /// - Returns: 설정된 섹션 레이아웃을 반환합니다. func setSection(section: Int, env: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection - + /// 주어진 인덱스 경로에 해당하는 셀을 반환합니다. /// /// - Parameters: @@ -52,7 +52,7 @@ protocol Sectionable { /// - indexPath: 셀의 인덱스 경로 /// - Returns: 셀을 반환합니다. func getCell(collectionView: UICollectionView, indexPath: IndexPath) -> UICollectionViewCell - + /// Supplementary View를 반환합니다. /// /// - Parameters: @@ -68,7 +68,7 @@ protocol Sectionable { } extension Sectionable { - + /// `setSection` 메서드를 호출하여 섹션 레이아웃을 설정하고, Supplementary View를 추가합니다. /// /// - Parameters: @@ -76,55 +76,53 @@ extension Sectionable { /// - env: 레이아웃 환경 /// - Returns: 설정된 섹션 레이아웃을 반환합니다. func getSection(section: Int, env: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - + let section = setSection(section: section, env: env) section.visibleItemsInvalidationHandler = { _, contentOffset, environment in let bannerIndex = Int(max(0, round(contentOffset.x / environment.container.contentSize.width))) // 음수가 되는 것을 방지하기 위해 max 사용 currentPage.onNext(bannerIndex) } - + if let supplementaryItems = supplementaryItems { - + let items = supplementaryItems.map { - + let size = NSCollectionLayoutSize( widthDimension: $0.widthDimension, heightDimension: $0.heightDimension ) - let sectionItem = NSCollectionLayoutBoundarySupplementaryItem( + + return NSCollectionLayoutBoundarySupplementaryItem( layoutSize: size, elementKind: $0.elementKind, alignment: $0.alignment ) - - return sectionItem } section.boundarySupplementaryItems = items } - + if let decorationItems = decorationItems { - + let items = decorationItems.map { - - let sectionItem = NSCollectionLayoutDecorationItem.background( + + return NSCollectionLayoutDecorationItem.background( elementKind: $0.elementKind ) - return sectionItem } section.decorationItems = items } return section } - + /// `inputDataList`의 비어 있지 않은지를 반환합니다. var isEmpty: Bool { return inputDataList.isEmpty } - + /// `inputDataList`의 아이템 수를 반환합니다. var dataCount: Int { return inputDataList.count - + } /// 주어진 인덱스 경로에 해당하는 셀을 생성하고 반환합니다. /// @@ -149,7 +147,7 @@ extension Sectionable { cell.injection(with: input) return cell } - + /// 주어진 Supplementary View를 생성하고 반환합니다. /// /// - Parameters: @@ -162,7 +160,7 @@ extension Sectionable { viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath ) -> UICollectionReusableView { - + guard let item = supplementaryItems?.filter({ $0.elementKind == kind }).first else { Logger.log( message: "ReusableView Not Register", @@ -172,8 +170,7 @@ extension Sectionable { ) fatalError() } - - let view = item.getItem(collectionView: collectionView, viewForSupplementaryElementOfKind: kind, at: indexPath) - return view + + return item.getItem(collectionView: collectionView, viewForSupplementaryElementOfKind: kind, at: indexPath) } } diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift b/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift index a23d52c3..8ec3d045 100644 --- a/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift +++ b/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift @@ -10,9 +10,9 @@ import UIKit import SnapKit final class BookMarkToastView: UIView { - + // MARK: - Components - + private let bgView: UIView = { let view = UIView() view.backgroundColor = .pb70 @@ -20,14 +20,14 @@ final class BookMarkToastView: UIView { view.clipsToBounds = true return view }() - + private let bookMarkLabel: UILabel = { let label = UILabel() label.setLineHeightText(text: "찜한 팝업에 저장했어요", font: .KorFont(style: .regular, size: 15), lineHeight: 1) label.textColor = .w100 return label }() - + private let unbookMarkLabel: UILabel = { let label = PPLabel(style: .regular, fontSize: 15, text: "찜한 팝업을 해제했어요") label.setLineHeightText(text: "찜한 팝업을 해제했어요", font: .KorFont(style: .regular, size: 15), lineHeight: 1) @@ -45,7 +45,7 @@ final class BookMarkToastView: UIView { button.titleLabel?.font = .KorFont(style: .medium, size: 12) return button }() - + // MARK: - init init(isBookMark: Bool) { super.init(frame: .zero) @@ -59,7 +59,7 @@ final class BookMarkToastView: UIView { setUpUnBookMarkConstraints() } } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -67,7 +67,7 @@ final class BookMarkToastView: UIView { // MARK: - SetUp private extension BookMarkToastView { - + func setUpBookMarkConstraints() { bgView.addSubview(moveButton) moveButton.snp.makeConstraints { make in @@ -83,7 +83,7 @@ private extension BookMarkToastView { make.centerY.equalToSuperview() } } - + func setUpUnBookMarkConstraints() { bgView.addSubview(unbookMarkLabel) unbookMarkLabel.snp.makeConstraints { make in diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift index a52b772c..54f6fdf7 100644 --- a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift +++ b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift @@ -7,14 +7,14 @@ import UIKit -import SnapKit -import RxSwift import RxCocoa +import RxSwift +import SnapKit final class ToastMaker { - + // MARK: - Properties - + /// 현재 디바이스 최상단 Window를 지정 static var window: UIWindow? { return UIApplication @@ -23,7 +23,7 @@ final class ToastMaker { .flatMap { ($0 as? UIWindowScene)?.windows ?? [] } .first { $0.isKeyWindow } } - + /// 최상단의 ViewController를 가져오는 메서드 private static func topViewController( _ rootViewController: UIViewController? = UIApplication.shared.keyWindow?.rootViewController @@ -39,32 +39,32 @@ final class ToastMaker { } return rootViewController } - + private static var currentToast: ToastView? private static var currentBookMarkToast: BookMarkToastView? private static var disposeBag = DisposeBag() } extension ToastMaker { - + // MARK: - Method - + /// 토스트 메시지를 생성하는 메서드 /// - Parameter message: 토스트 메세지에 담길 String 타입 static func createToast(message: String) { - + currentToast?.removeFromSuperview() currentToast = nil let toastMSG = ToastView(message: message) guard let window = window else { return } window.addSubview(toastMSG) currentToast = toastMSG - + toastMSG.snp.makeConstraints { make in make.bottom.equalTo(window.snp.bottom).inset(120) make.centerX.equalTo(window.snp.centerX) } - + UIView.animate( withDuration: 0.3, delay: 4, @@ -76,11 +76,11 @@ extension ToastMaker { if currentToast == toastMSG { currentToast = nil } } } - + /// 토스트 메시지를 생성하는 메서드 /// - Parameter message: 토스트 메세지에 담길 String 타입 static func createBookMarkToast(isBookMark: Bool) { - + currentBookMarkToast?.removeFromSuperview() currentBookMarkToast = nil disposeBag = DisposeBag() @@ -96,7 +96,7 @@ extension ToastMaker { owner.navigationController?.pushViewController(nextController, animated: true) }) .disposed(by: disposeBag) - + if isBookMark { toastMSG.snp.makeConstraints { make in make.bottom.equalTo(currentVC.view.snp.bottom).inset(120) @@ -108,8 +108,7 @@ extension ToastMaker { make.centerX.equalTo(currentVC.view.snp.centerX) } } - - + DispatchQueue.main.asyncAfter(deadline: .now() + 4) { UIView.animate( withDuration: 0.3, delay: 0, @@ -122,6 +121,6 @@ extension ToastMaker { disposeBag = DisposeBag() } } - + } } diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift index e20c3c72..bd04535e 100644 --- a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift +++ b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift @@ -11,9 +11,9 @@ import SnapKit /// 토스트 메시지를 담는 view 객체입니다 final class ToastView: UIView { - + // MARK: - Properties - + private let bgView: UIView = { let view = UIView() view.backgroundColor = .pb70 @@ -22,42 +22,42 @@ final class ToastView: UIView { view.sizeToFit() return view }() - + private let messageLabel: UILabel = { let label = UILabel() label.textColor = .w100 label.font = .KorFont(style: .regular, size: 15) return label }() - + // MARK: - Initializer - + init(message: String) { super.init(frame: .zero) setup() messageLabel.text = message } - + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension ToastView { - + // MARK: - Method - + private func setup() { addSubview(bgView) bgView.addSubview(messageLabel) - + bgView.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.bottom.equalTo(snp.bottom) make.top.equalTo(snp.top) make.height.equalTo(38) } - + messageLabel.snp.makeConstraints { make in make.leading.trailing.equalToSuperview().inset(16) make.centerY.equalToSuperview() From 82931e95ec7332e8c0cfcf188a72093c7cf5d4df Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 3 Apr 2025 19:44:46 +0900 Subject: [PATCH 053/393] =?UTF-8?q?refactor/#93:=20Swiftlint=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopUpStoreRegisterViewController.swift | 21 ++++++++++--------- .../CategoryEditModalReactor.swift | 8 +++---- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 9b92e953..bb60c048 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1,11 +1,13 @@ import UIKit +import CoreLocation +import PhotosUI + +import Then import SnapKit import ReactorKit import RxSwift import RxCocoa -import PhotosUI import Alamofire -import CoreLocation final class PopUpStoreRegisterViewController: BaseViewController { @@ -121,14 +123,13 @@ final class PopUpStoreRegisterViewController: BaseViewController { private let contentView = UIView() // MARK: - Form Background - private let formBackgroundView: UIView = { - let v = UIView() - v.backgroundColor = .white - v.layer.borderWidth = 1 - v.layer.borderColor = UIColor.lightGray.cgColor - v.layer.cornerRadius = 8 - return v - }() + private let formBackgroundView = UIView().then() { + $0.backgroundColor = .white + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.layer.cornerRadius = 8 + } + private let verticalStack = UIStackView() // MARK: - Bottom Save Button diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index d02975cd..2fb1bac2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -90,11 +90,11 @@ final class CategoryEditModalReactor: Reactor { var keepList: [Int64] = [] var deleteList: [Int64] = [] let currentArray = tagSection.inputDataList.filter { $0.isSelected == true }.compactMap { $0.id } - for i in currentArray { - if originSelectedID.contains(i) { - keepList.append(i) + for index in currentArray { + if originSelectedID.contains(index) { + keepList.append(index) } else { - addList.append(i) + addList.append(index) } } deleteList = originSelectedID.filter { !currentArray.contains($0) } From 81f4aafadde5ac9ee77886d0136c7ace4bb0094f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Apr 2025 10:45:20 +0000 Subject: [PATCH 054/393] style/#93: Apply SwiftLint autocorrect --- Poppool/Poppool/Application/AppDelegate.swift | 4 +- .../PopUpStoreRegisterViewController.swift | 89 ++++++------------- .../Admin/Data/MapDomain/MapPopUpStore.swift | 1 - .../MapDomain/Repository/MapRepository.swift | 2 +- .../Map/Common/MapUtilities.swift | 1 - .../Map/Common/NMFMapViewDelegateProxy.swift | 2 +- .../BalloonBackgroundView.swift | 14 +-- .../FillterSheetView/BalloonChipCell.swift | 2 +- .../FilterBottomSheetView.swift | 16 ++-- .../FilterBottomSheetViewController.swift | 26 ++---- .../FullScreenMapViewController.swift | 17 ++-- .../MapGuideView/MapGuideAppService.swift | 2 +- .../MapGuideView/MapGuideViewController.swift | 10 +-- .../Presentation/Map/MapView/MapMarker.swift | 4 +- .../Presentation/Map/MapView/MapReactor.swift | 2 +- .../Presentation/Map/MapView/MapView.swift | 9 +- .../Map/MapView/MapViewController.swift | 39 +++----- .../DetailInfoSectionCell.swift | 1 - 18 files changed, 78 insertions(+), 163 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 5be33692..5b018dd2 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,7 +1,7 @@ -import UIKit -import KakaoSDKCommon import CoreLocation +import KakaoSDKCommon import NMapsMap +import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index bb60c048..3c21a2e2 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1,13 +1,13 @@ -import UIKit import CoreLocation import PhotosUI +import UIKit -import Then -import SnapKit +import Alamofire import ReactorKit -import RxSwift import RxCocoa -import Alamofire +import RxSwift +import SnapKit +import Then final class PopUpStoreRegisterViewController: BaseViewController { @@ -24,8 +24,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { private var latField: UITextField? private var lonField: UITextField? private var descTV: UITextView? - - private let popupName: String = "" private var originalImageIds: [String: Int64] = [:] @@ -82,7 +80,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return lbl }() - private let menuButton: UIButton = { let btn = UIButton(type: .system) btn.setImage(UIImage(systemName: "adminlist"), for: .normal) @@ -107,7 +104,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return lbl }() - private let addImageButton = UIButton(type: .system).then { $0.setTitle("이미지 추가", for: .normal) $0.setTitleColor(.systemBlue, for: .normal) @@ -123,13 +119,13 @@ final class PopUpStoreRegisterViewController: BaseViewController { private let contentView = UIView() // MARK: - Form Background - private let formBackgroundView = UIView().then() { + private let formBackgroundView = UIView().then { $0.backgroundColor = .white $0.layer.borderWidth = 1 $0.layer.borderColor = UIColor.lightGray.cgColor $0.layer.cornerRadius = 8 } - + private let verticalStack = UIStackView() // MARK: - Bottom Save Button @@ -158,12 +154,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("카테고리 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() @@ -171,12 +167,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("기간 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() @@ -184,12 +180,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle("시간 선택 ▾", for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() @@ -199,8 +195,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) - - view.backgroundColor = UIColor(white:0.95, alpha:1) + + view.backgroundColor = UIColor(white: 0.95, alpha: 1) if let store = editingStore { // 삭제된 이미지 ID 복원 @@ -226,11 +222,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { setupAddressField() setupAllFieldListeners() - - } - // MARK: - Navigation private func setupNavigation() { backButton.addTarget(self, action: #selector(onBack), for: .touchUpInside) @@ -375,8 +368,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } - - func loadStoreDetail(for storeId: Int64) { Logger.log(message: "상세 정보 요청 시작 - Store ID: \(storeId)", category: .debug) @@ -448,8 +439,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } - - // MARK: - Layout private func setupLayout() { // (1) 상단 컨테이너 @@ -601,7 +590,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // 1) 주소 (TextField) let addressField = makeRoundedTextField("팝업스토어 주소를 입력해 주세요.") self.addressField = addressField - addressField.snp.makeConstraints { make in + addressField.snp.makeConstraints { _ in addressField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) } @@ -613,14 +602,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { self.latField = latField // latField와 연결 latField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) - let lonLabel = makePlainLabel("경도") let lonField = makeRoundedTextField("") self.lonField = lonField // lonField와 연결 lonField.textAlignment = .center lonField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) - let latStack = UIStackView(arrangedSubviews: [latLabel, latField]) latStack.axis = .horizontal latStack.spacing = 8 @@ -642,7 +629,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { locationVStack.spacing = 8 locationVStack.distribution = .fillEqually - // 한 행에 왼쪽 "위치", 오른쪽 2줄(주소 / 위도경도) addRowCustom(leftTitle: "위치", rightView: locationVStack, rowHeight: nil, totalHeight: 80) @@ -671,7 +657,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { markerVStack.spacing = 8 markerVStack.distribution = .fillEqually - // 한 행 => "마커" 라벨, 오른쪽 2줄 (마커명, 스니펫) addRowCustom(leftTitle: "마커", rightView: markerVStack, rowHeight: nil, totalHeight: 80) @@ -702,7 +687,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } - // MARK: - Row private func addRowTextField(leftTitle: String, placeholder: String) { @@ -796,7 +780,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { saveButton.backgroundColor = isFormValid ? .systemBlue : .lightGray } - private func addRowCustom(leftTitle: String, rightView: UIView, rowHeight: CGFloat? = 36, @@ -889,7 +872,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { } } - private func updatePeriodButtonTitle() { guard let selectedStartDate = selectedStartDate, let selectedEndDate = selectedEndDate else { return } let df = DateFormatter() @@ -997,7 +979,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeRoundedTextField(_ placeholder: String) -> UITextField { let tf = UITextField() tf.placeholder = placeholder - tf.font = UIFont.systemFont(ofSize:14) + tf.font = UIFont.systemFont(ofSize: 14) tf.textColor = .darkGray tf.borderStyle = .none tf.layer.cornerRadius = 8 @@ -1011,12 +993,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { let btn = UIButton(type: .system) btn.setTitle(title, for: .normal) btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize:14) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) btn.layer.cornerRadius = 8 btn.layer.borderWidth = 1 btn.layer.borderColor = UIColor.lightGray.cgColor btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn } @@ -1025,7 +1007,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { if let icon = UIImage(named: iconName) { btn.setImage(icon, for: .normal) btn.imageView?.contentMode = .scaleAspectFit - btn.titleEdgeInsets = UIEdgeInsets(top:0, left:6, bottom:0, right:0) + btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 0) } return btn } @@ -1033,7 +1015,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeSimpleLabel(_ text: String) -> UILabel { let lbl = UILabel() lbl.text = text - lbl.font = UIFont.systemFont(ofSize:14) + lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray return lbl } @@ -1042,7 +1024,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // 작은 라벨(위도/경도/마커명/스니펫 등) let lbl = UILabel() lbl.text = text - lbl.font = UIFont.systemFont(ofSize:14) + lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray lbl.textAlignment = .right lbl.setContentHuggingPriority(.required, for: .horizontal) @@ -1051,12 +1033,12 @@ final class PopUpStoreRegisterViewController: BaseViewController { private func makeRoundedTextView() -> UITextView { let tv = UITextView() - tv.font = UIFont.systemFont(ofSize:14) + tv.font = UIFont.systemFont(ofSize: 14) tv.textColor = .darkGray tv.layer.cornerRadius = 8 tv.layer.borderWidth = 1 tv.layer.borderColor = UIColor.lightGray.cgColor - tv.textContainerInset = UIEdgeInsets(top:7, left:7, bottom:7, right:7) + tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) tv.isScrollEnabled = true return tv } @@ -1064,8 +1046,8 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Padding private extension UITextField { - func setLeftPaddingPoints(_ amount: CGFloat){ - let paddingView = UIView(frame: CGRect(x:0, y:0, width:amount, height: frame.size.height)) + func setLeftPaddingPoints(_ amount: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) leftView = paddingView leftViewMode = .always } @@ -1148,9 +1130,6 @@ private extension PopUpStoreRegisterViewController { updateSaveButtonState() } - - - } extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { @@ -1172,7 +1151,7 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { for (index, provider) in itemProviders.enumerated() { if provider.canLoadObject(ofClass: UIImage.self) { dispatchGroup.enter() - provider.loadObject(ofClass: UIImage.self) { [weak self] object, error in + provider.loadObject(ofClass: UIImage.self) { [weak self] object, _ in defer { dispatchGroup.leave() } guard let self = self, let image = object as? UIImage else { return } @@ -1248,7 +1227,6 @@ private extension PopUpStoreRegisterViewController { return false } - // (4) 위도/경도 Logger.log(message: "latField.text = \(latField?.text ?? "nil")", category: .debug) Logger.log(message: "lonField.text = \(lonField?.text ?? "nil")", category: .debug) @@ -1324,7 +1302,6 @@ private extension PopUpStoreRegisterViewController { } } - // 폼 데이터 검증 private func validateFormData() -> Bool { guard let name = nameField?.text, @@ -1420,7 +1397,6 @@ private extension PopUpStoreRegisterViewController { .disposed(by: disposeBag) } - private func uploadImagesForUpdate(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { let uuid = UUID().uuidString let updatedImages = images.enumerated().map { index, image in @@ -1652,7 +1628,6 @@ private extension PopUpStoreRegisterViewController { startDateBeforeEndDate: isValidDateOrder ) - adminUseCase.createStore(request: request) .subscribe( onNext: { [weak self] _ in @@ -1693,7 +1668,6 @@ private extension PopUpStoreRegisterViewController { } } - private func createDateTime(date: Date?, time: Date?) -> Date? { guard let date = date else { return nil } @@ -1732,9 +1706,6 @@ private extension PopUpStoreRegisterViewController { return formatter.string(from: date) } - - - private func prepareDateTime() -> (startDate: String, endDate: String) { // 시작일/시간 결합 let startDateTime = createDateTime(date: selectedStartDate, time: selectedStartTime) @@ -1773,11 +1744,6 @@ private extension PopUpStoreRegisterViewController { return start < end } - - - - - private func showSuccessAlert() { let alert = UIAlertController( title: "등록 성공", @@ -1861,7 +1827,6 @@ extension PopUpStoreRegisterViewController: UITextFieldDelegate { .disposed(by: disposeBag) } - @objc private func addressFieldDidChange(_ textField: UITextField) { guard let address = textField.text, !address.isEmpty else { return } diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift index 02384864..2be1694e 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/MapPopUpStore.swift @@ -15,7 +15,6 @@ struct MapPopUpStore: Equatable { let markerSnippet: String let mainImageUrl: String? - var nmgCoordinate: NMGLatLng { NMGLatLng(lat: latitude, lng: longitude) } diff --git a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift index 8ac511d9..a10bffc5 100644 --- a/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift +++ b/Poppool/Poppool/Presentation/Admin/Data/MapDomain/Repository/MapRepository.swift @@ -62,7 +62,7 @@ class DefaultMapRepository: MapRepository { query: query, categories: categories ), - interceptor: TokenInterceptor() + interceptor: TokenInterceptor() ) .map { $0.popUpStoreList } } diff --git a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift index 3b4cd3bf..d76dafe1 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapUtilities.swift @@ -28,4 +28,3 @@ public let gyeonggiSouthRegions: [String] = [ "용인시", "화성시", "수원시", "안산시", "부천시", "의왕시", "과천시", "여주시", "양평군", "광주시", "이천시" ] - diff --git a/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift index dadff963..71e6a303 100644 --- a/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift +++ b/Poppool/Poppool/Presentation/Map/Common/NMFMapViewDelegateProxy.swift @@ -1,6 +1,6 @@ import NMapsMap -import RxSwift import RxCocoa +import RxSwift /// NMFMapViewDelegateProxy는 NMFMapView의 delegate 이벤트를 RxSwift Observable로 변환하는 역할 class NMFMapViewDelegateProxy: DelegateProxy, DelegateProxyType, NMFMapViewDelegate { diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift index 076c61b6..45161c19 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonBackgroundView.swift @@ -89,19 +89,17 @@ final class BalloonBackgroundView: UIView { backgroundColor = .clear self.isUserInteractionEnabled = true containerView.isUserInteractionEnabled = true - collectionView.isUserInteractionEnabled = true + collectionView.isUserInteractionEnabled = true setupLayout() setupCollectionView() } - - required init?(coder: NSCoder) { fatalError() } // MARK: - Setup private func setupLayout() { - + addSubview(containerView) containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() @@ -109,7 +107,6 @@ final class BalloonBackgroundView: UIView { make.bottom.equalToSuperview().priority(.high) } - containerView.addSubview(collectionView) containerView.addSubview(singleRegionIcon) containerView.addSubview(singleRegionTitleLabel) @@ -120,7 +117,6 @@ final class BalloonBackgroundView: UIView { make.bottom.equalToSuperview().priority(.high) } - singleRegionIcon.snp.makeConstraints { make in make.top.equalToSuperview().offset(24) make.centerX.equalToSuperview() @@ -242,14 +238,12 @@ final class BalloonBackgroundView: UIView { collectionView.layoutIfNeeded() - let balloonWidth = self.bounds.width let horizontalSpacing: CGFloat = 8 let leftPadding: CGFloat = 20 let rightPadding: CGFloat = 20 let availableWidth = balloonWidth - leftPadding - rightPadding - var currentRowWidth: CGFloat = 0 var numberOfRows: Int = 1 @@ -269,14 +263,12 @@ final class BalloonBackgroundView: UIView { let itemHeight: CGFloat = 36 let interGroupSpacing: CGFloat = 8 let verticalInset: CGFloat = 20 + 20 - let totalHeight = max( + return max( (itemHeight * CGFloat(numberOfRows)) + (interGroupSpacing * CGFloat(numberOfRows - 1)) + verticalInset, 36 ) - - return totalHeight } private func calculateButtonWidth(for text: String, font: UIFont, isSelected: Bool) -> CGFloat { let textWidth = (text as NSString).size(withAttributes: [.font: font]).width diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift index 0d2f0b54..40f33be0 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift @@ -11,7 +11,7 @@ final class BalloonChipCell: UICollectionViewCell { font: .KorFont(style: .medium, size: 11), cornerRadius: 15 ) - + button.titleLabel?.lineBreakMode = .byTruncatingTail button.titleLabel?.adjustsFontSizeToFitWidth = false return button diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index d465e412..c9a288cb 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -1,5 +1,5 @@ -import UIKit import SnapKit +import UIKit final class FilterBottomSheetView: UIView { // MARK: - UI Components @@ -26,8 +26,7 @@ final class FilterBottomSheetView: UIView { }() let segmentedControl: PPSegmentedControl = { - let control = PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) - return control + return PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) }() let locationScrollView: UIScrollView = { @@ -40,7 +39,7 @@ final class FilterBottomSheetView: UIView { var categoryHeightConstraint: Constraint? let categoryCollectionView: UICollectionView = { - let layout = UICollectionViewCompositionalLayout { section, env in + let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), heightDimension: .absolute(36) @@ -79,13 +78,11 @@ final class FilterBottomSheetView: UIView { let balloonBackgroundView = BalloonBackgroundView() let resetButton: PPButton = { - let button = PPButton(style: .secondary, text: "초기화") - return button + return PPButton(style: .secondary, text: "초기화") }() let saveButton: PPButton = { - let button = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - return button + return PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") }() private let buttonStack: UIStackView = { @@ -97,8 +94,7 @@ final class FilterBottomSheetView: UIView { }() let filterChipsView: FilterChipsView = { - let view = FilterChipsView() - return view + return FilterChipsView() }() private var balloonHeightConstraint: Constraint? diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift index f51ac692..b5261aa8 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetViewController.swift @@ -1,8 +1,8 @@ -import UIKit -import SnapKit -import RxSwift -import RxCocoa import ReactorKit +import RxCocoa +import RxSwift +import SnapKit +import UIKit final class FilterBottomSheetViewController: UIViewController, View { typealias Reactor = FilterBottomSheetReactor @@ -43,7 +43,7 @@ final class FilterBottomSheetViewController: UIViewController, View { setupLayout() setupGestures() setupCollectionView() - + containerView.filterChipsView.onRemoveChip = { [weak self] removedOption in guard let self = self, let reactor = self.reactor else { return } @@ -140,14 +140,11 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - }) .map { Reactor.Action.resetFilters } .bind(to: reactor.action) .disposed(by: disposeBag) - - containerView.saveButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -164,7 +161,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - containerView.closeButton.rx.tap .bind { [weak self] _ in guard let self = self, let reactor = self.reactor else { return } @@ -177,7 +173,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - // 5. 탭 변경 reactor.state.map { $0.activeSegment } .distinctUntilChanged() @@ -196,7 +191,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - // 6. 위치 데이터 바인딩 let locations = reactor.state .map { $0.locations } @@ -223,7 +217,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - let locationAndSubRegions = reactor.state .map { ($0.selectedLocationIndex, $0.selectedSubRegions) } .distinctUntilChanged { prev, curr in @@ -239,7 +232,6 @@ final class FilterBottomSheetViewController: UIViewController, View { guard let self = self, let reactor = self.reactor else { return } let (selectedIndexOptional, selectedSubRegions) = data - guard let selectedIndex = selectedIndexOptional, selectedIndex >= 0, selectedIndex < reactor.currentState.locations.count else { return } @@ -257,7 +249,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } ) - if let button = self.containerView.locationContentView.subviews[selectedIndex] as? UIButton { self.containerView.updateBalloonPosition(for: button) } @@ -296,8 +287,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - - reactor.state.map { $0.selectedSubRegions + $0.selectedCategories } .distinctUntilChanged() .bind { [weak self] selectedOptions in @@ -318,7 +307,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } .disposed(by: disposeBag) - reactor.state.map { $0.isSaveEnabled } .distinctUntilChanged() .observe(on: MainScheduler.instance) @@ -418,7 +406,6 @@ final class FilterBottomSheetViewController: UIViewController, View { view.layoutIfNeeded() } - private func setupGestures() { let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTapDimmedView)) tapGesture.delegate = self @@ -441,7 +428,6 @@ final class FilterBottomSheetViewController: UIViewController, View { let index = reactor.currentState.locations.firstIndex(where: { $0.main == locations }) { reactor.action.onNext(.selectLocation(index)) - } // 4. 필터 칩 뷰 업데이트 @@ -458,8 +444,6 @@ final class FilterBottomSheetViewController: UIViewController, View { } } - - func hideBottomSheet() { UIView.animate(withDuration: 0.25, delay: 0, options: .curveEaseIn) { self.dimmedView.alpha = 0 diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 8eeaa084..82393654 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,9 +1,9 @@ -import UIKit -import SnapKit -import RxSwift -import RxCocoa -import NMapsMap import CoreLocation +import NMapsMap +import RxCocoa +import RxSwift +import SnapKit +import UIKit class FullScreenMapViewController: MapViewController { // MARK: - Properties @@ -12,7 +12,6 @@ class FullScreenMapViewController: MapViewController { private var markerLocked = false // 마커 상태 잠금 플래그 private var initialMarker: NMFMarker? - // MARK: - Initialization init(store: MapPopUpStore?, existingMarker: NMFMarker? = nil) { self.initialStore = store @@ -20,8 +19,6 @@ class FullScreenMapViewController: MapViewController { super.init() } - - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } @@ -36,7 +33,6 @@ class FullScreenMapViewController: MapViewController { Logger.log(message: "💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) configureInitialMapPosition() - Logger.log(message: "✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) mainView.mapView.touchDelegate = self @@ -145,8 +141,6 @@ class FullScreenMapViewController: MapViewController { carouselView.isHidden = false } - - override func bind(reactor: MapReactor) { super.bind(reactor: reactor) @@ -192,7 +186,6 @@ class FullScreenMapViewController: MapViewController { super.updateMarkerStyle(marker: marker, selected: selected, isCluster: isCluster, count: count, regionName: regionName) } - override func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true markerLocked = true diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift index aab14ce8..048c1f2f 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideAppService.swift @@ -1,5 +1,5 @@ -import UIKit import CoreLocation +import UIKit enum MapAppType { case naver diff --git a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift index 4810452c..60d2d07c 100644 --- a/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -1,9 +1,9 @@ -import UIKit -import SnapKit -import ReactorKit -import RxSwift import CoreLocation import NMapsMap +import ReactorKit +import RxSwift +import SnapKit +import UIKit final class MapGuideViewController: UIViewController, View { // MARK: - Properties @@ -15,7 +15,7 @@ final class MapGuideViewController: UIViewController, View { private let dimmingView: UIView = { let viewInstance = UIView() - viewInstance.backgroundColor = UIColor.gray.withAlphaComponent(0.7) + viewInstance.backgroundColor = UIColor.gray.withAlphaComponent(0.7) viewInstance.alpha = 0 return viewInstance }() diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift index 9ddfb0e1..be525bb3 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapMarker.swift @@ -1,6 +1,6 @@ -import UIKit -import SnapKit import NMapsMap +import SnapKit +import UIKit final class MapMarker: UIView { // MARK: - Components diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift index dc404ec1..b0e82322 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapReactor.swift @@ -217,7 +217,7 @@ final class MapReactor: Reactor { case .viewDidLoad(let id): return directionRepository.getPopUpDirection(popUpStoreId: id) .do( - onNext: { response in + onNext: { _ in }, onError: { error in Logger.log( diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift index 3df6ad08..af1529e9 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapView.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapView.swift @@ -1,12 +1,12 @@ -import UIKit -import SnapKit import NMapsMap +import SnapKit +import UIKit final class MapView: UIView { // MARK: - Components let mapView: NMFMapView = { let view = NMFMapView() - view.positionMode = .disabled + view.positionMode = .disabled view.zoomLevel = 14 view.extent = NMGLatLngBounds( @@ -54,8 +54,7 @@ final class MapView: UIView { }() var storeCard: MapPopupCarouselView = { - let view = MapPopupCarouselView() - return view + return MapPopupCarouselView() }() // MARK: - Init diff --git a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift index 93d7d26f..e327b463 100644 --- a/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/Presentation/Map/MapView/MapViewController.swift @@ -1,12 +1,12 @@ -import UIKit +import CoreLocation import FloatingPanel -import SnapKit -import RxSwift -import RxCocoa -import ReactorKit import NMapsMap -import CoreLocation +import ReactorKit +import RxCocoa import RxGesture +import RxSwift +import SnapKit +import UIKit class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NMFMapViewTouchDelegate, NMFMapViewCameraDelegate, UIGestureRecognizerDelegate { typealias Reactor = MapReactor @@ -278,7 +278,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mapViewTapGesture.delegate = self } - private let defaultZoomLevel: Double = 15.0 private func setupPanAndSwipeGestures() { storeListViewController.mainView.grabberHandle.rx.swipeGesture(.up) @@ -347,12 +346,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM lng: location.coordinate.longitude ), zoomTo: 15.0) - self.mainView.mapView.moveCamera(cameraUpdate) } .disposed(by: disposeBag) - mainView.filterChips.onRemoveLocation = { [weak self] in guard let self = self else { return } // 필터 제거 액션 @@ -547,7 +544,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: 1) // 중요: 마커에 직접 터치 핸들러 추가 - marker.touchHandler = { [weak self] (overlay) -> Bool in + marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } Logger.log(message: "마커 터치됨! 위치: \(marker.position), 스토어: \(store.name)", category: .debug) @@ -560,7 +557,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM markerDictionary[store.id] = marker } - func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { if selected { marker.width = 44 @@ -660,10 +656,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateMapViewAlpha(for offset: CGFloat, minOffset: CGFloat, maxOffset: CGFloat) { let progress = (maxOffset - offset) / (maxOffset - minOffset) - mainView.mapView.alpha = max(0, min(progress, 1)) + mainView.mapView.alpha = max(0, min(progress, 1)) } - private func animateToState(_ state: ModalState) { guard modalState != state else { return } self.view.layoutIfNeeded() @@ -790,7 +785,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) // 직접 터치 핸들러 추가 - marker.touchHandler = { [weak self] (overlay) -> Bool in + marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } print("개별 마커 터치됨! 스토어: \(store.name)") @@ -816,7 +811,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeGroup.count) // 직접 터치 핸들러 추가 - marker.touchHandler = { [weak self] (overlay) -> Bool in + marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } print("마이크로 클러스터 마커 터치됨! 스토어 수: \(storeGroup.count)개") @@ -902,7 +897,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM CATransaction.commit() } - private func clearAllMarkers() { individualMarkerDictionary.values.forEach { $0.mapView = nil } individualMarkerDictionary.removeAll() @@ -1024,7 +1018,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM currentFilterBottomSheet = nil } - //기본 마커 + // 기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { markerDictionary.values.forEach { $0.mapView = nil } markerDictionary.removeAll() @@ -1037,7 +1031,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) // 직접 터치 핸들러 추가 - marker.touchHandler = { [weak self] (overlay) -> Bool in + marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } print("검색 결과 마커 터치됨! 스토어: \(store.name)") @@ -1175,7 +1169,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) // 직접 터치 핸들러 추가 - marker.touchHandler = { [weak self] (overlay) -> Bool in + marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } print("클러스터 내 마커 터치됨! 스토어: \(store.name)") @@ -1187,7 +1181,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } - private func findMarkerForStore(for store: MapPopUpStore) -> NMFMarker? { // individualMarkerDictionary에 저장된 모든 마커를 순회 for marker in individualMarkerDictionary.values { @@ -1508,10 +1501,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - - default: + default: print("기타 레벨 클러스터 처리") - break } // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 @@ -1525,7 +1516,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return true } - // 마이크로 클러스터 탭 처리 func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { // 이미 선택된 마커를 다시 탭할 때 @@ -1644,7 +1634,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return false } - // 지도 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { guard !isMovingToMarker else { return } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift index e7029550..1ba18382 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift @@ -170,4 +170,3 @@ extension DetailInfoSectionCell: Inputable { addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 13)) } } - From d6137556ba2bde930819d54863e47ba9a3bf377c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 10:51:16 +0900 Subject: [PATCH 055/393] =?UTF-8?q?fix/#82:=20=ED=8C=A8=ED=82=A4=EC=A7=80?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= 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.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 172ad40b..ea30e126 100644 --- a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "a264a064b245047631c2da3a5af0624dcc8a9814c5033d1880880c8dbbdc6a20", + "originHash" : "90182ae75bddc149e01f3583353785f437a618e30e1e9df09cabd3c7f3605747", "pins" : [ { "identity" : "alamofire", @@ -33,8 +33,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/onevcat/Kingfisher.git", "state" : { - "revision" : "3db26ab625d194c38e68c1a40e43d1bc12743fe0", - "version" : "8.2.0" + "revision" : "4c6b067f96953ee19526e49e4189403a2be21fb3", + "version" : "8.3.1" } }, { @@ -141,8 +141,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/scinfu/SwiftSoup", "state" : { - "revision" : "18ad8b8ff0f03f3c0a5544ffccfa2ea1c051ae6e", - "version" : "2.8.0" + "revision" : "bba848db50462894e7fc0891d018dfecad4ef11e", + "version" : "2.8.7" } }, { From 17ac190254e66315255d98bbc8d162c1f4325c54 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 13:10:03 +0900 Subject: [PATCH 056/393] =?UTF-8?q?style/#82:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=83=81=EB=8B=A8=20=EC=A3=BC=EC=84=9D=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Scene/Detail/DetailController.swift | 7 ------- .../Poppool/Presentation/Scene/Detail/DetailReactor.swift | 7 ------- .../Presentation/Scene/Home/Main/HomeController.swift | 7 ------- .../Poppool/Presentation/Scene/Home/Main/HomeReactor.swift | 7 ------- .../ImageBannerSection/ImageBannerSectionCell.swift | 7 ------- 5 files changed, 35 deletions(-) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift index 75cfd69c..cc6f6183 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift @@ -1,10 +1,3 @@ -// -// DetailController.swift -// Poppool -// -// Created by SeoJunYoung on 12/9/24. -// - import UIKit import SnapKit diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 165a8b7a..c8c38941 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -1,10 +1,3 @@ -// -// DetailReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/9/24. -// - import UIKit import ReactorKit diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift index 5a507a7d..47c5a576 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift @@ -1,10 +1,3 @@ -// -// HomeController.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - import UIKit import SnapKit diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift index d7bdfb8a..6bba6dea 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift @@ -1,10 +1,3 @@ -// -// HomeReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - import UIKit import ReactorKit diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index 025ec4a9..d72ea488 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -1,10 +1,3 @@ -// -// ImageBannerSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - import UIKit import SnapKit From 612fbdef3a5d8db7a7e37ff0bf95e24e703ce88a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 13:11:00 +0900 Subject: [PATCH 057/393] =?UTF-8?q?fix/#82:=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=EB=B0=B0=EB=84=88=EC=97=90=20=EC=82=AC=EC=A7=84=EC=9D=B4=20?= =?UTF-8?q?=ED=95=9C=EC=9E=A5=EC=9D=BC=20=EB=95=8C=20=EB=AC=B4=ED=95=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=81=AC=EB=A1=A4=20=EB=B0=8F=20=EC=8A=A4=ED=81=AC?= =?UTF-8?q?=EB=A1=A4=20=EB=B0=A9=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageBannerSectionCell.swift | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index d72ea488..0803d4fa 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -200,16 +200,20 @@ extension ImageBannerSectionCell: Inputable { if imageSection.isEmpty { pageControl.setNumberOfPages(input.imagePaths.count) let datas = zip(input.imagePaths, input.idList) - let backContents = datas.suffix(1) - let frontContents = datas.prefix(1) - imageSection.inputDataList = datas.map { .init(imagePath: $0.0, id: $0.1) } - imageSection.inputDataList.append(contentsOf: frontContents.map { .init(imagePath: $0.0, id: $0.1) }) - imageSection.inputDataList = backContents.map {.init(imagePath: $0.0, id: $0.1) } + imageSection.inputDataList - DispatchQueue.main.async { [weak self] in - self?.contentCollectionView.scrollToItem( - at: .init(row: 1, section: 0), - at: .centeredHorizontally, animated: false - ) + if input.imagePaths.count > 1 { + let backContents = datas.suffix(1) + let frontContents = datas.prefix(1) + imageSection.inputDataList = datas.map { .init(imagePath: $0.0, id: $0.1) } + imageSection.inputDataList.append(contentsOf: frontContents.map { .init(imagePath: $0.0, id: $0.1) }) + imageSection.inputDataList = backContents.map { .init(imagePath: $0.0, id: $0.1) } + imageSection.inputDataList + DispatchQueue.main.async { [weak self] in + self?.contentCollectionView.scrollToItem( + at: .init(row: 1, section: 0), + at: .centeredHorizontally, animated: false + ) + } + } else { + imageSection.inputDataList = datas.map { .init(imagePath: $0.0, id: $0.1) } } } From 68e8644a97ccf767d9117b36d9151d6e64028cac Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 13:12:27 +0900 Subject: [PATCH 058/393] =?UTF-8?q?fix/#82:=20=EB=B0=B0=EB=84=88=20?= =?UTF-8?q?=EC=82=AC=EC=A7=84=EC=9D=B4=201=EC=9E=A5=EC=9D=BC=20=EB=95=8C?= =?UTF-8?q?=20=EB=8D=B8=EB=A6=AC=EA=B2=8C=EC=9D=B4=ED=8A=B8=20=EB=A9=94?= =?UTF-8?q?=EC=84=9C=EB=93=9C=20=EB=A1=9C=EC=A7=81=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 셀에서 벗어날 때 해당 델리게이트 메서드가 동작 - 하지만 셀에 이미지가 한장일 때 해당 메서드가 out of index를 유발함 - 해당 부분을 guard문으로 처리 --- .../ImageBannerSection/ImageBannerSectionCell.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index 0803d4fa..3f650e81 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -263,6 +263,8 @@ extension ImageBannerSectionCell: UICollectionViewDelegate, UICollectionViewData } func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { + guard imageSection.dataCount > 1 else { return } + if currentIndex == 0 { contentCollectionView.scrollToItem( at: .init(row: imageSection.dataCount - 2, section: 0), From 7ce8f3008137f2ab6ab28358cbacb6dea576d7d0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 13:13:34 +0900 Subject: [PATCH 059/393] =?UTF-8?q?docs/#82:=20=EC=9D=B4=EC=8A=88=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20=EC=A4=91=20=EC=B6=94=EA=B0=80=20=EB=B0=9C?= =?UTF-8?q?=EA=B2=AC=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20=EC=A3=BC=EC=84=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 - 디버깅 중 확인한 부분입니다 - (홈 → 상세) 이동 후에도 홈의 배너 무한스크롤이 계속 작동하는 문제 - 홈의 무한 스크롤이 한번 시작했음에도 계속 다시 시작하는 문제 --- .../ImageBannerSection/ImageBannerSectionCell.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index 3f650e81..eb9baaca 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -85,6 +85,8 @@ final class ImageBannerSectionCell: UICollectionViewCell { autoScrollTimer = nil } + // FIXME: (홈 -> 상세) 이동 시 홈의 자동 스크롤이 계속 돌아감. + // FIXME: 또한 오토 스크롤을 한번만 실행하면 되는데, 사진이 넘어갈 때 마다 실행되는것으로 보임 func startAutoScroll(interval: TimeInterval = 3.0) { stopAutoScroll() // 기존 타이머를 중지 stopButton.isHidden = false From 7177fc073edee96b0a49c8e70d213ab1a6cbd778 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 4 Apr 2025 19:42:41 +0900 Subject: [PATCH 060/393] =?UTF-8?q?style/#93:=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=8A=A4=ED=83=80=EC=9D=BC=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=EC=9D=BC=EB=B6=80=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: JunYoung --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 ---- .../ResponseDTO/GetMyProfileResponseDTO.swift | 11 ++++++++- .../NetworkLayer/Provider/ProviderImpl.swift | 17 ++++++-------- .../AdminBottomSheetView.swift | 4 ++-- .../Presentation/Components/PPButton.swift | 2 +- .../Components/PPCancelHeaderView.swift | 2 +- .../Presentation/Components/PPLabel.swift | 2 +- .../Presentation/Components/PPPicker.swift | 4 ++-- .../Components/PPReturnHeaderView.swift | 2 +- .../Components/PPSegmentedControl.swift | 12 +++++----- .../Presentation/Extension/Optional+.swift | 14 ----------- .../Presentation/Extension/UIFont+.swift | 12 ++-------- .../LocationPermissionBottomSheet.swift | 8 +++---- .../FillterSheetView/BalloonChipCell.swift | 6 ++--- .../FilterBottomSheetView.swift | 6 ++--- .../CommentDetailController.swift | 2 +- .../CommentDetailContentSectionCell.swift | 2 +- .../CommentMyMenu/CommentMyMenuView.swift | 6 ++--- .../CommentUserInfoController.swift | 2 +- .../CommentUserInfo/CommentUserInfoView.swift | 6 ++--- .../CommentListTitleSectionCell.swift | 2 +- .../CommentSelected/CommentSelectedView.swift | 6 ++--- .../CommentUserBlockController.swift | 2 +- .../InstaComment/InstaCommentAddReactor.swift | 8 +++---- .../View/InstaCommentAddView.swift | 6 ++--- .../InstaGuideChildSectionCell.swift | 2 +- .../AddCommentDescriptionSectionCell.swift | 2 +- .../AddCommentSectionCell.swift | 2 +- .../AddCommentTitleSectionCell.swift | 2 +- .../View/NormalCommentAddView.swift | 2 +- .../NormalCommentEditView.swift | 2 +- .../OtherUserCommentSectionCell.swift | 8 +++---- .../View/OtherUserCommentView.swift | 2 +- .../Scene/Detail/DetailController.swift | 4 ++-- .../Scene/Detail/DetailReactor.swift | 23 ++++++++----------- .../DetailCommentSectionCell.swift | 16 ++++++------- .../DetailCommentTitleSectionCell.swift | 4 ++-- .../DetailContentSectionCell.swift | 2 +- .../DetailEmptyCommetSectionCell.swift | 2 +- .../DetailInfoSectionCell.swift | 6 ++--- .../DetailSimilarSectionCell.swift | 6 ++--- .../DetailTitleSectionCell.swift | 2 +- .../Scene/Home/Main/HomeReactor.swift | 11 ++++++++- .../HomeCardSection/HomeCardSectionCell.swift | 10 ++++---- .../HomePopularCardSectionCell.swift | 4 ++-- .../HomeTitleSectionCell.swift | 6 ++--- .../Scene/Login/LastLoginView.swift | 2 +- .../Scene/Login/Main/LoginView.swift | 4 ++-- .../Scene/Login/Sub/SubLoginView.swift | 4 ++-- .../BlockUserListSectionCell.swift | 6 ++--- .../Block/View/BlockUserManageView.swift | 2 +- .../Main/MyPageBookmarkController.swift | 4 ++-- .../Bookmark/Main/MyPageBookmarkView.swift | 4 ++-- .../PopUpCardSectionCell.swift | 8 +++---- .../View/PopUpCardSection/PopUpCardView.swift | 8 +++---- .../FAQDropdownSectionCell.swift | 6 ++--- .../Scene/MyPage/FAQ/View/FAQView.swift | 2 +- .../MyPageCommentSectionCell.swift | 2 +- .../MyPageListSectionCell.swift | 4 ++-- .../MyPageMyCommentTitleSectionCell.swift | 4 ++-- .../MyPageProfileSectionCell.swift | 6 ++--- .../ListCountButtonSectionCell.swift | 4 ++-- .../MyComment/Main/View/MyCommentView.swift | 2 +- .../MyCommentedPopUpGridSectionCell.swift | 6 ++--- .../Detail/MyPageNoticeDetailController.swift | 6 ++--- .../Detail/MyPageNoticeDetailView.swift | 2 +- .../Notice/List/View/MyPageNoticeView.swift | 2 +- .../NoticeListSectionCell.swift | 4 ++-- .../Main/ProfileEditController.swift | 4 ++-- .../ProfileEdit/Main/ProfileEditReactor.swift | 2 +- .../Main/View/ProfileEditView.swift | 14 +++++------ .../MyPage/Recent/View/MyPageRecentView.swift | 2 +- .../MyPage/Terms/MyPageTermsController.swift | 2 +- .../WithdrawlCheckModalController.swift | 2 +- .../CheckModal/WithdrawlCheckModalView.swift | 4 ++-- .../Complete/WithdrawlCompleteView.swift | 2 +- .../View/WithdrawlCheckSectionCell.swift | 4 ++-- .../View/WithdrawlReasonView.swift | 6 ++--- .../CancelableTagSectionCell.swift | 4 ++-- .../SearchTitleSectionCell.swift | 4 ++-- .../Scene/Search/Main/SearchMainView.swift | 6 ++--- .../SignUpCompleteController.swift | 4 ++-- .../SignUp/Step2/SignUpStep2Reactor.swift | 2 +- .../Scene/SignUp/Step2/SignUpStep2View.swift | 6 ++--- .../View/TagSection/TagSectionCell.swift | 4 ++-- .../TermsDetail/TermsDetailController.swift | 2 +- .../TabbarController/TabbarController.swift | 4 ++-- .../Utills/ToastMaker/BookMarkToastView.swift | 6 ++--- .../Utills/ToastMaker/ToastView.swift | 2 +- 89 files changed, 214 insertions(+), 230 deletions(-) delete mode 100644 Poppool/Poppool/Presentation/Extension/Optional+.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 74fce4bf..3e794526 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -354,7 +354,6 @@ 08DC620B2CF8AE0F002A2F44 /* ImageBannerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC620A2CF8AE0F002A2F44 /* ImageBannerSection.swift */; }; 08DC620D2CF8AE16002A2F44 /* ImageBannerSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC620C2CF8AE16002A2F44 /* ImageBannerSectionCell.swift */; }; 08DC62112CF8B446002A2F44 /* SortedRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62102CF8B446002A2F44 /* SortedRequestDTO.swift */; }; - 08DC62132CF8B833002A2F44 /* Optional+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62122CF8B833002A2F44 /* Optional+.swift */; }; 08DE8A0D2D5236840049BCAC /* PutCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A0C2D5236840049BCAC /* PutCommentRequestDTO.swift */; }; 08DE8A102D5255110049BCAC /* DetailEmptyCommetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A0F2D5255110049BCAC /* DetailEmptyCommetSection.swift */; }; 08DE8A122D5255180049BCAC /* DetailEmptyCommetSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A112D5255180049BCAC /* DetailEmptyCommetSectionCell.swift */; }; @@ -821,7 +820,6 @@ 08DC620A2CF8AE0F002A2F44 /* ImageBannerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerSection.swift; sourceTree = ""; }; 08DC620C2CF8AE16002A2F44 /* ImageBannerSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerSectionCell.swift; sourceTree = ""; }; 08DC62102CF8B446002A2F44 /* SortedRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortedRequestDTO.swift; sourceTree = ""; }; - 08DC62122CF8B833002A2F44 /* Optional+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+.swift"; sourceTree = ""; }; 08DE8A0C2D5236840049BCAC /* PutCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutCommentRequestDTO.swift; sourceTree = ""; }; 08DE8A0F2D5255110049BCAC /* DetailEmptyCommetSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEmptyCommetSection.swift; sourceTree = ""; }; 08DE8A112D5255180049BCAC /* DetailEmptyCommetSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEmptyCommetSectionCell.swift; sourceTree = ""; }; @@ -2325,7 +2323,6 @@ 08B191362CF366670057BC04 /* UITableViewCell+.swift */, 08B1913A2CF366A00057BC04 /* UIApplication+.swift */, 08B191772CF442230057BC04 /* UIImage+.swift */, - 08DC62122CF8B833002A2F44 /* Optional+.swift */, 0841BAAB2CFA35F300049E31 /* UILabel+.swift */, 0841BABD2CFB5AA600049E31 /* Date?+.swift */, 086DD8C72CFDEA9200B97D3B /* UIView+.swift */, @@ -3332,7 +3329,6 @@ 0899524D2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift in Sources */, 081898962D2965C90067BF01 /* ProfileEditView.swift in Sources */, 083C86602D0EC496003F441C /* InstaCommentAddView.swift in Sources */, - 08DC62132CF8B833002A2F44 /* Optional+.swift in Sources */, 081898F02D33A3A30067BF01 /* MyCommentSortedModalReactor.swift in Sources */, 081898E42D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift in Sources */, 088DE25D2D145E3A0030FA9E /* DetailSimilarSection.swift in Sources */, diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift index 02b18df9..5704cf9b 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift @@ -20,6 +20,15 @@ struct GetMyProfileResponseDTO: Decodable { extension GetMyProfileResponseDTO { func toDomain() -> GetMyProfileResponse { - return .init(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro, gender: gender, age: age, interestCategoryList: interestCategoryList.map { $0.toDomain() }) + return .init( + profileImageUrl: profileImageUrl, + nickname: nickname, + email: email, + instagramId: instagramId, + intro: intro, + gender: gender, + age: age, + interestCategoryList: interestCategoryList.map { $0.toDomain() } + ) } } diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift index 643a0e1f..a7fa9517 100644 --- a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift +++ b/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift @@ -1,10 +1,3 @@ -// -// ProviderImpl.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/16/24. -// - import Alamofire import Foundation import RxSwift @@ -50,9 +43,13 @@ final class ProviderImpl: Provider { case .success(let data): // 빈 응답 처리 if R.self == EmptyResponse.self && data.isEmpty { - observer.onNext(EmptyResponse() as! R) - observer.onCompleted() - return + if let response = EmptyResponse() as? R { + observer.onNext(response) + observer.onCompleted() + return + } else { + observer.onError(NetworkError.decodeError) + } } do { // JSON 디코딩 diff --git a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 88d37c32..b23047dc 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -93,7 +93,7 @@ final class AdminBottomSheetView: UIView { let button = PPButton( style: .secondary, text: "초기화", - font: .KorFont(style: .medium, size: 16), + font: .korFont(style: .medium, size: 16), cornerRadius: 4 ) button.isEnabled = false @@ -111,7 +111,7 @@ final class AdminBottomSheetView: UIView { style: .primary, text: "옵션저장", disabledText: "옵션저장", - font: .KorFont(style: .medium, size: 16), + font: .korFont(style: .medium, size: 16), cornerRadius: 4 ) button.isEnabled = false diff --git a/Poppool/Poppool/Presentation/Components/PPButton.swift b/Poppool/Poppool/Presentation/Components/PPButton.swift index 5d446403..5d99f435 100644 --- a/Poppool/Poppool/Presentation/Components/PPButton.swift +++ b/Poppool/Poppool/Presentation/Components/PPButton.swift @@ -73,7 +73,7 @@ class PPButton: UIButton { style: ButtonStyle, text: String, disabledText: String = "", - font: UIFont? = .KorFont(style: .medium, size: 16), + font: UIFont? = .korFont(style: .medium, size: 16), cornerRadius: CGFloat = 4 ) { super.init(frame: .zero) diff --git a/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift b/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift index eac352f6..4e1bd069 100644 --- a/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift +++ b/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift @@ -22,7 +22,7 @@ final class PPCancelHeaderView: UIView { let cancelButton: UIButton = { let button = UIButton(type: .system) button.setTitle("취소", for: .normal) - button.titleLabel?.font = .KorFont(style: .regular, size: 14) + button.titleLabel?.font = .korFont(style: .regular, size: 14) button.setTitleColor(.black, for: .normal) return button }() diff --git a/Poppool/Poppool/Presentation/Components/PPLabel.swift b/Poppool/Poppool/Presentation/Components/PPLabel.swift index 25f16a0d..614638ac 100644 --- a/Poppool/Poppool/Presentation/Components/PPLabel.swift +++ b/Poppool/Poppool/Presentation/Components/PPLabel.swift @@ -16,7 +16,7 @@ class PPLabel: UILabel { lineHeight: CGFloat = 1.2 ) { super.init(frame: .zero) - self.font = .KorFont(style: style, size: fontSize) + self.font = .korFont(style: style, size: fontSize) let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = lineHeight self.attributedText = NSMutableAttributedString( diff --git a/Poppool/Poppool/Presentation/Components/PPPicker.swift b/Poppool/Poppool/Presentation/Components/PPPicker.swift index 82fa75fc..acaa9456 100644 --- a/Poppool/Poppool/Presentation/Components/PPPicker.swift +++ b/Poppool/Poppool/Presentation/Components/PPPicker.swift @@ -97,10 +97,10 @@ extension PPPicker: UIPickerViewDelegate, UIPickerViewDataSource { let label = UILabel() label.text = components[row] label.textAlignment = .center - label.font = .KorFont(style: .medium, size: 16) + label.font = .korFont(style: .medium, size: 16) DispatchQueue.main.async { if let label = pickerView.view(forRow: row, forComponent: component) as? UILabel { - label.font = .KorFont(style: .bold, size: 18) + label.font = .korFont(style: .bold, size: 18) } } pickerView.subviews[1].isHidden = true diff --git a/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift b/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift index eb91144d..f8589886 100644 --- a/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift +++ b/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift @@ -20,7 +20,7 @@ final class PPReturnHeaderView: UIView { let headerLabel: UILabel = { let label = UILabel() - label.font = .KorFont(style: .regular, size: 15) + label.font = .korFont(style: .regular, size: 15) label.textColor = .g1000 return label }() diff --git a/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift b/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift index 7566851e..e9cde785 100644 --- a/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift +++ b/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift @@ -85,8 +85,8 @@ private extension PPSegmentedControl { } } self.selectedSegmentTintColor = .blu500 - setFont(color: .w100, font: .KorFont(style: .bold, size: 15), state: .selected) - setFont(color: .g400, font: .KorFont(style: .medium, size: 15), state: .normal) + setFont(color: .w100, font: .korFont(style: .bold, size: 15), state: .selected) + setFont(color: .g400, font: .korFont(style: .medium, size: 15), state: .normal) case .base: // background color 변경이 g50값으로 변경이 되지 않아 subview에 접근하여 layer를 Hidden처리 하고 새로운 뷰를 덮어씌워서 색상을 적용 for (index, view) in self.subviews.enumerated() { @@ -100,8 +100,8 @@ private extension PPSegmentedControl { } } self.selectedSegmentTintColor = .blu500 - setFont(color: .w100, font: .KorFont(style: .bold, size: 15), state: .selected) - setFont(color: .g400, font: .KorFont(style: .medium, size: 14), state: .normal) + setFont(color: .w100, font: .korFont(style: .bold, size: 15), state: .selected) + setFont(color: .g400, font: .korFont(style: .medium, size: 14), state: .normal) case .tab: self.clipsToBounds = false self.setBackgroundImage(emptyImage, for: .normal, barMetrics: .default) @@ -118,8 +118,8 @@ private extension PPSegmentedControl { make.height.equalTo(2) make.bottom.equalTo(bottomLineView.snp.bottom) } - setFont(color: .blu500, font: .KorFont(style: .bold, size: 15), state: .selected) - setFont(color: .g400, font: .KorFont(style: .medium, size: 15), state: .normal) + setFont(color: .blu500, font: .korFont(style: .bold, size: 15), state: .selected) + setFont(color: .g400, font: .korFont(style: .medium, size: 15), state: .normal) } } diff --git a/Poppool/Poppool/Presentation/Extension/Optional+.swift b/Poppool/Poppool/Presentation/Extension/Optional+.swift deleted file mode 100644 index 9cb9f785..00000000 --- a/Poppool/Poppool/Presentation/Extension/Optional+.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// Optional+.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - -import Foundation - -extension Optional where Wrapped: Collection { - var orEmpty: Wrapped { - return self ?? [] as! Wrapped - } -} diff --git a/Poppool/Poppool/Presentation/Extension/UIFont+.swift b/Poppool/Poppool/Presentation/Extension/UIFont+.swift index 098bc0da..75bdb25b 100644 --- a/Poppool/Poppool/Presentation/Extension/UIFont+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIFont+.swift @@ -1,20 +1,12 @@ -// -// UIFont+.swift -// PopPool -// -// Created by Porori on 6/20/24. -// - import Foundation import UIKit extension UIFont { - - static func KorFont(style: FontStyle, size: CGFloat) -> UIFont? { + static func korFont(style: FontStyle, size: CGFloat) -> UIFont? { return UIFont(name: "GothicA1\(style.rawValue)", size: size) } - static func EngFont(style: FontStyle, size: CGFloat) -> UIFont? { + static func engFont(style: FontStyle, size: CGFloat) -> UIFont? { return UIFont(name: "Poppins\(style.rawValue)", size: size) } diff --git a/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift b/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift index 4362eb92..1b5d50da 100644 --- a/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift +++ b/Poppool/Poppool/Presentation/Map/Common/LocationPermissionBottomSheet.swift @@ -10,7 +10,7 @@ final class LocationPermissionBottomSheet: UIViewController { private let titleLabel: UILabel = { let label = UILabel() label.text = "내 위치를 중심으로\n보기 위한 준비가 필요해요" - label.font = UIFont.KorFont(style: .bold, size: 18) + label.font = UIFont.korFont(style: .bold, size: 18) label.textColor = .g1000 label.numberOfLines = 2 label.textAlignment = .center @@ -21,7 +21,7 @@ final class LocationPermissionBottomSheet: UIViewController { private let descriptionLabel: UILabel = { let label = UILabel() label.text = "설정 > 위치 권한을 허용하신 후에\n내 주변의 다양한 팝업스토어를 볼 수 있어요." - label.font = UIFont.KorFont(style: .regular, size: 14) + label.font = UIFont.korFont(style: .regular, size: 14) label.textColor = .g600 label.numberOfLines = 2 label.textAlignment = .center @@ -33,7 +33,7 @@ final class LocationPermissionBottomSheet: UIViewController { let button = UIButton() button.setTitle("취소", for: .normal) button.setTitleColor(.g600, for: .normal) - button.titleLabel?.font = UIFont.KorFont(style: .medium, size: 16) + button.titleLabel?.font = UIFont.korFont(style: .medium, size: 16) button.backgroundColor = .g50 button.layer.cornerRadius = 10 return button @@ -44,7 +44,7 @@ final class LocationPermissionBottomSheet: UIViewController { let button = UIButton() button.setTitle("권한설정", for: .normal) button.setTitleColor(.white, for: .normal) - button.titleLabel?.font = UIFont.KorFont(style: .medium, size: 16) + button.titleLabel?.font = UIFont.korFont(style: .medium, size: 16) button.backgroundColor = .blu500 button.layer.cornerRadius = 10 return button diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift index 40f33be0..964e5995 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/BalloonChipCell.swift @@ -8,7 +8,7 @@ final class BalloonChipCell: UICollectionViewCell { let button = PPButton( style: .secondary, text: "", - font: .KorFont(style: .medium, size: 11), + font: .korFont(style: .medium, size: 11), cornerRadius: 15 ) @@ -45,7 +45,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setBackgroundColor(.blu500, for: .normal) button.setTitleColor(.white, for: .normal) button.layer.borderWidth = 0 - button.titleLabel?.font = .KorFont(style: .bold, size: 11) + button.titleLabel?.font = .korFont(style: .bold, size: 11) } else { button.setImage(nil, for: .normal) @@ -56,7 +56,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setTitleColor(.g400, for: .normal) button.layer.borderWidth = 1 button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .KorFont(style: .medium, size: 11) + button.titleLabel?.font = .korFont(style: .medium, size: 11) } } diff --git a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift index c9a288cb..b7414a4b 100644 --- a/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/Poppool/Presentation/Map/FillterSheetView/FilterBottomSheetView.swift @@ -305,7 +305,7 @@ final class FilterBottomSheetView: UIView { let button = PPButton( style: .secondary, text: title, - font: .KorFont(style: .medium, size: 13), + font: .korFont(style: .medium, size: 13), cornerRadius: 18 ) button.setBackgroundColor(.w100, for: .normal) @@ -331,12 +331,12 @@ final class FilterBottomSheetView: UIView { button.setBackgroundColor(.blu500, for: .normal) button.setTitleColor(.w100, for: .normal) button.layer.borderWidth = 0 - button.titleLabel?.font = .KorFont(style: .bold, size: 13) + button.titleLabel?.font = .korFont(style: .bold, size: 13) } else { button.setBackgroundColor(.w100, for: .normal) button.setTitleColor(.g400, for: .normal) button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .KorFont(style: .medium, size: 13) + button.titleLabel?.font = .korFont(style: .medium, size: 13) button.layer.borderWidth = 1 } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift index 4196ffe8..49d47a25 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift @@ -92,7 +92,7 @@ extension CommentDetailController { owner.mainView.profileView.dateLabel.text = state.commentData.date owner.mainView.profileView.nickNameLabel.text = state.commentData.nickName owner.mainView.profileView.profileImageView.setPPImage(path: state.commentData.profileImagePath) - owner.mainView.likeButtonTitleLabel.setLineHeightText(text: "도움돼요 \(state.commentData.likeCount)", font: .KorFont(style: .medium, size: 13)) + owner.mainView.likeButtonTitleLabel.setLineHeightText(text: "도움돼요 \(state.commentData.likeCount)", font: .korFont(style: .medium, size: 13)) if state.commentData.isLike { owner.mainView.likeButtonImageView.image = UIImage(named: "icon_like_blue") owner.mainView.likeButtonTitleLabel.textColor = .blu500 diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift index 0d814d0a..43237524 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift @@ -49,6 +49,6 @@ extension CommentDetailContentSectionCell: Inputable { } func injection(with input: Input) { - contentLabel.setLineHeightText(text: input.content, font: .KorFont(style: .medium, size: 13)) + contentLabel.setLineHeightText(text: input.content, font: .korFont(style: .medium, size: 13)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift index 93153e71..a6034782 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift @@ -14,7 +14,7 @@ final class CommentMyMenuView: UIView { // MARK: - Components let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) - label.setLineHeightText(text: "내가 작성한 코멘트", font: .KorFont(style: .bold, size: 18)) + label.setLineHeightText(text: "내가 작성한 코멘트", font: .korFont(style: .bold, size: 18)) return label }() @@ -28,7 +28,7 @@ final class CommentMyMenuView: UIView { let button = UIButton() button.setTitle("코멘트 삭제하기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() @@ -43,7 +43,7 @@ final class CommentMyMenuView: UIView { let button = UIButton() button.setTitle("코멘트 수정하기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift index 190de3ae..41bbe402 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift @@ -70,7 +70,7 @@ extension CommentUserInfoController { .subscribe { (owner, state) in owner.mainView.titleLabel.setLineHeightText( text: "\(state.nickName ?? "")님에 대해 더 알아보기", - font: .KorFont(style: .bold, size: 18) + font: .korFont(style: .bold, size: 18) ) } .disposed(by: disposeBag) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift index 0ee09c62..f3a6927e 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift @@ -14,7 +14,7 @@ final class CommentUserInfoView: UIView { // MARK: - Components let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) - label.setLineHeightText(text: "님에 대해 더 알아보기", font: .KorFont(style: .bold, size: 18)) + label.setLineHeightText(text: "님에 대해 더 알아보기", font: .korFont(style: .bold, size: 18)) return label }() @@ -28,7 +28,7 @@ final class CommentUserInfoView: UIView { let button = UIButton() button.setTitle("코멘트를 작성한 팝업 모두보기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() @@ -43,7 +43,7 @@ final class CommentUserInfoView: UIView { let button = UIButton() button.setTitle("이 유저 차단하기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift index f8b33c01..da43fc9a 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift @@ -49,6 +49,6 @@ extension CommentListTitleSectionCell: Inputable { } func injection(with input: Input) { - countLabel.setLineHeightText(text: "총 \(input.count)\(input.unit)", font: .KorFont(style: .regular, size: 13)) + countLabel.setLineHeightText(text: "총 \(input.count)\(input.unit)", font: .korFont(style: .regular, size: 13)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift index 7c41e5bd..e77d939b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift @@ -14,7 +14,7 @@ final class CommentSelectedView: UIView { // MARK: - Components private let titleLabel: PPLabel = { let label = PPLabel(style: .bold, fontSize: 18) - label.setLineHeightText(text: "코멘트 작성 방법 선택", font: .KorFont(style: .bold, size: 18)) + label.setLineHeightText(text: "코멘트 작성 방법 선택", font: .korFont(style: .bold, size: 18)) return label }() @@ -28,7 +28,7 @@ final class CommentSelectedView: UIView { let button = UIButton() button.setTitle("일반 코멘트 작성하기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() @@ -43,7 +43,7 @@ final class CommentSelectedView: UIView { let button = UIButton() button.setTitle("인스타그램 연동 코멘트 작성하기", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 15) + button.titleLabel?.font = .korFont(style: .medium, size: 15) button.contentHorizontalAlignment = .leading return button }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift index 1c4871af..75166c4d 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift @@ -59,7 +59,7 @@ extension CommentUserBlockController { .subscribe { (owner, state) in owner.mainView.titleLabel.setLineHeightText( text: "\(state.nickName ?? "")님을 차단할까요?", - font: .KorFont(style: .bold, size: 18) + font: .korFont(style: .bold, size: 18) ) } .disposed(by: disposeBag) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift index e05674c3..c9d0e127 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift @@ -59,7 +59,7 @@ final class InstaCommentAddReactor: Reactor { { let title = "아래 인스타그램 열기\n버튼을 터치해 앱 열기" let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.KorFont(style: .bold, size: 20)! + let koreanFont = UIFont.korFont(style: .bold, size: 20)! attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "인스타그램 열기")) let paragraphStyle = NSMutableParagraphStyle() @@ -70,7 +70,7 @@ final class InstaCommentAddReactor: Reactor { { let title = "원하는 피드의 이미지로 이동 후\n공유하기 > 링크복사 터치하기" let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.KorFont(style: .bold, size: 20)! + let koreanFont = UIFont.korFont(style: .bold, size: 20)! attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "공유하기 > 링크복사")) let paragraphStyle = NSMutableParagraphStyle() @@ -81,7 +81,7 @@ final class InstaCommentAddReactor: Reactor { { let title = "아래 이미지 영역을 터치해\n팝풀 앱으로 돌아오기" let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.KorFont(style: .bold, size: 20)! + let koreanFont = UIFont.korFont(style: .bold, size: 20)! attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "팝풀 앱")) let paragraphStyle = NSMutableParagraphStyle() @@ -92,7 +92,7 @@ final class InstaCommentAddReactor: Reactor { { let title = "복사된 인스타 피드 이미지와\n함께할 글을 입력 후 등록하기" let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.KorFont(style: .bold, size: 20)! + let koreanFont = UIFont.korFont(style: .bold, size: 20)! attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "글을 입력 후 등록")) let paragraphStyle = NSMutableParagraphStyle() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift index 9e7fc499..c7c9faa0 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift @@ -14,7 +14,7 @@ final class InstaCommentAddView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .korFont(style: .regular, size: 15)) return view }() @@ -27,10 +27,10 @@ final class InstaCommentAddView: UIView { let title = "Instagram 열기" let attributedTitle = NSMutableAttributedString(string: title) - let englishFont = UIFont.EngFont(style: .medium, size: 15)! + let englishFont = UIFont.engFont(style: .medium, size: 15)! attributedTitle.addAttribute(.font, value: englishFont, range: (title as NSString).range(of: "Instagram")) - let koreanFont = UIFont.KorFont(style: .medium, size: 15)! + let koreanFont = UIFont.korFont(style: .medium, size: 15)! attributedTitle.addAttribute(.font, value: koreanFont, range: (title as NSString).range(of: "열기")) button.setAttributedTitle(attributedTitle, for: .normal) diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift index 508c459f..5a4bff39 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift @@ -26,7 +26,7 @@ final class InstaGuideChildSectionCell: UICollectionViewCell { private let indexLabel: UILabel = { let label = UILabel() - label.font = .EngFont(style: .medium, size: 16) + label.font = .engFont(style: .medium, size: 16) label.textColor = .w100 label.textAlignment = .center return label diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift index 910cf5f2..dd8cd85f 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift @@ -50,6 +50,6 @@ extension AddCommentDescriptionSectionCell: Inputable { } func injection(with input: Input) { - descriptionLabel.setLineHeightText(text: input.description, font: .KorFont(style: .regular, size: 13)) + descriptionLabel.setLineHeightText(text: input.description, font: .korFont(style: .regular, size: 13)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift index b264381b..b81b98f3 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift @@ -21,7 +21,7 @@ final class AddCommentSectionCell: UICollectionViewCell { let view = UITextView() view.textContainerInset = .zero view.textContainer.lineFragmentPadding = 0 - view.font = .KorFont(style: .medium, size: 14) + view.font = .korFont(style: .medium, size: 14) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift index c71411bf..b5847ee0 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift @@ -47,6 +47,6 @@ extension AddCommentTitleSectionCell: Inputable { } func injection(with input: Input) { - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 16)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift index 1913249a..819e9200 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift @@ -15,7 +15,7 @@ final class NormalCommentAddView: UIView { let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift index b3d9d1b2..ee06753a 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift @@ -15,7 +15,7 @@ final class NormalCommentEditView: UIView { let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "코멘트 수정하기", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "코멘트 수정하기", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift index b086e771..7f1a0770 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift @@ -121,9 +121,9 @@ extension OtherUserCommentSectionCell: Inputable { func injection(with input: Input) { imageView.setPPImage(path: input.imagePath) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 11)) - contentLabel.setLineHeightText(text: input.comment, font: .KorFont(style: .medium, size: 11)) - dateLabel.setLineHeightText(text: input.date, font: .KorFont(style: .regular, size: 11)) - likeCountLabel.setLineHeightText(text: "\(input.likeCount)", font: .KorFont(style: .regular, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11)) + contentLabel.setLineHeightText(text: input.comment, font: .korFont(style: .medium, size: 11)) + dateLabel.setLineHeightText(text: input.date, font: .korFont(style: .regular, size: 11)) + likeCountLabel.setLineHeightText(text: "\(input.likeCount)", font: .korFont(style: .regular, size: 11)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift index 89715fd4..0cd89426 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift @@ -14,7 +14,7 @@ final class OtherUserCommentView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "코멘트 작성 팝업", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "코멘트 작성 팝업", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift index d6f3535e..a8a17fff 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift @@ -204,12 +204,12 @@ extension DetailController: UICollectionViewDelegate, UICollectionViewDataSource .subscribe { (collectionView, _) in cell.isOpen.toggle() if cell.isOpen { - cell.buttonTitleLabel.setLineHeightText(text: "닫기", font: .KorFont(style: .medium, size: 13)) + cell.buttonTitleLabel.setLineHeightText(text: "닫기", font: .korFont(style: .medium, size: 13)) cell.contentLabel.numberOfLines = 0 cell.buttonImageView.image = UIImage(named: "icon_dropdown_top_gray") } else { cell.contentLabel.numberOfLines = 3 - cell.buttonTitleLabel.setLineHeightText(text: "더보기", font: .KorFont(style: .medium, size: 13)) + cell.buttonTitleLabel.setLineHeightText(text: "더보기", font: .korFont(style: .medium, size: 13)) cell.buttonImageView.image = UIImage(named: "icon_dropdown_bottom_gray") } collectionView.reloadData() diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 74097657..964cdb5f 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -1,10 +1,3 @@ -// -// DetailReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/9/24. -// - import UIKit import LinkPresentation @@ -26,7 +19,7 @@ final class DetailReactor: Reactor { case commentMenuButtonTapped(controller: BaseViewController, indexPath: IndexPath) case commentDetailButtonTapped(controller: BaseViewController, indexPath: IndexPath) case commentLikeButtonTapped(indexPath: IndexPath) - case commentImageTapped(controller: BaseViewController, cellRow: Int, ImageRow: Int) + case commentImageTapped(controller: BaseViewController, cellRow: Int, imageRow: Int) case similarSectionTapped(controller: BaseViewController, indexPath: IndexPath) case backButtonTapped(controller: BaseViewController) case loginButtonTapped(controller: BaseViewController) @@ -44,7 +37,7 @@ final class DetailReactor: Reactor { case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) case moveToRecentScene(controller: BaseViewController) case moveToLoginScene(controller: BaseViewController) - case moveToImageDetailScene(controller: BaseViewController, cellRow: Int, ImageRow: Int) + case moveToImageDetailScene(controller: BaseViewController, cellRow: Int, imageRow: Int) } private var commentButtonIsEnable: Bool = false @@ -135,8 +128,8 @@ final class DetailReactor: Reactor { return Observable.just(.moveToRecentScene(controller: controller)) case .loginButtonTapped(let controller): return Observable.just(.moveToLoginScene(controller: controller)) - case .commentImageTapped(let controller, let cellRow, let ImageRow): - return Observable.just(.moveToImageDetailScene(controller: controller, cellRow: cellRow, ImageRow: ImageRow)) + case .commentImageTapped(let controller, let cellRow, let imageRow): + return Observable.just(.moveToImageDetailScene(controller: controller, cellRow: cellRow, imageRow: imageRow)) } } @@ -212,8 +205,8 @@ final class DetailReactor: Reactor { let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) - case .moveToImageDetailScene(let controller, let cellRow, let ImageRow): - let imagePath = commentSection.inputDataList[cellRow].imageList[ImageRow] + case .moveToImageDetailScene(let controller, let cellRow, let imageRow): + let imagePath = commentSection.inputDataList[cellRow].imageList[imageRow] let nextController = ImageDetailController() nextController.reactor = ImageDetailReactor(imagePath: imagePath) nextController.modalPresentationStyle = .overCurrentContext @@ -221,7 +214,10 @@ final class DetailReactor: Reactor { } return newState } +} +// MARK: Section function +extension DetailReactor { func getSection() -> [any Sectionable] { if similarSection.inputDataList.isEmpty { if commentSection.inputDataList.isEmpty { @@ -304,7 +300,6 @@ final class DetailReactor: Reactor { ] } } - } func setContent() -> Observable { diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift index b7fceb6c..4251a119 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift @@ -112,7 +112,7 @@ final class DetailCommentSectionCell: UICollectionViewCell { let loginButton: UIButton = { let button = UIButton() button.setTitle("로그인하고 후기보기", for: .normal) - button.titleLabel?.font = .KorFont(style: .medium, size: 13) + button.titleLabel?.font = .korFont(style: .medium, size: 13) button.setTitleColor(.w100, for: .normal) button.layer.cornerRadius = 4 button.backgroundColor = .blu500 @@ -282,10 +282,10 @@ extension DetailCommentSectionCell: Inputable { let comment = input.comment ?? "" profileView.profileImageView.setPPImage(path: input.profileImagePath) - profileView.nickNameLabel.setLineHeightText(text: input.nickName, font: .KorFont(style: .bold, size: 13)) - profileView.dateLabel.setLineHeightText(text: input.date, font: .KorFont(style: .regular, size: 12)) - contentLabel.setLineHeightText(text: input.comment, font: .KorFont(style: .regular, size: 13)) - likeButtonTitleLabel.setLineHeightText(text: "도움돼요 \(input.likeCount)", font: .KorFont(style: .regular, size: 13)) + profileView.nickNameLabel.setLineHeightText(text: input.nickName, font: .korFont(style: .bold, size: 13)) + profileView.dateLabel.setLineHeightText(text: input.date, font: .korFont(style: .regular, size: 12)) + contentLabel.setLineHeightText(text: input.comment, font: .korFont(style: .regular, size: 13)) + likeButtonTitleLabel.setLineHeightText(text: "도움돼요 \(input.likeCount)", font: .korFont(style: .regular, size: 13)) if input.isLike { likeButtonImageView.image = UIImage(named: "icon_like_blue") likeButtonTitleLabel.textColor = .blu500 @@ -326,20 +326,20 @@ extension DetailCommentSectionCell: Inputable { // 기본 스타일 (폰트, 색상 등) let normalAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.KorFont(style: .regular, size: 14)!, + .font: UIFont.korFont(style: .regular, size: 14)!, .foregroundColor: UIColor.g1000, .paragraphStyle: paragraphStyle ] // 스타일을 다르게 할 부분 (팝업스토어명, 생생한 후기) let popupStoreAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.KorFont(style: .bold, size: 14)!, // 다른 폰트 스타일 + .font: UIFont.korFont(style: .bold, size: 14)!, // 다른 폰트 스타일 .foregroundColor: UIColor.blu500, // 다른 색상 .paragraphStyle: paragraphStyle ] let reviewAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.KorFont(style: .bold, size: 14)!, // 이탤릭체 + .font: UIFont.korFont(style: .bold, size: 14)!, // 이탤릭체 .foregroundColor: UIColor.g1000, // 다른 색상 .paragraphStyle: paragraphStyle ] diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift index 26024838..f7c37325 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift @@ -28,7 +28,7 @@ final class DetailCommentTitleSectionCell: UICollectionViewCell { let attributedTitle = NSAttributedString( string: "전체보기", attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 + .font: UIFont.korFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 .underlineStyle: NSUnderlineStyle.single.rawValue // 밑줄 스타일 ] ) @@ -82,7 +82,7 @@ extension DetailCommentTitleSectionCell: Inputable { } func injection(with input: Input) { - countLabel.setLineHeightText(text: "총 \(input.commentCount)개", font: .KorFont(style: .regular, size: 13)) + countLabel.setLineHeightText(text: "총 \(input.commentCount)개", font: .korFont(style: .regular, size: 13)) totalViewButton.isHidden = input.buttonIsHidden } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift index e319d2ab..02e98e1c 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift @@ -99,7 +99,7 @@ extension DetailContentSectionCell: Inputable { func injection(with input: Input) { let text = input.content ?? "" - contentLabel.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 13)) + contentLabel.setLineHeightText(text: text, font: .korFont(style: .regular, size: 13)) if text.count >= 68 { dropDownButton.isHidden = false } else { diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift index aa27bcbc..76e43892 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift @@ -27,7 +27,7 @@ final class DetailEmptyCommetSectionCell: UICollectionViewCell { let attributedTitle = NSAttributedString( string: "첫번째 코멘트 남기기", attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 + .font: UIFont.korFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 .underlineStyle: NSUnderlineStyle.single.rawValue // 밑줄 스타일 ] ) diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift index 1ba18382..2a81ca35 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift @@ -165,8 +165,8 @@ extension DetailInfoSectionCell: Inputable { let startTime = input.startTime ?? "?" let endTime = input.endTime ?? "?" - dateLabel.setLineHeightText(text: startDate + " ~ " + endDate, font: .KorFont(style: .regular, size: 14)) - timeLabel.setLineHeightText(text: startTime + " ~ " + endTime, font: .KorFont(style: .regular, size: 14)) - addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 13)) + dateLabel.setLineHeightText(text: startDate + " ~ " + endDate, font: .korFont(style: .regular, size: 14)) + timeLabel.setLineHeightText(text: startTime + " ~ " + endTime, font: .korFont(style: .regular, size: 14)) + addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .regular, size: 13)) } } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift index e4b66e5f..3ca56575 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift @@ -22,7 +22,7 @@ final class DetailSimilarSectionCell: UICollectionViewCell { private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) - label.font = .EngFont(style: .regular, size: 11) + label.font = .engFont(style: .regular, size: 11) label.textColor = .g400 return label }() @@ -142,8 +142,8 @@ extension DetailSimilarSectionCell: Inputable { func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) - dateLabel.setLineHeightText(text: "~" + date, font: .EngFont(style: .regular, size: 11)) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 12)) + dateLabel.setLineHeightText(text: "~" + date, font: .engFont(style: .regular, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 12)) if let isBookMark = input.isBookMark { bookMarkButton.isHidden = false if isBookMark { diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift index 2f7f7767..907df869 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift @@ -82,7 +82,7 @@ extension DetailTitleSectionCell: Inputable { func injection(with input: Input) { let bookMarkImage = input.isBookMark ? UIImage(named: "icon_bookmark_blue") : UIImage(named: "icon_bookmark_gray") bookMarkButton.setImage(bookMarkImage, for: .normal) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 18)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 18)) bookMarkButton.isHidden = !input.isLogin } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift index 4759ee2c..83fadfc2 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift @@ -70,7 +70,16 @@ final class HomeReactor: Reactor { private var popularTitleSection = HomeTitleSection(inputDataList: [ .init(blueText: "팝풀이", topSubText: "들은 지금 이런", bottomText: "팝업에 가장 관심있어요", backgroundColor: .g700, textColor: .w100) ]) - private var popularSection = HomePopularCardSection(inputDataList: [], decorationItems: [SectionDecorationItem(elementKind: "BackgroundView", reusableView: SectionBackGroundDecorationView(), viewInput: .init(backgroundColor: .g700))]) + private var popularSection = HomePopularCardSection( + inputDataList: [], + decorationItems: [ + SectionDecorationItem( + elementKind: "BackgroundView", + reusableView: SectionBackGroundDecorationView(), + viewInput: .init(backgroundColor: .g700) + ) + ] + ) private var newTitleSection = HomeTitleSection(inputDataList: [.init(blueText: "제일 먼저", topSubText: "피드 올리는", bottomText: "신규 오픈 팝업")]) private var newSection = HomeCardSection(inputDataList: []) private var spaceClear48Section = SpacingSection(inputDataList: [.init(spacing: 48)]) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 8ca3caf6..14c03267 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -157,11 +157,11 @@ extension HomeCardSectionCell: Inputable { } func injection(with input: Input) { - categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .KorFont(style: .bold, size: 11)) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 14)) - addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .medium, size: 11)) + categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .korFont(style: .bold, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 14)) + addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .medium, size: 11)) let date = input.startDate.toDate().toPPDateString() + " ~ " + input.endDate.toDate().toPPDateString() - dateLabel.setLineHeightText(text: date, font: .KorFont(style: .medium, size: 11)) + dateLabel.setLineHeightText(text: date, font: .korFont(style: .medium, size: 11)) let bookmarkImage = input.isBookmark ? UIImage(named: "icon_bookmark_fill") : UIImage(named: "icon_bookmark") bookmarkButton.setImage(bookmarkImage, for: .normal) imageView.setPPImage(path: input.imagePath) @@ -169,7 +169,7 @@ extension HomeCardSectionCell: Inputable { rankLabel.isHidden = !input.isPopular let rank = input.row ?? 0 - rankLabel.setLineHeightText(text: "\(rank + 1)위", font: .KorFont(style: .medium, size: 11), lineHeight: 1) + rankLabel.setLineHeightText(text: "\(rank + 1)위", font: .korFont(style: .medium, size: 11), lineHeight: 1) rankLabel.textAlignment = .center if rank > 2 { rankLabel.isHidden = true diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index f613e678..9e8f66af 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -134,7 +134,7 @@ extension HomePopularCardSectionCell: Inputable { func injection(with input: Input) { let date = "#\(input.endDate.toDate().toPPDateMonthString())까지 열리는" - dateLabel.setLineHeightText(text: date, font: .KorFont(style: .regular, size: 16)) + dateLabel.setLineHeightText(text: date, font: .korFont(style: .regular, size: 16)) let category = "#\(input.category ?? "")" if let addressArray = input.address?.components(separatedBy: " ") { if addressArray.count > 2 { @@ -144,7 +144,7 @@ extension HomePopularCardSectionCell: Inputable { } categoryLabel.text = category - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 16)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .regular, size: 16)) backGroundImageView.setPPImage(path: input.imagePath) } } diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift index d25f06bf..cf0f58cb 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift @@ -90,9 +90,9 @@ extension HomeTitleSectionCell: Inputable { } func injection(with input: Input) { - blueLabel.setLineHeightText(text: input.blueText, font: .KorFont(style: .bold, size: 16)) - subLabel.setLineHeightText(text: input.topSubText, font: .KorFont(style: .bold, size: 16)) - bottomLabel.setLineHeightText(text: input.bottomText, font: .KorFont(style: .bold, size: 16)) + blueLabel.setLineHeightText(text: input.blueText, font: .korFont(style: .bold, size: 16)) + subLabel.setLineHeightText(text: input.topSubText, font: .korFont(style: .bold, size: 16)) + bottomLabel.setLineHeightText(text: input.bottomText, font: .korFont(style: .bold, size: 16)) contentView.backgroundColor = input.backgroundColor subLabel.textColor = input.textColor bottomLabel.textColor = input.textColor diff --git a/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift index fb04c749..eeebbd5a 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift @@ -46,7 +46,7 @@ final class LastLoginView: UIView { private let notificationLabel: UILabel = { let label = UILabel() - label.font = .KorFont(style: .medium, size: 13) + label.font = .korFont(style: .medium, size: 13) return label }() diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift index 8333b589..437232c9 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift @@ -15,7 +15,7 @@ final class LoginView: UIView { let guestButton: UIButton = { let button = UIButton(type: .system) button.setTitle("둘러보기", for: .normal) - button.titleLabel?.font = .KorFont(style: .regular, size: 14) + button.titleLabel?.font = .korFont(style: .regular, size: 14) button.setTitleColor(.g1000, for: .normal) return button }() @@ -57,7 +57,7 @@ final class LoginView: UIView { let inquiryButton: UIButton = { let button = UIButton(type: .system) button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .KorFont(style: .regular, size: 12) + button.titleLabel?.font = .korFont(style: .regular, size: 12) button.setTitleColor(.g1000, for: .normal) return button }() diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift index 00e54ac7..c1a34e01 100644 --- a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift +++ b/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift @@ -28,7 +28,7 @@ final class SubLoginView: UIView { 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.setLineHeightText(text: "간편하게 SNS 로그인하고\n공감가는 코멘트에 반응해볼까요?\n다른 코멘트를 확인해볼까요?", font: .korFont(style: .bold, size: 16), lineHeight: 1.3) label.numberOfLines = 0 label.textAlignment = .center return label @@ -57,7 +57,7 @@ final class SubLoginView: UIView { let inquiryButton: UIButton = { let button = UIButton(type: .system) button.setTitle("로그인이 어려우신가요?", for: .normal) - button.titleLabel?.font = .KorFont(style: .regular, size: 12) + button.titleLabel?.font = .korFont(style: .regular, size: 12) button.setTitleColor(.g1000, for: .normal) return button }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift index e7380373..dc08a239 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift @@ -86,16 +86,16 @@ extension BlockUserListSectionCell: Inputable { func injection(with input: Input) { profileImageView.setPPImage(path: input.profileImagePath) - nickNameLabel.setLineHeightText(text: input.nickName, font: .KorFont(style: .bold, size: 14)) + nickNameLabel.setLineHeightText(text: input.nickName, font: .korFont(style: .bold, size: 14)) if input.isBlocked { blockButton.setTitle("차단완료", for: .normal) - blockButton.titleLabel?.font = .KorFont(style: .medium, size: 13) + blockButton.titleLabel?.font = .korFont(style: .medium, size: 13) blockButton.backgroundColor = .re600 blockButton.setTitleColor(.w100, for: .normal) blockButton.layer.borderWidth = 0 } else { blockButton.setTitle("차단해제", for: .normal) - blockButton.titleLabel?.font = .KorFont(style: .medium, size: 13) + blockButton.titleLabel?.font = .korFont(style: .medium, size: 13) blockButton.backgroundColor = .w100 blockButton.setTitleColor(.g300, for: .normal) blockButton.layer.borderWidth = 1 diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift index f3fb7ef3..4fb424f1 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift @@ -14,7 +14,7 @@ final class BlockUserManageView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "차단한 사용자 관리", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "차단한 사용자 관리", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift index 5b24ba1c..55e98c2b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift @@ -166,8 +166,8 @@ extension MyPageBookmarkController { owner.mainView.contentCollectionView.isHidden = state.isEmptyCase owner.mainView.emptyLabel.isHidden = !state.isEmptyCase owner.mainView.emptyButton.isHidden = !state.isEmptyCase - owner.mainView.countButtonView.buttonTitleLabel.setLineHeightText(text: state.buttonTitle, font: .KorFont(style: .regular, size: 13)) - owner.mainView.countButtonView.countLabel.setLineHeightText(text: "총 \(state.count)개", font: .KorFont(style: .regular, size: 13)) + owner.mainView.countButtonView.buttonTitleLabel.setLineHeightText(text: state.buttonTitle, font: .korFont(style: .regular, size: 13)) + owner.mainView.countButtonView.countLabel.setLineHeightText(text: "총 \(state.count)개", font: .korFont(style: .regular, size: 13)) if state.buttonTitle != owner.viewType { owner.mainView.contentCollectionView.scrollsToTop = true diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift index 8c5a7f1d..1bf108c0 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift @@ -14,7 +14,7 @@ final class MyPageBookmarkView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "찜한 팝업", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "찜한 팝업", font: .korFont(style: .regular, size: 15)) return view }() @@ -42,7 +42,7 @@ final class MyPageBookmarkView: UIView { let buttonTitle = NSAttributedString( string: "추천 팝업 보러가기", attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, + .font: UIFont.korFont(style: .regular, size: 13)!, .underlineStyle: NSUnderlineStyle.single.rawValue, .foregroundColor: UIColor.g1000 ] diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift index a957011f..33ac884c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift @@ -23,7 +23,7 @@ final class PopUpCardSectionCell: UICollectionViewCell { private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) - label.font = .EngFont(style: .regular, size: 11) + label.font = .engFont(style: .regular, size: 11) label.textColor = .g1000 return label }() @@ -159,10 +159,10 @@ extension PopUpCardSectionCell: Inputable { func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) - dateLabel.setLineHeightText(text: date, font: .EngFont(style: .regular, size: 13)) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) + dateLabel.setLineHeightText(text: date, font: .engFont(style: .regular, size: 13)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 16)) titleLabel.textAlignment = .center - addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 14)) + addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .regular, size: 14)) addressLabel.textAlignment = .center if input.isBookMark { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift index 41ffc78c..c24d05f2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift @@ -25,7 +25,7 @@ final class PopUpCardView: UIView { private let dateLabel: PPLabel = { let label = PPLabel(style: .regular, fontSize: 11) - label.font = .EngFont(style: .regular, size: 11) + label.font = .engFont(style: .regular, size: 11) label.textColor = .g1000 return label }() @@ -158,9 +158,9 @@ extension PopUpCardView: Inputable { func injection(with input: Input) { let date = input.date ?? "" imageView.setPPImage(path: input.imagePath) - dateLabel.setLineHeightText(text: date, font: .EngFont(style: .regular, size: 13)) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) - addressLabel.setLineHeightText(text: input.address, font: .KorFont(style: .regular, size: 14)) + dateLabel.setLineHeightText(text: date, font: .engFont(style: .regular, size: 13)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 16)) + addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .regular, size: 14)) if input.isBookMark { bookMarkButton.setImage(UIImage(named: "icon_bookmark_fill"), for: .normal) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift index 57a7e833..36493d0c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift @@ -23,7 +23,7 @@ final class FAQDropdownSectionCell: UICollectionViewCell { let qLabel: UILabel = { let label = UILabel() - label.setLineHeightText(text: "Q", font: .EngFont(style: .bold, size: 16), lineHeight: 1) + label.setLineHeightText(text: "Q", font: .engFont(style: .bold, size: 16), lineHeight: 1) label.textColor = .blu500 return label }() @@ -112,8 +112,8 @@ extension FAQDropdownSectionCell: Inputable { } func injection(with input: Input) { - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .medium, size: 14)) - dropContentLabel.setLineHeightText(text: input.content, font: .KorFont(style: .regular, size: 14), lineHeight: 1.5) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 14)) + dropContentLabel.setLineHeightText(text: input.content, font: .korFont(style: .regular, size: 14), lineHeight: 1.5) dropContentLabel.lineBreakStrategy = .hangulWordPriority dropContentLabel.textColor = .g600 if input.isOpen { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift index 93a270ec..77680e11 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift @@ -14,7 +14,7 @@ final class FAQView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "고객문의", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "고객문의", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift index 72b73d0a..1415b4f1 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift @@ -102,7 +102,7 @@ extension MyPageCommentSectionCell: Inputable { func injection(with input: Input) { imageView.setPPImage(path: input.popUpImagePath) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .regular, size: 11)) titleLabel.textAlignment = .center if input.isFirstCell { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift index 31d71249..0cd90311 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift @@ -67,7 +67,7 @@ extension MyPageListSectionCell: Inputable { } func injection(with input: Input) { - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .regular, size: 15)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .regular, size: 15)) if input.subTitle == nil { rightImageView.isHidden = false @@ -75,7 +75,7 @@ extension MyPageListSectionCell: Inputable { } else { rightImageView.isHidden = true subTitleLabel.isHidden = false - subTitleLabel.setLineHeightText(text: input.subTitle, font: .KorFont(style: .regular, size: 13)) + subTitleLabel.setLineHeightText(text: input.subTitle, font: .korFont(style: .regular, size: 13)) subTitleLabel.textColor = . blu500 } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift index e6ccae2a..db582281 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift @@ -61,13 +61,13 @@ extension MyPageMyCommentTitleSectionCell: Inputable { } func injection(with input: Input) { - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 16)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 16)) if input.buttonTitle != nil { let buttonTitle = NSAttributedString( string: input.buttonTitle ?? "", attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, + .font: UIFont.korFont(style: .regular, size: 13)!, .underlineStyle: NSUnderlineStyle.single.rawValue, .foregroundColor: UIColor.g600 ] diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift index 642118cf..639252c2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift @@ -75,7 +75,7 @@ final class MyPageProfileSectionCell: UICollectionViewCell { let button = UIButton() button.setTitle("로그인/회원가입", for: .normal) button.backgroundColor = .w10 - button.titleLabel?.font = .KorFont(style: .medium, size: 13) + button.titleLabel?.font = .korFont(style: .medium, size: 13) button.setTitleColor(.w100, for: .normal) button.layer.cornerRadius = 4 return button @@ -223,8 +223,8 @@ extension MyPageProfileSectionCell: Inputable { loginLabel.isHidden = true loginButton.isHidden = true blurView.isHidden = false - nickNameLabel.setLineHeightText(text: input.nickName, font: .KorFont(style: .bold, size: 16)) - descriptionLabel.setLineHeightText(text: input.description ?? "", font: .KorFont(style: .light, size: 11)) + nickNameLabel.setLineHeightText(text: input.nickName, font: .korFont(style: .bold, size: 16)) + descriptionLabel.setLineHeightText(text: input.description ?? "", font: .korFont(style: .light, size: 11)) backGroundImageView.image = nil backGroundImageView.setPPImage(path: input.profileImagePath) profileImageView.setPPImage(path: input.profileImagePath) { [weak self] in diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift index 41fb7421..35d9658e 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift @@ -89,7 +89,7 @@ extension ListCountButtonSectionCell: Inputable { } func injection(with input: Input) { - countLabel.setLineHeightText(text: "총 \(input.count)개", font: .KorFont(style: .regular, size: 13)) - buttonTitleLabel.setLineHeightText(text: input.buttonTitle, font: .KorFont(style: .regular, size: 13)) + countLabel.setLineHeightText(text: "총 \(input.count)개", font: .korFont(style: .regular, size: 13)) + buttonTitleLabel.setLineHeightText(text: input.buttonTitle, font: .korFont(style: .regular, size: 13)) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift index 8d0b9c95..f1cf8955 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift @@ -14,7 +14,7 @@ final class MyCommentView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "내가 코멘트한 팝업", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "내가 코멘트한 팝업", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift index 0c88ef82..f8dfa86a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift @@ -99,9 +99,9 @@ extension MyCommentedPopUpGridSectionCell: Inputable { func injection(with input: Input) { contentImageView.setPPImage(path: input.imageURL) - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 11)) - contentLabel.setLineHeightText(text: input.content, font: .KorFont(style: .medium, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11)) + contentLabel.setLineHeightText(text: input.content, font: .korFont(style: .medium, size: 11)) contentLabel.numberOfLines = 2 - dateLabel.setLineHeightText(text: "\(input.startDate ?? "") ~ \(input.endDate ?? "")", font: .EngFont(style: .regular, size: 11)) + dateLabel.setLineHeightText(text: "\(input.startDate ?? "") ~ \(input.endDate ?? "")", font: .engFont(style: .regular, size: 11)) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift index 535b6b12..678153d9 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift @@ -60,9 +60,9 @@ extension MyPageNoticeDetailController { reactor.state .withUnretained(self) .subscribe { (owner, state) in - owner.mainView.titleLabel.setLineHeightText(text: state.title, font: .KorFont(style: .bold, size: 18)) - owner.mainView.dateLabel.setLineHeightText(text: state.date, font: .EngFont(style: .regular, size: 14)) - owner.mainView.contentLabel.setLineHeightText(text: state.content, font: .KorFont(style: .regular, size: 14)) + owner.mainView.titleLabel.setLineHeightText(text: state.title, font: .korFont(style: .bold, size: 18)) + owner.mainView.dateLabel.setLineHeightText(text: state.date, font: .engFont(style: .regular, size: 14)) + owner.mainView.contentLabel.setLineHeightText(text: state.content, font: .korFont(style: .regular, size: 14)) } .disposed(by: disposeBag) } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift index a4ae7b9d..64125f43 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift @@ -14,7 +14,7 @@ final class MyPageNoticeDetailView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "공지사항", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "공지사항", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift index bb0af74b..874e228c 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift @@ -14,7 +14,7 @@ final class MyPageNoticeView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "공지사항", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "공지사항", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift index c06f9e4a..6708b6a8 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift @@ -73,7 +73,7 @@ extension NoticeListSectionCell: Inputable { } func injection(with input: Input) { - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .medium, size: 14)) - dateLabel.setLineHeightText(text: input.date, font: .EngFont(style: .regular, size: 12)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 14)) + dateLabel.setLineHeightText(text: input.date, font: .engFont(style: .regular, size: 12)) } } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index a6cee295..95801cc2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -162,10 +162,10 @@ extension ProfileEditController { categoryTitle = "" } owner.mainView.categoryButton.subTitleLabel - .setLineHeightText(text: categoryTitle, font: .KorFont(style: .regular, size: 13), lineHeight: 1) + .setLineHeightText(text: categoryTitle, font: .korFont(style: .regular, size: 13), lineHeight: 1) let userInfoTitle = "\(originProfileData.gender ?? "")・\(originProfileData.age)세" owner.mainView.infoButton.subTitleLabel - .setLineHeightText(text: userInfoTitle, font: .KorFont(style: .regular, size: 13), lineHeight: 1) + .setLineHeightText(text: userInfoTitle, font: .korFont(style: .regular, size: 13), lineHeight: 1) // NickName TextField 설정 owner.mainView.nickNameTextFieldTrailingView.layer.borderColor = state.nickNameState.borderColor?.cgColor diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index fef97600..5719a881 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -241,7 +241,7 @@ final class ProfileEditReactor: Reactor { // kor and end Check let pattern = "^[가-힣a-zA-Z\\s]+$" // 허용하는 문자만 검사 - let regex = try! NSRegularExpression(pattern: pattern) + guard let regex = try? NSRegularExpression(pattern: pattern) else { return .empty } let range = NSRange(location: 0, length: text.utf16.count) if regex.firstMatch(in: text, options: [], range: range) == nil { return isActive ? .korAndEngActive : .korAndEng } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift index c2adb140..ea14a361 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift @@ -14,7 +14,7 @@ final class ProfileEditView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "프로필 설정", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "프로필 설정", font: .korFont(style: .regular, size: 15)) return view }() let saveButton: PPButton = { @@ -69,7 +69,7 @@ final class ProfileEditView: UIView { let nickNameTextField: UITextField = { let textField = UITextField() textField.placeholder = "별명을 입력해주세요" - textField.font = .KorFont(style: .medium, size: 14) + textField.font = .korFont(style: .medium, size: 14) return textField }() let nickNameClearButton: UIButton = { @@ -96,7 +96,7 @@ final class ProfileEditView: UIView { let attributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g1000 // 텍스트 색상 ] @@ -104,7 +104,7 @@ final class ProfileEditView: UIView { let disabledAttributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g300 // 텍스트 색상 ] @@ -130,7 +130,7 @@ final class ProfileEditView: UIView { let view = UITextView() view.textContainerInset = .zero view.contentInset = .zero - view.font = .KorFont(style: .medium, size: 14) + view.font = .korFont(style: .medium, size: 14) return view }() let introTextCountLabel: PPLabel = { @@ -154,13 +154,13 @@ final class ProfileEditView: UIView { let categoryButton: ProfileEditListButton = { let button = ProfileEditListButton() - button.mainTitleLabel.setLineHeightText(text: "관심 카테고리", font: .KorFont(style: .regular, size: 15)) + button.mainTitleLabel.setLineHeightText(text: "관심 카테고리", font: .korFont(style: .regular, size: 15)) return button }() let infoButton: ProfileEditListButton = { let button = ProfileEditListButton() - button.mainTitleLabel.setLineHeightText(text: "사용자 정보", font: .KorFont(style: .regular, size: 15)) + button.mainTitleLabel.setLineHeightText(text: "사용자 정보", font: .korFont(style: .regular, size: 15)) return button }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift index a608b4e9..d9f8fe3b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift @@ -14,7 +14,7 @@ final class MyPageRecentView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "최근 본 팝업", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "최근 본 팝업", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift index 68883315..2cd74d2e 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift @@ -21,7 +21,7 @@ final class MyPageTermsController: BaseViewController, View { private let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "약관", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "약관", font: .korFont(style: .regular, size: 15)) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift index b8fbe817..c0d6bdd2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift @@ -24,7 +24,7 @@ final class WithdrawlCheckModalController: BaseViewController, View { init(nickName: String?) { super.init() let title = "\(nickName ?? "")님, 팝풀 서비스를\n정말 탈퇴하시겠어요?" - mainView.titleLabel.setLineHeightText(text: title, font: .KorFont(style: .bold, size: 18), lineHeight: 1.312) + mainView.titleLabel.setLineHeightText(text: title, font: .korFont(style: .bold, size: 18), lineHeight: 1.312) mainView.titleLabel.numberOfLines = 2 } diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift index 5c72ed50..bf7e88f2 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift @@ -34,7 +34,7 @@ final class WithdrawlCheckModalView: UIView { let firstLabel: UILabel = { let text = "서비스 탈퇴 시 회원 전용 서비스 이용이 불가하며 회원 데이터는 일괄 삭제 처리돼요." let label = UILabel() - label.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 13), lineHeight: 1.4) + label.setLineHeightText(text: text, font: .korFont(style: .regular, size: 13), lineHeight: 1.4) label.numberOfLines = 2 label.textColor = .g600 return label @@ -43,7 +43,7 @@ final class WithdrawlCheckModalView: UIView { let secondLabel: UILabel = { let text = "탈퇴 후에는 계정을 다시 살리거나 복구할 수 없어요." let label = UILabel() - label.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 13), lineHeight: 1.4) + label.setLineHeightText(text: text, font: .korFont(style: .regular, size: 13), lineHeight: 1.4) label.numberOfLines = 2 label.textColor = .g600 return label diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift index e85f5a9a..0fd5844a 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift @@ -29,7 +29,7 @@ final class WithdrawlCompleteView: UIView { private let descriptionLabel: PPLabel = { let text = "고객님이 만족하실 수 있는\n팝풀이 되도록 앞으로도 노력할게요 :)" let label = PPLabel(style: .regular, fontSize: 15, text: text) - label.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 15), lineHeight: 1.5) + label.setLineHeightText(text: text, font: .korFont(style: .regular, size: 15), lineHeight: 1.5) label.numberOfLines = 2 label.textAlignment = .center label.textColor = .g600 diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift index 2e0f1eb5..8ef20cb3 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift @@ -26,7 +26,7 @@ final class WithdrawlCheckSectionCell: UICollectionViewCell { let view = UITextView() view.textContainerInset = .zero view.contentInset = .zero - view.font = .KorFont(style: .medium, size: 14) + view.font = .korFont(style: .medium, size: 14) view.backgroundColor = .clear return view }() @@ -157,7 +157,7 @@ extension WithdrawlCheckSectionCell: Inputable { let image = input.isSelected ? UIImage(named: "icon_check_fill") : UIImage(named: "icon_check") checkImageView.image = image let title = input.title ?? "" - titleLabel.setLineHeightText(text: title, font: .KorFont(style: .regular, size: 14)) + titleLabel.setLineHeightText(text: title, font: .korFont(style: .regular, size: 14)) bind() if input.isSelected { if title == "기타" { diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift index de4f289b..250a933b 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift @@ -14,14 +14,14 @@ final class WithdrawlReasonView: UIView { // MARK: - Components let headerView: PPReturnHeaderView = { let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "회원 탈퇴", font: .KorFont(style: .regular, size: 15)) + view.headerLabel.setLineHeightText(text: "회원 탈퇴", font: .korFont(style: .regular, size: 15)) return view }() private let titleLabel: UILabel = { let text = "탈퇴하려는 이유가\n무엇인가요?" let label = UILabel() - label.setLineHeightText(text: text, font: .KorFont(style: .bold, size: 20), lineHeight: 1.312) + label.setLineHeightText(text: text, font: .korFont(style: .bold, size: 20), lineHeight: 1.312) label.numberOfLines = 2 return label }() @@ -31,7 +31,7 @@ final class WithdrawlReasonView: UIView { let label = PPLabel(style: .regular, fontSize: 15, text: text) label.textColor = .g600 label.numberOfLines = 2 - label.setLineHeightText(text: text, font: .KorFont(style: .regular, size: 15), lineHeight: 1.4) + label.setLineHeightText(text: text, font: .korFont(style: .regular, size: 15), lineHeight: 1.4) return label }() diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift index e89783de..c7773c88 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift @@ -86,12 +86,12 @@ extension CancelableTagSectionCell: Inputable { cancelButton.setImage(xmarkImage, for: .normal) if input.isSelected { contentView.backgroundColor = .blu500 - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .bold, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) titleLabel.textColor = .w100 contentView.layer.borderColor = UIColor.blu500.cgColor } else { contentView.backgroundColor = .clear - titleLabel.setLineHeightText(text: input.title, font: .KorFont(style: .medium, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) titleLabel.textColor = .g400 contentView.layer.borderColor = UIColor.g200.cgColor } diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift index 97d074a2..63dad364 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift @@ -18,7 +18,7 @@ final class SearchTitleSectionCell: UICollectionViewCell { private let sectionTitleLabel: UILabel = { let label = UILabel() - label.font = .KorFont(style: .bold, size: 16) + label.font = .korFont(style: .bold, size: 16) return label }() @@ -73,7 +73,7 @@ extension SearchTitleSectionCell: Inputable { titleButton.isHidden = false let attributes: [NSAttributedString.Key: Any] = [ .underlineStyle: NSUnderlineStyle.single.rawValue, - .font: UIFont.KorFont(style: .regular, size: 13)! + .font: UIFont.korFont(style: .regular, size: 13)! ] let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) titleButton.setAttributedTitle(attributedTitle, for: .normal) diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift index 89364b85..3d3ffda9 100644 --- a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift @@ -37,15 +37,15 @@ final class SearchMainView: UIView { let button = UIButton(type: .system) button.setTitle("취소", for: .normal) button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .KorFont(style: .regular, size: 14) + button.titleLabel?.font = .korFont(style: .regular, size: 14) button.imageView?.contentMode = .scaleAspectFit return button }() let searchTextField: UITextField = { let view = UITextField() - view.font = .KorFont(style: .regular, size: 14) - view.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .KorFont(style: .regular, size: 14)!) + view.font = .korFont(style: .regular, size: 14) + view.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .korFont(style: .regular, size: 14)!) return view }() diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift index b7f26876..7556c572 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift @@ -69,13 +69,13 @@ extension SignUpCompleteController { let attributedText = NSMutableAttributedString( string: categoryString, attributes: [ - .font: UIFont.KorFont(style: .bold, size: 15)!, + .font: UIFont.korFont(style: .bold, size: 15)!, .foregroundColor: UIColor.g600, NSAttributedString.Key.paragraphStyle: paragraphStyle ] ) attributedText.append(NSAttributedString(string: "와 연관된 팝업스토어 정보를 안내해드릴게요.", attributes: [ - .font: UIFont.KorFont(style: .regular, size: 15)!, + .font: UIFont.korFont(style: .regular, size: 15)!, .foregroundColor: UIColor.g600, NSAttributedString.Key.paragraphStyle: paragraphStyle ])) diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift index 13b7df79..9604a80d 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift @@ -99,7 +99,7 @@ final class SignUpStep2Reactor: Reactor { // kor and end Check let pattern = "^[가-힣a-zA-Z\\s]+$" // 허용하는 문자만 검사 - let regex = try! NSRegularExpression(pattern: pattern) + guard let regex = try? NSRegularExpression(pattern: pattern) else { return .empty } let range = NSRange(location: 0, length: text.utf16.count) if regex.firstMatch(in: text, options: [], range: range) == nil { return isActive ? .korAndEngActive : .korAndEng } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift index b40c3bc2..984e0d56 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift @@ -45,7 +45,7 @@ final class SignUpStep2View: UIView { let textField: UITextField = { let textField = UITextField() textField.placeholder = "별명을 입력해주세요" - textField.font = .KorFont(style: .medium, size: 14) + textField.font = .korFont(style: .medium, size: 14) return textField }() @@ -75,7 +75,7 @@ final class SignUpStep2View: UIView { let attributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g1000 // 텍스트 색상 ] @@ -83,7 +83,7 @@ final class SignUpStep2View: UIView { let disabledAttributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.KorFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g300 // 텍스트 색상 ] diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift index ed6bb5a6..28e1b592 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift @@ -58,12 +58,12 @@ extension TagSectionCell: Inputable { contentView.backgroundColor = .blu500 contentView.layer.borderWidth = 0 titleLabel.textColor = .w100 - titleLabel.font = . KorFont(style: .medium, size: 13) + titleLabel.font = . korFont(style: .medium, size: 13) } else { contentView.backgroundColor = .clear contentView.layer.borderWidth = 1 titleLabel.textColor = .g400 - titleLabel.font = . KorFont(style: .medium, size: 13) + titleLabel.font = . korFont(style: .medium, size: 13) } } } diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift index fdfa8afe..79f1c5f2 100644 --- a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift @@ -24,7 +24,7 @@ final class TermsDetailController: BaseViewController { paragraphStyle.lineHeightMultiple = 1.2 let attributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.KorFont(style: .regular, size: 14), + .font: UIFont.korFont(style: .regular, size: 14), .paragraphStyle: paragraphStyle ] diff --git a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift index 14c76db0..efac523a 100644 --- a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift @@ -237,11 +237,11 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { // 폰트 설정 let appearance = UITabBarAppearance() appearance.stackedLayoutAppearance.normal.titleTextAttributes = [ - .font: UIFont.KorFont(style: .medium, size: 11)!, + .font: UIFont.korFont(style: .medium, size: 11)!, .paragraphStyle: paragraphStyle ] appearance.stackedLayoutAppearance.selected.titleTextAttributes = [ - .font: UIFont.KorFont(style: .bold, size: 11)!, + .font: UIFont.korFont(style: .bold, size: 11)!, .paragraphStyle: paragraphStyle ] diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift b/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift index 8ec3d045..27f6d3df 100644 --- a/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift +++ b/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift @@ -23,14 +23,14 @@ final class BookMarkToastView: UIView { private let bookMarkLabel: UILabel = { let label = UILabel() - label.setLineHeightText(text: "찜한 팝업에 저장했어요", font: .KorFont(style: .regular, size: 15), lineHeight: 1) + label.setLineHeightText(text: "찜한 팝업에 저장했어요", font: .korFont(style: .regular, size: 15), lineHeight: 1) label.textColor = .w100 return label }() private let unbookMarkLabel: UILabel = { let label = PPLabel(style: .regular, fontSize: 15, text: "찜한 팝업을 해제했어요") - label.setLineHeightText(text: "찜한 팝업을 해제했어요", font: .KorFont(style: .regular, size: 15), lineHeight: 1) + label.setLineHeightText(text: "찜한 팝업을 해제했어요", font: .korFont(style: .regular, size: 15), lineHeight: 1) label.textColor = .w100 return label }() @@ -42,7 +42,7 @@ final class BookMarkToastView: UIView { button.setTitleColor(.blu300, for: .normal) button.layer.cornerRadius = 4 button.clipsToBounds = true - button.titleLabel?.font = .KorFont(style: .medium, size: 12) + button.titleLabel?.font = .korFont(style: .medium, size: 12) return button }() diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift index bd04535e..79124cca 100644 --- a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift +++ b/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift @@ -26,7 +26,7 @@ final class ToastView: UIView { private let messageLabel: UILabel = { let label = UILabel() label.textColor = .w100 - label.font = .KorFont(style: .regular, size: 15) + label.font = .korFont(style: .regular, size: 15) return label }() From 4b8a89bb9198ed5771a3cd603f151f8ff510293d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 5 Apr 2025 03:54:00 +0000 Subject: [PATCH 061/393] style/#82: Apply SwiftLint autocorrect --- .../ImageBannerSection/ImageBannerSectionCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index 56817f73..c6d16cef 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -264,7 +264,7 @@ extension ImageBannerSectionCell: UICollectionViewDelegate, UICollectionViewData func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { guard imageSection.dataCount > 1 else { return } - + if currentIndex == 0 { contentCollectionView.scrollToItem( at: .init(row: imageSection.dataCount - 2, section: 0), From e8d28831b84c4eade07e718d282eff8557a0c52f Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Sat, 5 Apr 2025 13:50:03 +0900 Subject: [PATCH 062/393] =?UTF-8?q?fix/#93:=20CI=EA=B0=80=20dev=EC=97=90?= =?UTF-8?q?=20push=20=EB=90=A0=EB=95=8C=EB=8A=94=20=EB=8F=8C=EC=95=84?= =?UTF-8?q?=EA=B0=80=EC=A7=80=20=EC=95=8A=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 오직 PR의 이벤트에만 반응하도록 수정 - PR이 오픈될 때 - PR에 추가 커밋이 들어올 때 - PR이 dev 브랜치를 타겟팅 하고 있을 때 --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 01cd77eb..852aaace 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,8 +3,6 @@ name: CI on: pull_request: branches: [dev] - push: - branches: [dev] jobs: autocorrect: From 6db82d35484d6fbec64384195f4adf31f63fa45d Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 6 Apr 2025 18:03:19 +0900 Subject: [PATCH 063/393] =?UTF-8?q?refactor/#92:=20Kingfisher=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0,=20=EB=8C=80=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 17 ----------- .../xcshareddata/swiftpm/Package.resolved | 11 +------ .../Presentation/Extension/String?+.swift | 9 ------ .../Presentation/Extension/UIImageView+.swift | 29 +++---------------- .../MapPopupCardView/PopupCardCell.swift | 1 - .../HomeCardSection/HomeCardSectionCell.swift | 8 ----- .../HomePopularCardSectionCell.swift | 8 ----- 7 files changed, 5 insertions(+), 78 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index d4a99af2..70e891ae 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -474,7 +474,6 @@ BDCA41E22CF35AC1005EECF6 /* PoppoolUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41E12CF35AC1005EECF6 /* PoppoolUITests.swift */; }; BDCA41E42CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41E32CF35AC1005EECF6 /* PoppoolUITestsLaunchTests.swift */; }; BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F12CF35D0D005EECF6 /* SnapKit */; }; - BDCA41F52CF35D33005EECF6 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F42CF35D33005EECF6 /* Kingfisher */; }; BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F72CF35D9A005EECF6 /* RxSwift */; }; BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41FD2CF35EE7005EECF6 /* ReactorKit */; }; BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA42002CF35EFE005EECF6 /* RxKeyboard */; }; @@ -975,7 +974,6 @@ files = ( BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */, BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, - BDCA41F52CF35D33005EECF6 /* Kingfisher in Frameworks */, BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, @@ -3088,7 +3086,6 @@ name = Poppool; packageProductDependencies = ( BDCA41F12CF35D0D005EECF6 /* SnapKit */, - BDCA41F42CF35D33005EECF6 /* Kingfisher */, BDCA41F72CF35D9A005EECF6 /* RxSwift */, BDCA41FD2CF35EE7005EECF6 /* ReactorKit */, BDCA42002CF35EFE005EECF6 /* RxKeyboard */, @@ -3179,7 +3176,6 @@ mainGroup = BDCA41B42CF35AC0005EECF6; packageReferences = ( BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */, - BDCA41F32CF35D33005EECF6 /* XCRemoteSwiftPackageReference "Kingfisher" */, BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */, BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */, BDCA41FF2CF35EFE005EECF6 /* XCRemoteSwiftPackageReference "RxKeyboard" */, @@ -4123,14 +4119,6 @@ minimumVersion = 5.7.1; }; }; - BDCA41F32CF35D33005EECF6 /* XCRemoteSwiftPackageReference "Kingfisher" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/onevcat/Kingfisher.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 8.1.1; - }; - }; BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift.git"; @@ -4238,11 +4226,6 @@ package = BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - BDCA41F42CF35D33005EECF6 /* Kingfisher */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F32CF35D33005EECF6 /* XCRemoteSwiftPackageReference "Kingfisher" */; - productName = Kingfisher; - }; BDCA41F72CF35D9A005EECF6 /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */; diff --git a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5c6fc0d1..78d0f8f5 100644 --- a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "408890f5035d25ec9b1705255e27f587050890efcc8e3a540c8d9edbae7aeece", + "originHash" : "000b8c18b59df01ac73af8ca8c37167230915d09ad120950c2d361fbb92fe695", "pins" : [ { "identity" : "alamofire", @@ -37,15 +37,6 @@ "version" : "2.24.0" } }, - { - "identity" : "kingfisher", - "kind" : "remoteSourceControl", - "location" : "https://github.com/onevcat/Kingfisher.git", - "state" : { - "revision" : "3db26ab625d194c38e68c1a40e43d1bc12743fe0", - "version" : "8.2.0" - } - }, { "identity" : "lottie-spm", "kind" : "remoteSourceControl", diff --git a/Poppool/Poppool/Presentation/Extension/String?+.swift b/Poppool/Poppool/Presentation/Extension/String?+.swift index c1c5a916..13785af3 100644 --- a/Poppool/Poppool/Presentation/Extension/String?+.swift +++ b/Poppool/Poppool/Presentation/Extension/String?+.swift @@ -1,14 +1,5 @@ -// -// String?+.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit -import Kingfisher - extension Optional where Wrapped == String { /// ISO 8601 형식의 문자열을 `Date`로 변환하는 메서드 func toDate() -> Date? { diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index f2670759..284957d6 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -1,14 +1,5 @@ -// -// UIImageView+.swift -// Poppool -// -// Created by SeoJunYoung on 12/3/24. -// - import UIKit -import Kingfisher - extension UIImageView { func setPPImage(path: String?) { guard let path = path else { @@ -22,15 +13,6 @@ extension UIImageView { self?.image = image } } -// let imageURL = URL(string: cenvertimageURL) -// self.kf.setImage(with: imageURL) { result in -// switch result { -// case .failure(let error): -// Logger.log(message: "\(path) image Load Fail: \(error.localizedDescription)", category: .error) -// default: -// break -// } -// } } } @@ -43,13 +25,10 @@ extension UIImageView { let imageURLString = Secrets.popPoolS3BaseURL.rawValue + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) - self.kf.setImage(with: imageURL) { result in - completion() - switch result { - case .failure(let error): - Logger.log(message: "\(path) image Load Fail: \(error.localizedDescription)", category: .error) - default: - break + ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in + DispatchQueue.main.async { + completion() + self?.image = image } } } diff --git a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift index 4d1ee89d..87ad36d5 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift @@ -1,6 +1,5 @@ import UIKit import SnapKit -import Kingfisher final class PopupCardCell: UICollectionViewCell { static let identifier = "PopupCardCell" diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 5bc534e3..d81a8abd 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -1,15 +1,7 @@ -// -// HomeCardSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit import SnapKit import RxSwift -import Kingfisher final class HomeCardSectionCell: UICollectionViewCell { diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index aa395113..05bd9715 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -1,15 +1,7 @@ -// -// HomePopularCardSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit import SnapKit import RxSwift -import Kingfisher final class HomePopularCardSectionCell: UICollectionViewCell { From d80a512d7f4bebe3eca14ed073d12bd82f2aa41d Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 6 Apr 2025 18:08:33 +0900 Subject: [PATCH 064/393] =?UTF-8?q?fix/#92:=20DiskStorage=20TImer=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ImageLoader/DiskStorage.swift | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift index 92c0f05c..95d4b70d 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift @@ -120,36 +120,26 @@ final class DiskStorage { } /// 주기적으로 만료된 캐시를 삭제하는 메서드 - /// - 5분(300초)마다 실행되며, 만료된 이미지와 메타데이터를 정리함. private func startCacheCleanup() { - DispatchQueue.global(qos: .background).async { [weak self] in - guard let self = self else { return } + let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] - let cleanTimer = Timer.scheduledTimer(withTimeInterval: 300, repeats: true) { _ in - let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] + for file in files { + if file.pathExtension == "metadata", + let metadataData = try? Data(contentsOf: file), + let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], + let expirationTime = metadata["expiration"] { - for file in files { - if file.pathExtension == "metadata", - let metadataData = try? Data(contentsOf: file), - let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], - let expirationTime = metadata["expiration"] { - - // 만료 시간이 지나면 이미지와 메타데이터 삭제 - if Date().timeIntervalSince1970 > expirationTime { - let imageFileURL = file.deletingPathExtension() // 메타데이터와 동일한 이름의 이미지 파일 - do { - try self.fileManager.removeItem(at: imageFileURL) - try self.fileManager.removeItem(at: file) // 메타데이터 삭제 - } catch { - print("Failed to delete expired cache: \(error)") - } - } + // 만료 시간이 지나면 이미지와 메타데이터 삭제 + if Date().timeIntervalSince1970 > expirationTime { + let imageFileURL = file.deletingPathExtension() // 메타데이터와 동일한 이름의 이미지 파일 + do { + try self.fileManager.removeItem(at: imageFileURL) + try self.fileManager.removeItem(at: file) // 메타데이터 삭제 + } catch { + print("Failed to delete expired cache: \(error)") } } } - // 백그라운드에서 실행되는 타이머를 메인 루프에 추가 - RunLoop.current.add(cleanTimer, forMode: .common) - RunLoop.current.run() // 백그라운드 스레드에서 타이머를 계속 실행하기 위해 RunLoop를 유지 } } } From 72b9de63e56b8c55dd3cd428aae5f203bed8f1bd Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 7 Apr 2025 23:33:48 +0900 Subject: [PATCH 065/393] =?UTF-8?q?refactor/#92:=20import=20Kingfisher=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=B9=8C=EB=93=9C=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EA=B2=8C=20=EC=BD=94=EB=93=9C=20=EC=9D=BC?= =?UTF-8?q?=EB=B6=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Map/Common/MapPopupCardView/PopupCardCell.swift | 4 ++-- .../Presentation/Scene/Detail/DetailController.swift | 2 +- .../Main/View/HomeCardSection/HomeCardSectionCell.swift | 8 -------- .../HomePopularCardSectionCell.swift | 7 ------- 4 files changed, 3 insertions(+), 18 deletions(-) diff --git a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift index 27350c36..5d2f8f11 100644 --- a/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift +++ b/Poppool/Poppool/Presentation/Map/Common/MapPopupCardView/PopupCardCell.swift @@ -1,7 +1,7 @@ -import Kingfisher -import SnapKit import UIKit +import SnapKit + final class PopupCardCell: UICollectionViewCell { static let identifier = "PopupCardCell" diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift index d9030086..ee001146 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift @@ -224,7 +224,7 @@ extension DetailController: UICollectionViewDelegate, UICollectionViewDataSource cell.imageCollectionView.rx.itemSelected .withUnretained(self) .map { (owner, cellIndexPath) in - Reactor.Action.commentImageTapped(controller: owner, cellRow: indexPath.row, ImageRow: cellIndexPath.row) + Reactor.Action.commentImageTapped(controller: owner, cellRow: indexPath.row, imageRow: cellIndexPath.row) } .bind(to: reactor.action) .disposed(by: cell.disposeBag) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 14c03267..30bb15e1 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -1,13 +1,5 @@ -// -// HomeCardSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit -import Kingfisher import RxSwift import SnapKit diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index 9e8f66af..54d97a52 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -1,10 +1,3 @@ -// -// HomePopularCardSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit import Kingfisher From 8b2674edf15c2de4fa7953f14bd07e7f169e0830 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 7 Apr 2025 23:58:05 +0900 Subject: [PATCH 066/393] =?UTF-8?q?refactor/#92:=20import=20Kingfisher=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 --- .../View/HomePopularCardSection/HomePopularCardSectionCell.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index 54d97a52..b4bd7862 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -1,6 +1,5 @@ import UIKit -import Kingfisher import RxSwift import SnapKit From ec1848bc53ad13278bd57a4c436f88c2fb95e1b6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 7 Apr 2025 15:04:14 +0000 Subject: [PATCH 067/393] style/#92: Apply SwiftLint autocorrect --- .../ImageLoader/DiskStorage.swift | 40 +++++++++---------- .../ImageLoader/ImageLoader.swift | 36 ++++++++--------- .../ImageLoader/MemoryStorage.swift | 24 +++++------ 3 files changed, 50 insertions(+), 50 deletions(-) diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift index 95d4b70d..127639b3 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift @@ -1,30 +1,30 @@ -import UIKit import CryptoKit +import UIKit /// 디스크에 이미지를 캐싱하는 클래스 final class DiskStorage { - + /// 싱글톤 인스턴스 static let shared = DiskStorage() - + /// 파일 관리 객체 private let fileManager = FileManager.default - + /// 이미지 캐시 디렉터리 경로 private let cacheDirectory: URL - + /// 초기화 메서드 (캐시 디렉터리 생성 및 자동 삭제 스케줄 시작) private init() { let urls = fileManager.urls(for: .cachesDirectory, in: .userDomainMask) cacheDirectory = urls[0].appendingPathComponent("ImageCache") - + // 디렉터리가 존재하지 않으면 생성 if !fileManager.fileExists(atPath: cacheDirectory.path) { try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true, attributes: nil) } startCacheCleanup() } - + /// URL을 안전한 파일명으로 변환하는 메서드 /// - Parameter url: 원본 URL 문자열 /// - Returns: 파일명으로 변환된 문자열 @@ -33,7 +33,7 @@ final class DiskStorage { let hashed = SHA256.hash(data: data) return hashed.compactMap { String(format: "%02x", $0) }.joined() } - + /// 이미지를 디스크에 저장하는 메서드 /// - Parameters: /// - image: 저장할 UIImage 객체 @@ -42,7 +42,7 @@ final class DiskStorage { let fileName = cacheFileName(for: url) let fileURL = cacheDirectory.appendingPathComponent(fileName) let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") - + // 이미지 데이터를 JPEG 형식으로 변환하여 저장 if let data = image.jpegData(compressionQuality: 0.8) { do { @@ -51,11 +51,11 @@ final class DiskStorage { print("Error writing image data to disk: \(error)") } } - + // 만료 시간 기록 let expirationDate = Date().addingTimeInterval(ImageLoader.shared.configure.diskCacheExpiration) let metadata = ["expiration": expirationDate.timeIntervalSince1970] - + // 만료 정보를 JSON 형태로 저장 if let metadataData = try? JSONSerialization.data(withJSONObject: metadata) { do { @@ -65,7 +65,7 @@ final class DiskStorage { } } } - + /// 디스크에서 이미지를 불러오는 메서드 (만료된 경우 자동 삭제) /// - Parameter url: 이미지의 원본 URL 문자열 /// - Returns: UIImage 객체 (없거나 만료된 경우 nil) @@ -73,34 +73,34 @@ final class DiskStorage { let fileName = cacheFileName(for: url) let fileURL = cacheDirectory.appendingPathComponent(fileName) let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") - + // 만료 시간 확인 if let metadataData = try? Data(contentsOf: metadataURL), let metadata = try? JSONSerialization.jsonObject(with: metadataData) as? [String: TimeInterval], let expirationTime = metadata["expiration"] { - + // 만료 시간이 현재 시각을 초과하면 삭제 후 nil 반환 if Date().timeIntervalSince1970 > expirationTime { removeImage(url: url) return nil } } - + // 이미지 파일이 존재하면 로드하여 반환 if let data = try? Data(contentsOf: fileURL) { return UIImage(data: data) } - + return nil } - + /// 특정 URL에 해당하는 이미지를 디스크에서 삭제하는 메서드 /// - Parameter url: 삭제할 이미지의 원본 URL 문자열 func removeImage(url: String) { let fileName = cacheFileName(for: url) let fileURL = cacheDirectory.appendingPathComponent(fileName) let metadataURL = cacheDirectory.appendingPathComponent("\(fileName).metadata") - + do { try fileManager.removeItem(at: fileURL) // 이미지 파일 삭제 try fileManager.removeItem(at: metadataURL) // 메타데이터 파일 삭제 @@ -108,7 +108,7 @@ final class DiskStorage { print("Failed to remove image: \(error)") } } - + /// 모든 캐시 데이터를 삭제하는 메서드 func clearCache() { do { @@ -118,7 +118,7 @@ final class DiskStorage { print("Failed to clear cache: \(error)") } } - + /// 주기적으로 만료된 캐시를 삭제하는 메서드 private func startCacheCleanup() { let files = (try? self.fileManager.contentsOfDirectory(at: self.cacheDirectory, includingPropertiesForKeys: nil)) ?? [] diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift index 9e254f47..efaecd9d 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift @@ -11,7 +11,7 @@ enum ImageSizeOption { case middle case high case origin - + var size: CGSize { switch self { case .low: @@ -35,14 +35,14 @@ class ImageLoaderConfigure { /// URL을 통해 이미지를 비동기적으로 로드하는 클래스 final class ImageLoader { - + static let shared = ImageLoader() - + /// 이미지 로더 설정 객체 let configure = ImageLoaderConfigure() - + private init() {} - + /// URL을 통해 이미지를 로드하고, 실패 시 기본 이미지를 반환하는 메서드 /// - Parameters: /// - stringURL: 이미지 URL 문자열 @@ -66,7 +66,7 @@ final class ImageLoader { } private extension ImageLoader { - + /// URL을 통해 이미지를 로드하는 내부 메서드 /// - Parameters: /// - stringURL: 이미지 URL 문자열 @@ -76,13 +76,13 @@ private extension ImageLoader { completion(.failure(ImageLoaderError.invalidURL)) return } - + // 메모리 캐시에서 이미지 조회 if let cachedImage = MemoryStorage.shared.fetchImage(url: stringURL) { completion(.success(cachedImage)) return } - + // 디스크 캐시 확인 if let diskImage = DiskStorage.shared.fetchImage(url: stringURL) { // 메모리 캐시에 저장 후 반환 @@ -90,7 +90,7 @@ private extension ImageLoader { completion(.success(diskImage)) return } - + // 네트워크에서 데이터 요청 fetchDataFrom(url: url) { result in switch result { @@ -107,13 +107,13 @@ private extension ImageLoader { } } } - + /// URL을 통해 데이터를 요청하는 메서드 /// - Parameters: /// - url: 요청할 URL 객체 /// - completion: 요청 완료 후 호출되는 클로저 func fetchDataFrom(url: URL, completion: @escaping (Result) -> Void) { - let task = URLSession.shared.dataTask(with: url) { data, response, error in + let task = URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { completion(.failure(ImageLoaderError.networkError(description: "Network Error: \(error.localizedDescription)"))) return @@ -122,26 +122,26 @@ private extension ImageLoader { } task.resume() } - + func resizeImage(_ image: UIImage?, defaultImage: UIImage?, with sizeOption: ImageSizeOption) -> UIImage? { guard let image else { return defaultImage } - + if sizeOption == .origin { return image } - + let targetSize = sizeOption.size - + // 비율 유지 리사이징 let aspectRatio = image.size.width / image.size.height var newSize = targetSize - + if aspectRatio > 1 { // 가로 이미지 newSize.height = targetSize.width / aspectRatio } else { // 세로 이미지 newSize.width = targetSize.height * aspectRatio } - + let renderer = UIGraphicsImageRenderer(size: newSize) - + return renderer.image { _ in image.draw(in: CGRect(origin: .zero, size: newSize)) } diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift index 517bc9a7..2d4b30dc 100644 --- a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift +++ b/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift @@ -4,7 +4,7 @@ import UIKit class StorageData: NSObject { let image: UIImage? /// 캐시된 이미지 let expirationDate: Date /// 캐시 만료 시간 - + /// 초기화 메서드 /// - Parameters: /// - image: 저장할 이미지 @@ -13,7 +13,7 @@ class StorageData: NSObject { self.image = image self.expirationDate = Date().addingTimeInterval(expiration) } - + /// 캐시가 만료되었는지 확인하는 메서드 /// - Returns: 만료 여부 (true: 만료됨, false: 유효함) func isExpired() -> Bool { @@ -23,21 +23,21 @@ class StorageData: NSObject { /// 메모리 캐시를 관리하는 클래스 final class MemoryStorage { - + /// 싱글톤 인스턴스 static let shared = MemoryStorage() - + /// 이미지 캐시 저장소 private let cache = NSCache() - + /// 현재 캐시에 저장된 키 목록 private var cachedKeys: Set = [] - + /// 초기화 (자동 캐시 정리 시작) private init() { startCacheCleanup() } - + /// 이미지를 캐시에 저장하는 메서드 /// - Parameters: /// - image: 저장할 이미지 @@ -47,7 +47,7 @@ final class MemoryStorage { cache.setObject(cachedData, forKey: url as NSString) cachedKeys.insert(url) } - + /// 캐시에서 이미지를 가져오는 메서드 /// - Parameter url: 이미지 URL 문자열 /// - Returns: 캐시된 UIImage (없으면 nil) @@ -59,25 +59,25 @@ final class MemoryStorage { return nil } } - + /// 특정 URL의 캐시 데이터를 제거하는 메서드 /// - Parameter url: 제거할 이미지의 URL 문자열 func removeData(url: String) { cache.removeObject(forKey: url as NSString) cachedKeys.remove(url) } - + /// 모든 캐시 데이터를 삭제하는 메서드 func clearCache() { cache.removeAllObjects() cachedKeys.removeAll() } - + /// 주기적으로 만료된 캐시를 정리하는 메서드 private func startCacheCleanup() { DispatchQueue.global(qos: .background).async { [weak self] in guard let self = self else { return } - + let cleanTimer = Timer.scheduledTimer(withTimeInterval: 60, repeats: true) { _ in for key in self.cachedKeys { let nsKey = key as NSString From e09c017db224a82df72fd7f22b09b8f3fcf8e4a3 Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Tue, 8 Apr 2025 21:32:31 +0900 Subject: [PATCH 068/393] =?UTF-8?q?refactor:=20CI=EA=B0=80=20=ED=83=80?= =?UTF-8?q?=EA=B2=9F=ED=95=98=EB=8A=94=20=EB=B8=8C=EB=9E=9C=EC=B9=98=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 852aaace..abb6166d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: CI on: pull_request: - branches: [dev] + branches: [develop] jobs: autocorrect: From c193b059842c608be560a5b6b8ea9e474d881e85 Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Tue, 8 Apr 2025 22:36:49 +0900 Subject: [PATCH 069/393] =?UTF-8?q?refactor:=20CI=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CI가 새로운 브랜치들을 추적할 수 있도록 수정 - 자동 수정이 develop 브랜치에서만 동작하도록 수정 --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abb6166d..04668907 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,13 @@ name: CI on: pull_request: - branches: [develop] + branches: [main, develop, 'release/*'] jobs: autocorrect: name: 🤖 Autocorrect Workflow runs-on: macos-15 # 최신 macOS 15 환경에서 실행 - if: github.actor != 'github-actions[bot]' # Actions 봇 커밋은 무시 + if: github.actor != 'github-actions[bot]'&& github.base_ref == 'develop' # Actions 봇 커밋은 무시 && develop에서만 자동 수정 진행 steps: - name: Checkout Repository # 저장소 코드 체크아웃 From c40ee06be606abfc1e3a220c1496fffee5305139 Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Wed, 9 Apr 2025 19:55:48 +0900 Subject: [PATCH 070/393] =?UTF-8?q?feat:=20TestFlight=20=EB=B0=B0=ED=8F=AC?= =?UTF-8?q?=20=EC=9E=90=EB=8F=99=ED=99=94=20yml=20=ED=8C=8C=EC=9D=BC=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 --- .github/workflows/deploy_on_release.yml | 88 +++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 .github/workflows/deploy_on_release.yml diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml new file mode 100644 index 00000000..a4604535 --- /dev/null +++ b/.github/workflows/deploy_on_release.yml @@ -0,0 +1,88 @@ +name: Distribution to TestFlight + +on: + pull_request: + branches: [ release/* ] + +jobs: + deploy: + name: 🚀 Distribution to TestFlight Workflow + runs-on: macos-15 # 최신 macOS 15 환경에서 실행 + env: + ## 프로젝트 이름을 변수로 설정 + PROJECT_NAME: Poppool + + # app archive 및 export 에 쓰일 환경 변수 설정 + XC_PROJECT: ${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.xcodeproj + XC_SCHEME: ${{ env.PROJECT_NAME }} + XC_ARCHIVE: ${{ env.PROJECT_NAME }}.xcarchive + + # certificate + ENCRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12.gpg' }} + DECRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12' }} + CERT_ENCRYPTION_KEY: ${{ secrets.CERT_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 + + # provisioning + ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/' }}${{ env.PROJECT_NAME }}GithubActions.mobileprovision.gpg + DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/' }}${{ env.PROJECT_NAME }}GithubActions.mobileprovision + PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 + + # certification export key + CERT_EXPORT_KEY: ${{ secrets.CERT_EXPORT_PWD }} + + KEYCHAIN: ${{ 'test.keychain' }} + + steps: + - name: Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + + - name: 🛠️ Set up Xcode # Xcode 16.2 선택 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 + run: | + security create-keychain -p "" "$KEYCHAIN" + security list-keychains -s "$KEYCHAIN" + security default-keychain -s "$KEYCHAIN" + security unlock-keychain -p "" "$KEYCHAIN" + security set-keychain-settings + + - name : ©️ Configure Code Signing + run: | + # certificate 복호화 + gpg -d -o "$DECRYPTED_CERT_FILE_PATH" --pinentry-mode=loopback --passphrase "$CERT_ENCRYPTION_KEY" "$ENCRYPTED_CERT_FILE_PATH" + + # provisioning 복호화 + gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$PROVISIONING_ENCRYPTION_KEY" "$ENCRYPTED_PROVISION_FILE_PATH" + + # security를 사용하여 인증서와 개인 키를 새로 만든 키 체인으로 가져옴 + security import "$DECRYPTED_CERT_FILE_PATH" -k "$KEYCHAIN" -P "$CERT_EXPORT_KEY" -A + security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN" + + # Xcode에서 찾을 수 있는 프로비저닝 프로필 설치하기 위해 우선 프로비저닝 디렉토리를 생성 + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + + # 디버깅 용 echo 명령어 + echo `ls .github/secrets/*.mobileprovision` + # 모든 프로비저닝 프로파일을 rename 하고 위에서 만든 디렉토리로 복사하는 과정 + for PROVISION in `ls .github/secrets/*.mobileprovision` + do + UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)` + cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" + done + + - name: ⬇️ Archive app # 빌드 및 아카이브 + run: | + xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE + + - name: ⬆️ Export app # export 를 통해 ipa 파일 만듦 + run: | + xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist ExportOptions.plist -exportPath . -allowProvisioningUpdates + + - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 + uses: apple-actions/upload-testflight-build@v1 + with: + app-path: '${{ env.PROJECT_NAME }}.ipa' + issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} + api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} + api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} From bf40154dc2c52c7f1cc59e724e7eb7cd3dd2d8b9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 19:59:50 +0900 Subject: [PATCH 071/393] =?UTF-8?q?chore:=20=EC=9E=90=EB=8F=99=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EC=95=94=ED=98=B8?= =?UTF-8?q?=ED=99=94=EB=90=9C=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 상상도 못한 정체 같이 감(패키지 ㅎ) --- .../PoppoolGitHubAction.mobileprovision.gpg | Bin 0 -> 7965 bytes .github/secrets/certification.p12.gpg | Bin 0 -> 3341 bytes .../xcshareddata/swiftpm/Package.resolved | 168 ++++++++++++++++++ 3 files changed, 168 insertions(+) create mode 100644 .github/secrets/PoppoolGitHubAction.mobileprovision.gpg create mode 100644 .github/secrets/certification.p12.gpg create mode 100644 Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/.github/secrets/PoppoolGitHubAction.mobileprovision.gpg b/.github/secrets/PoppoolGitHubAction.mobileprovision.gpg new file mode 100644 index 0000000000000000000000000000000000000000..71dce0814f5e3a99b534ac27d1ebacb3cf0220b7 GIT binary patch literal 7965 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 literal 0 HcmV?d00001 diff --git a/.github/secrets/certification.p12.gpg b/.github/secrets/certification.p12.gpg new file mode 100644 index 0000000000000000000000000000000000000000..05c0e8d586ad4faee9a9b448029748eee74afd75 GIT binary patch literal 3341 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 X_A3lWwA6lX692w!m&XlR<2%EWX0)9g literal 0 HcmV?d00001 diff --git a/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..810c5679 --- /dev/null +++ b/Poppool/Poppool.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,168 @@ +{ + "originHash" : "893e9d5956a6d674d140654334c58c276c99001321206803c07c48415f2e6ac3", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", + "version" : "5.10.2" + } + }, + { + "identity" : "floatingpanel", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scenee/FloatingPanel.git", + "state" : { + "revision" : "b6e8928b1a3ad909e6db6a0278d286c33cfd0dc3", + "version" : "2.8.6" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk.git", + "state" : { + "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", + "version" : "2.24.0" + } + }, + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", + "version" : "4.5.1" + } + }, + { + "identity" : "pageboy", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Pageboy.git", + "state" : { + "revision" : "be0c1f6f1964cfb07f9d819b0863f2c3f255f612", + "version" : "4.2.0" + } + }, + { + "identity" : "panmodal", + "kind" : "remoteSourceControl", + "location" : "https://github.com/slackhq/PanModal.git", + "state" : { + "revision" : "b012aecb6b67a8e46369227f893c12544846613f", + "version" : "1.2.7" + } + }, + { + "identity" : "reactorkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/ReactorKit.git", + "state" : { + "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", + "version" : "3.2.0" + } + }, + { + "identity" : "rxdatasources", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxDataSources.git", + "state" : { + "revision" : "90c29b48b628479097fe775ed1966d75ac374518", + "version" : "5.0.2" + } + }, + { + "identity" : "rxgesture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxGesture.git", + "state" : { + "revision" : "1b137c576b4aaaab949235752278956697c9e4a0", + "version" : "4.0.4" + } + }, + { + "identity" : "rxkeyboard", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxKeyboard.git", + "state" : { + "revision" : "63f6377975c962a1d89f012a6f1e5bebb2c502b7", + "version" : "2.0.1" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "5dd1907d64f0d36f158f61a466bab75067224893", + "version" : "6.9.0" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + }, + { + "identity" : "spm-nmapsgeometry", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsGeometry.git", + "state" : { + "revision" : "436d5e2e684f557faf5ef5862fd6633a42d7af11", + "version" : "1.0.2" + } + }, + { + "identity" : "spm-nmapsmap", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsMap", + "state" : { + "revision" : "ad89e53fdfec3b8d8994280fb0414b5a7b1c3e8e", + "version" : "3.21.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "bba848db50462894e7fc0891d018dfecad4ef11e", + "version" : "2.8.7" + } + }, + { + "identity" : "tabman", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Tabman.git", + "state" : { + "revision" : "3b2213290eb93e55bb50b49d1a179033005c11ab", + "version" : "3.2.0" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + }, + { + "identity" : "weakmaptable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/WeakMapTable.git", + "state" : { + "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", + "version" : "1.2.1" + } + } + ], + "version" : 3 +} From 529850f673c1d27352a85ccf36ca404c818dcabd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 20:07:03 +0900 Subject: [PATCH 072/393] =?UTF-8?q?fix/#103:=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=EB=A5=BC=20=EC=B0=BE=EC=A7=80=20=EB=AA=BB?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 프로젝트 이름 환경변수 제거 --- .github/workflows/deploy_on_release.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index a4604535..fe1e3f0d 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -9,13 +9,10 @@ jobs: name: 🚀 Distribution to TestFlight Workflow runs-on: macos-15 # 최신 macOS 15 환경에서 실행 env: - ## 프로젝트 이름을 변수로 설정 - PROJECT_NAME: Poppool - # app archive 및 export 에 쓰일 환경 변수 설정 - XC_PROJECT: ${{ env.PROJECT_NAME }}/${{ env.PROJECT_NAME }}.xcodeproj - XC_SCHEME: ${{ env.PROJECT_NAME }} - XC_ARCHIVE: ${{ env.PROJECT_NAME }}.xcarchive + XC_PROJECT: Poppool/Poppool.xcodeproj + XC_SCHEME: Poppool + XC_ARCHIVE: Poppool.xcarchive # certificate ENCRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12.gpg' }} @@ -23,8 +20,8 @@ jobs: CERT_ENCRYPTION_KEY: ${{ secrets.CERT_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 # provisioning - ENCRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/' }}${{ env.PROJECT_NAME }}GithubActions.mobileprovision.gpg - DECRYPTED_PROVISION_FILE_PATH: ${{ '.github/secrets/' }}${{ env.PROJECT_NAME }}GithubActions.mobileprovision + ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/Poppool.mobileprovision.gpg' + DECRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGithubActions.mobileprovision' PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 # certification export key From 841c9edc042314803e110da50a8e0fb94be54fe3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 20:10:22 +0900 Subject: [PATCH 073/393] =?UTF-8?q?fix/#103:=20=ED=94=84=EB=A1=9C=EB=B9=84?= =?UTF-8?q?=EC=A0=80=EB=8B=9D=20=EA=B2=BD=EB=A1=9C=20=EC=9D=B4=EC=83=81=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index fe1e3f0d..2e160fe4 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -20,7 +20,7 @@ jobs: CERT_ENCRYPTION_KEY: ${{ secrets.CERT_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 # provisioning - ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/Poppool.mobileprovision.gpg' + ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGithubActions.mobileprovision.gpg' DECRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGithubActions.mobileprovision' PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 From 101b203c46c87caaa670004a393604c07e187667 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 20:13:02 +0900 Subject: [PATCH 074/393] =?UTF-8?q?fix/#103:=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EA=B2=BD=EB=A1=9C=20=EB=8B=A4=EC=8B=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 멍청하게 파일명을 잘못 넣음 ㅠ --- .github/workflows/deploy_on_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 2e160fe4..e9c91e99 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -20,8 +20,8 @@ jobs: CERT_ENCRYPTION_KEY: ${{ secrets.CERT_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 # provisioning - ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGithubActions.mobileprovision.gpg' - DECRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGithubActions.mobileprovision' + ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGitHubAction.mobileprovision.gpg' + DECRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGitHubAction.mobileprovision' PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 # certification export key From 691c3cad667098735a003d805e954ad17b1b5ab1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 20:20:18 +0900 Subject: [PATCH 075/393] =?UTF-8?q?fix/#103:=20=EB=B0=B0=ED=8F=AC=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=ED=99=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 자동화 코드에서 xcconfig를 생성하도록 수정 - 프로젝트 설정에 프로비저닝을 빌드용으로 변경 --- .github/workflows/deploy_on_release.yml | 10 ++++++++++ Poppool/Poppool.xcodeproj/project.pbxproj | 10 +++++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index e9c91e99..0d49db4d 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -36,6 +36,16 @@ jobs: - name: 🛠️ Set up Xcode # Xcode 16.2 선택 run: sudo xcode-select -s /Applications/Xcode_16.2.app + - name: ⚙️ Generate xcconfig + run: | + cat < Poppool/Poppool/Resource/Debug.xcconfig + KAKAO_AUTH_APP_KEY=${{ secrets.KAKAO_AUTH_APP_KEY }} + NAVER_MAP_CLIENT_ID=${{ secrets.NAVER_MAP_CLIENT_ID }} + POPPOOL_BASE_URL=${{ secrets.POPPOOL_BASE_URL }} + POPPOOL_S3_BASE_URL=${{ secrets.POPPOOL_S3_BASE_URL }} + POPPOOL_API_KEY=${{ secrets.POPPOOL_API_KEY }} + EOF + - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 run: | security create-keychain -p "" "$KEYCHAIN" diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 91185e2b..e9518457 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3359,7 +3359,6 @@ BD9103652CF6149D00BBCCAE /* HomeAPIEndpoint.swift in Sources */, 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */, 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */, - 083A258D2CF361F90099B58E /* ConventionCollectionViewCell.swift in Sources */, 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */, 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */, 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */, @@ -3676,6 +3675,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Apple Distribution"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -3706,7 +3706,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3732,7 +3732,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -3750,7 +3750,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3776,7 +3776,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From 60ff137eb1dd9a061dae3ca00398ccf5e0b93320 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 20:38:42 +0900 Subject: [PATCH 076/393] =?UTF-8?q?fix/#103:=20=EB=B9=A0=EC=A0=B8=EC=9E=88?= =?UTF-8?q?=EB=8D=98=20=EC=B6=94=EC=B6=9C=20=EC=98=B5=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EC=A0=81?= =?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/secrets/ExportOptions.plist | 29 +++++++++++++++++++++++++ .github/workflows/deploy_on_release.yml | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/secrets/ExportOptions.plist diff --git a/.github/secrets/ExportOptions.plist b/.github/secrets/ExportOptions.plist new file mode 100644 index 00000000..940c9086 --- /dev/null +++ b/.github/secrets/ExportOptions.plist @@ -0,0 +1,29 @@ + + + + + destination + export + manageAppVersionAndBuildNumber + + method + app-store-connect + provisioningProfiles + + com.poppoolIOS.poppool + PoppoolGitHubAction + + signingCertificate + 82F980617C0479150A4BCB89DC90498DCB319F8F + signingStyle + manual + stripSwiftSymbols + + teamID + W5QTRMS954 + testFlightInternalTestingOnly + + uploadSymbols + + + diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 0d49db4d..9e4ca74f 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -84,7 +84,7 @@ jobs: - name: ⬆️ Export app # export 를 통해 ipa 파일 만듦 run: | - xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist ExportOptions.plist -exportPath . -allowProvisioningUpdates + xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist .github/secrets/ExportOptions.plist -exportPath . -allowProvisioningUpdates - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 uses: apple-actions/upload-testflight-build@v1 From e7aea50c2ec66492d243307341fda95d74576d45 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 21:25:20 +0900 Subject: [PATCH 077/393] =?UTF-8?q?fix/#103:=20ipa=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=EB=AA=85=20=EC=B6=94=EC=A0=81=ED=95=98=EB=8F=84=EB=A1=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 --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 9e4ca74f..aa6896da 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -89,7 +89,7 @@ jobs: - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 uses: apple-actions/upload-testflight-build@v1 with: - app-path: '${{ env.PROJECT_NAME }}.ipa' + app-path: 'Poppool.ipa' issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} From 93827b100fc991ee4e35803c49f45b00acc714e5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 21:32:12 +0900 Subject: [PATCH 078/393] =?UTF-8?q?fix/#103:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=A4=91=EB=B3=B5=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 테플에서 중복된 빌드 번호가 중복될 경우 배포가 안되는 문제를 해결하기 위해 빌드번호를 자동으로 추가하도록 수정 --- .github/workflows/deploy_on_release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index aa6896da..9f7493f6 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -78,6 +78,11 @@ jobs: cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" done + - name: 🔢 Bump Build Number + run: | + BUILD_NUMBER=$(date +%s) + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Info.plist + - name: ⬇️ Archive app # 빌드 및 아카이브 run: | xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE From 0be45d3ab0e24e52f695d8fb9c50880951db6548 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 21:35:39 +0900 Subject: [PATCH 079/393] =?UTF-8?q?fix/#103:=20info.plsit=20=EA=B2=BD?= =?UTF-8?q?=EB=A1=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 9f7493f6..e8539296 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -81,7 +81,7 @@ jobs: - name: 🔢 Bump Build Number run: | BUILD_NUMBER=$(date +%s) - /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Info.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Resource/Info.plist - name: ⬇️ Archive app # 빌드 및 아카이브 run: | From 831807da86198bbbeb232426211de76f0d0ea683 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 21:52:45 +0900 Subject: [PATCH 080/393] =?UTF-8?q?fix/#103:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EC=9E=90=EB=8F=99=20=EC=A6=9D=EA=B0=80=20?= =?UTF-8?q?run=20script=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빌드 번호 증가를 위한 Actions step 제거 --- .github/workflows/deploy_on_release.yml | 5 ----- Poppool/Poppool.xcodeproj/project.pbxproj | 19 +++++++++++++++++ Poppool/Poppool/Resource/Info.plist | 26 ++++++++++++++--------- 3 files changed, 35 insertions(+), 15 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index e8539296..aa6896da 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -78,11 +78,6 @@ jobs: cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" done - - name: 🔢 Bump Build Number - run: | - BUILD_NUMBER=$(date +%s) - /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Resource/Info.plist - - name: ⬇️ Archive app # 빌드 및 아카이브 run: | xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index e9518457..3a4203bc 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -2992,6 +2992,7 @@ buildConfigurationList = BDCA41E72CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "Poppool" */; buildPhases = ( 05229DCF2D99507C00D88E73 /* Run SwiftLint */, + 051E7D992DA6A23600F92DA8 /* Run Bump build version */, BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, BDCA41BB2CF35AC0005EECF6 /* Resources */, @@ -3100,6 +3101,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 051E7D992DA6A23600F92DA8 /* Run Bump build version */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run Bump build version"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\n\nINFO_PLIST=\"$INFOPLIST_FILE\"\n\n# 기존 값 가져오기\nbuildDay=$(/usr/libexec/PlistBuddy -c \"Print buildDay\" \"$INFO_PLIST\" 2>/dev/null)\nbuildCount=$(/usr/libexec/PlistBuddy -c \"Print buildCount\" \"$INFO_PLIST\" 2>/dev/null)\ntoday=$(date +%Y%m%d)\n\n# 첫 실행 또는 키 없음\nif [ -z \"$buildDay\" ]; then\n buildDay=$today\n buildCount=1\nelse\n if [ \"$buildDay\" != \"$today\" ]; then\n buildDay=$today\n buildCount=1\n else\n buildCount=$((buildCount + 1))\n fi\nfi\n\nprintf -v zeroPadBuildCount \"%03d\" $buildCount\nbuildNumber=\"${buildDay}${zeroPadBuildCount}\"\n\n# Info.plist 업데이트\n/usr/libexec/PlistBuddy -c \"Delete buildDay\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add buildDay string $buildDay\" \"$INFO_PLIST\"\n\n/usr/libexec/PlistBuddy -c \"Delete buildCount\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add buildCount string $buildCount\" \"$INFO_PLIST\"\n\n/usr/libexec/PlistBuddy -c \"Delete CFBundleVersion\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add CFBundleVersion string $buildNumber\" \"$INFO_PLIST\"\n\necho \"✅ CFBundleVersion set to $buildNumber\"\n"; + }; 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index c4884906..d5ec3550 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,16 +2,6 @@ - KAKAO_AUTH_APP_KEY - ${KAKAO_AUTH_APP_KEY} - POPPOOL_BASE_URL - ${POPPOOL_BASE_URL} - POPPOOL_S3_BASE_URL - ${POPPOOL_S3_BASE_URL} - POPPOOL_API_KEY - ${POPPOOL_API_KEY} - NAVER_MAP_CLIENT_ID - ${NAVER_MAP_CLIENT_ID} CFBundleURLTypes @@ -23,8 +13,12 @@ + CFBundleVersion + 20250409004 ITSAppUsesNonExemptEncryption + KAKAO_AUTH_APP_KEY + ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes instagram @@ -35,6 +29,14 @@ kakaokompassauth kakaotalk + NAVER_MAP_CLIENT_ID + ${NAVER_MAP_CLIENT_ID} + POPPOOL_API_KEY + ${POPPOOL_API_KEY} + POPPOOL_BASE_URL + ${POPPOOL_BASE_URL} + POPPOOL_S3_BASE_URL + ${POPPOOL_S3_BASE_URL} UIAppFonts GothicA1-Bold.ttf @@ -63,5 +65,9 @@ + buildCount + 4 + buildDay + 20250409 From 50c8334fc2a0f98d470b80d10ae707603fbb0887 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:13:35 +0900 Subject: [PATCH 081/393] =?UTF-8?q?fix/#103:=20=EB=B9=8C=EB=93=9C=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EC=A6=9D=EA=B0=80=20Run=20Script=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 - 같이 변경된 info도 동반할게요 --- Poppool/Poppool.xcodeproj/project.pbxproj | 2 +- Poppool/Poppool/Resource/Info.plist | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 3a4203bc..154f7baf 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3117,7 +3117,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "#!/bin/bash\n\nINFO_PLIST=\"$INFOPLIST_FILE\"\n\n# 기존 값 가져오기\nbuildDay=$(/usr/libexec/PlistBuddy -c \"Print buildDay\" \"$INFO_PLIST\" 2>/dev/null)\nbuildCount=$(/usr/libexec/PlistBuddy -c \"Print buildCount\" \"$INFO_PLIST\" 2>/dev/null)\ntoday=$(date +%Y%m%d)\n\n# 첫 실행 또는 키 없음\nif [ -z \"$buildDay\" ]; then\n buildDay=$today\n buildCount=1\nelse\n if [ \"$buildDay\" != \"$today\" ]; then\n buildDay=$today\n buildCount=1\n else\n buildCount=$((buildCount + 1))\n fi\nfi\n\nprintf -v zeroPadBuildCount \"%03d\" $buildCount\nbuildNumber=\"${buildDay}${zeroPadBuildCount}\"\n\n# Info.plist 업데이트\n/usr/libexec/PlistBuddy -c \"Delete buildDay\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add buildDay string $buildDay\" \"$INFO_PLIST\"\n\n/usr/libexec/PlistBuddy -c \"Delete buildCount\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add buildCount string $buildCount\" \"$INFO_PLIST\"\n\n/usr/libexec/PlistBuddy -c \"Delete CFBundleVersion\" \"$INFO_PLIST\" 2>/dev/null\n/usr/libexec/PlistBuddy -c \"Add CFBundleVersion string $buildNumber\" \"$INFO_PLIST\"\n\necho \"✅ CFBundleVersion set to $buildNumber\"\n"; + shellScript = "buildDay=$(/usr/libexec/PlistBuddy -c \"Print buildDay\" \"$INFOPLIST_FILE\")\nbuildCount=$(/usr/libexec/PlistBuddy -c \"Print buildCount\" \"$INFOPLIST_FILE\")\ntoday=$(date +%Y%m%d)\n\nif [ x$buildDay == x ]; then\n buildDay=${today}\n buildCount=1\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Add :buildDay string $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Add :buildCount string $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nelif [ $buildDay != $today ]; then\n buildDay=${today}\n buildCount=1\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Set :buildDay $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :buildCount $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nelse\n buildCount=$(($buildCount + 1))\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Set :buildDay $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :buildCount $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nfi\n"; }; 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index d5ec3550..194184c8 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -14,7 +14,7 @@ CFBundleVersion - 20250409004 + 2025040911 ITSAppUsesNonExemptEncryption KAKAO_AUTH_APP_KEY @@ -66,7 +66,7 @@ buildCount - 4 + 11 buildDay 20250409 From ad0c4eb5c4ff9c7c064ee048a93dc2087029b4ea Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:25:46 +0900 Subject: [PATCH 082/393] =?UTF-8?q?chore/#103:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20echo=ED=95=98=EB=8F=84=EB=A1=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 --- .github/workflows/deploy_on_release.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index aa6896da..620e9725 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -86,6 +86,10 @@ jobs: run: | xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist .github/secrets/ExportOptions.plist -exportPath . -allowProvisioningUpdates + - name: 🔍 Check final CFBundleVersion + run: | + /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist + - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 uses: apple-actions/upload-testflight-build@v1 with: From ec97e621bac410b207296b4aa78424553e010fe8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:40:31 +0900 Subject: [PATCH 083/393] =?UTF-8?q?fix/#103:=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=B2=88=ED=98=B8=20=EC=A6=9D=EA=B0=80=EB=A5=BC=20CD=EC=97=90?= =?UTF-8?q?=20=EB=A7=A1=EA=B8=B0=EB=8F=84=EB=A1=9D=20=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 5 +++++ Poppool/Poppool.xcodeproj/project.pbxproj | 19 ------------------- Poppool/Poppool/Resource/Info.plist | 6 ------ 3 files changed, 5 insertions(+), 25 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 620e9725..30823858 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -78,6 +78,11 @@ jobs: cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" done + - name: 🔢 Set Build Number to datetime + run: | + BUILD_NUMBER=$(date +%Y%m%d%H%M) + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/ResourceInfo.plist + - name: ⬇️ Archive app # 빌드 및 아카이브 run: | xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 154f7baf..e9518457 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -2992,7 +2992,6 @@ buildConfigurationList = BDCA41E72CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "Poppool" */; buildPhases = ( 05229DCF2D99507C00D88E73 /* Run SwiftLint */, - 051E7D992DA6A23600F92DA8 /* Run Bump build version */, BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, BDCA41BB2CF35AC0005EECF6 /* Resources */, @@ -3101,24 +3100,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 051E7D992DA6A23600F92DA8 /* Run Bump build version */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run Bump build version"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "buildDay=$(/usr/libexec/PlistBuddy -c \"Print buildDay\" \"$INFOPLIST_FILE\")\nbuildCount=$(/usr/libexec/PlistBuddy -c \"Print buildCount\" \"$INFOPLIST_FILE\")\ntoday=$(date +%Y%m%d)\n\nif [ x$buildDay == x ]; then\n buildDay=${today}\n buildCount=1\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Add :buildDay string $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Add :buildCount string $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nelif [ $buildDay != $today ]; then\n buildDay=${today}\n buildCount=1\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Set :buildDay $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :buildCount $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nelse\n buildCount=$(($buildCount + 1))\n buildNumber=${buildDay}${buildCount}\n\n /usr/libexec/PlistBuddy -c \"Set :buildDay $buildDay\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :buildCount $buildCount\" \"$INFOPLIST_FILE\"\n /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $buildNumber\" \"$INFOPLIST_FILE\"\n\nfi\n"; - }; 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 194184c8..ab02d47f 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -13,8 +13,6 @@ - CFBundleVersion - 2025040911 ITSAppUsesNonExemptEncryption KAKAO_AUTH_APP_KEY @@ -65,9 +63,5 @@ - buildCount - 11 - buildDay - 20250409 From c8447cc3f5612b6122f9d0039be53d31f20cda20 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:42:36 +0900 Subject: [PATCH 084/393] =?UTF-8?q?chore/#103:=20=EC=A3=BC=EC=84=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 --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 30823858..95812bf2 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -78,7 +78,7 @@ jobs: cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" done - - name: 🔢 Set Build Number to datetime + - name: 🔢 Set Build Number # 빌드 번호 자동 수정 run: | BUILD_NUMBER=$(date +%Y%m%d%H%M) /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/ResourceInfo.plist From 8233967f63396558d118044f31954eb6504af493 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:44:22 +0900 Subject: [PATCH 085/393] =?UTF-8?q?fix/#103:=20plist=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 95812bf2..036a3a30 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -81,7 +81,7 @@ jobs: - name: 🔢 Set Build Number # 빌드 번호 자동 수정 run: | BUILD_NUMBER=$(date +%Y%m%d%H%M) - /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/ResourceInfo.plist + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Resource/Info.plist - name: ⬇️ Archive app # 빌드 및 아카이브 run: | From d5333c196c4e62c5652c4d026fa44553d546cf27 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 22:51:34 +0900 Subject: [PATCH 086/393] =?UTF-8?q?fix/#103:=20=EB=B2=88=EB=93=A4=20?= =?UTF-8?q?=EB=B2=84=EC=A0=84=20=EC=97=86=EC=9C=BC=EB=A9=B4=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=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 --- .github/workflows/deploy_on_release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 036a3a30..dc037a12 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -81,7 +81,13 @@ jobs: - name: 🔢 Set Build Number # 빌드 번호 자동 수정 run: | BUILD_NUMBER=$(date +%Y%m%d%H%M) - /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" Poppool/Poppool/Resource/Info.plist + PLIST_PATH="Poppool/Poppool/Resource/Info.plist" + + if /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$PLIST_PATH"; then + /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$PLIST_PATH" + else + /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $BUILD_NUMBER" "$PLIST_PATH" + fi - name: ⬇️ Archive app # 빌드 및 아카이브 run: | From c18a7d40e7156ce4cc423319299d47942775909f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 9 Apr 2025 23:03:18 +0900 Subject: [PATCH 087/393] =?UTF-8?q?fix/#103:=20=EC=84=A4=EB=A7=88=20?= =?UTF-8?q?=EB=84=88=EB=83=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index e9518457..74b1e0a7 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3708,7 +3708,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -3752,7 +3752,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; From 58205d21ba39fb61cc8d20d0bba6edac00c1cc4c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 10 Apr 2025 02:27:43 +0900 Subject: [PATCH 088/393] =?UTF-8?q?fix/#103:=20=EC=9E=90=EB=8F=99=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=EB=B2=88=ED=98=B8=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=A0=95=EC=83=81=ED=99=94(=EC=B0=90=EB=A7=89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 빌드 번호 자동 증가를 run script에서 하도록 수정 - run script는 archive 동작에만 변경되도록 수정 - info.plist에 CFBundleVersion, CFBundleShortVersion 추가 --- .github/workflows/deploy_on_release.yml | 11 --------- Poppool/Poppool.xcodeproj/project.pbxproj | 27 +++++++++++++++++++---- Poppool/Poppool/Resource/Info.plist | 6 +++-- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index dc037a12..620e9725 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -78,17 +78,6 @@ jobs: cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" done - - name: 🔢 Set Build Number # 빌드 번호 자동 수정 - run: | - BUILD_NUMBER=$(date +%Y%m%d%H%M) - PLIST_PATH="Poppool/Poppool/Resource/Info.plist" - - if /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$PLIST_PATH"; then - /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$PLIST_PATH" - else - /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $BUILD_NUMBER" "$PLIST_PATH" - fi - - name: ⬇️ Archive app # 빌드 및 아카이브 run: | xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 74b1e0a7..b031f273 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -2991,6 +2991,7 @@ isa = PBXNativeTarget; buildConfigurationList = BDCA41E72CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "Poppool" */; buildPhases = ( + 051E7DD22DA6DD8100F92DA8 /* Run Build Bump Script */, 05229DCF2D99507C00D88E73 /* Run SwiftLint */, BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, @@ -3100,6 +3101,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 051E7DD22DA6DD8100F92DA8 /* Run Build Bump Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Run Build Bump Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\n\n# Only run during Archive\nif [ \"$ACTION\" != \"install\" ]; then\n echo \"Skipping build number bump (not archiving).\"\n exit 0\nfi\n\n# Get current date in YYMMDD format\ncurrentDate=$(date +%y%m%d)\n\n# Path to Info.plist\nplistPath=\"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n\n# Get current build number from plist\ncurrentBuild=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$plistPath\" 2>/dev/null)\n\n# Extract previous date and count if formatted correctly\nif [[ \"$currentBuild\" == *\".\"* ]]; then\n previousDate=$(echo \"$currentBuild\" | cut -d '.' -f 1)\n previousCount=$(echo \"$currentBuild\" | cut -d '.' -f 2)\nelse\n previousDate=\"\"\n previousCount=0\nfi\n\n# If the date has changed, reset count to 1, otherwise increment\nif [[ \"$previousDate\" == \"$currentDate\" ]]; then\n newCount=$((previousCount + 1))\nelse\n newCount=1\nfi\n\n# Combine into new build number\nnewBuild=\"${currentDate}.${newCount}\"\n\n# Set new build number in plist\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $newBuild\" \"$plistPath\"\n\necho \"Build number set to $newBuild\"\n"; + }; 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -3708,12 +3727,12 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "사용자의 현재 위치를 기반으로 주변 게시글을 추천하는 데 사용됩니다."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 사진라이브러리에 접근합니다."; @@ -3728,7 +3747,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -3752,12 +3771,12 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; + INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "사용자의 현재 위치를 기반으로 주변 게시글을 추천하는 데 사용됩니다."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 사진라이브러리에 접근합니다."; @@ -3772,7 +3791,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; 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 ab02d47f..2529719b 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,6 +2,8 @@ + CFBundleShortVersionString + $(MARKETING_VERSION) CFBundleURLTypes @@ -13,8 +15,8 @@ - ITSAppUsesNonExemptEncryption - + CFBundleVersion + 250410.3 KAKAO_AUTH_APP_KEY ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes From 6d463b9bf2b58fdbf8c30150c35138ca3b386700 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 10 Apr 2025 23:20:54 +0900 Subject: [PATCH 089/393] =?UTF-8?q?fix/#103:=20agvtool=EC=9D=84=20?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=ED=95=B4=EC=84=9C=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=EB=B2=94=ED=94=84=EB=A5=BC=20=ED=95=98=EB=8F=84=EB=A1=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 - Bundle version이 CFBundleVersion을 따르지 않았음 - Info를 기반으로 pbxproj를 수정하는 방식은 옳지 않다 생각했음 - 커스텀된 빌드 버전 넘버링을 버리고 agvtool을 이용하도록 수정 --- .github/workflows/deploy_on_release.yml | 3 +++ Poppool/Poppool.xcodeproj/project.pbxproj | 23 ++++------------------- Poppool/Poppool/Resource/Info.plist | 2 +- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 620e9725..f67a25a4 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -36,6 +36,9 @@ jobs: - name: 🛠️ Set up Xcode # Xcode 16.2 선택 run: sudo xcode-select -s /Applications/Xcode_16.2.app + - name: 📈 Bump Bundle Version # agvtool을 사용하여 번들 버전을 자동 증가시킴 + run: agvtool next-version + - name: ⚙️ Generate xcconfig run: | cat < Poppool/Poppool/Resource/Debug.xcconfig diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index b031f273..99403291 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -2991,7 +2991,6 @@ isa = PBXNativeTarget; buildConfigurationList = BDCA41E72CF35AC2005EECF6 /* Build configuration list for PBXNativeTarget "Poppool" */; buildPhases = ( - 051E7DD22DA6DD8100F92DA8 /* Run Build Bump Script */, 05229DCF2D99507C00D88E73 /* Run SwiftLint */, BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, @@ -3101,24 +3100,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 051E7DD22DA6DD8100F92DA8 /* Run Build Bump Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "Run Build Bump Script"; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "#!/bin/bash\n\n# Only run during Archive\nif [ \"$ACTION\" != \"install\" ]; then\n echo \"Skipping build number bump (not archiving).\"\n exit 0\nfi\n\n# Get current date in YYMMDD format\ncurrentDate=$(date +%y%m%d)\n\n# Path to Info.plist\nplistPath=\"${PROJECT_DIR}/${INFOPLIST_FILE}\"\n\n# Get current build number from plist\ncurrentBuild=$(/usr/libexec/PlistBuddy -c \"Print CFBundleVersion\" \"$plistPath\" 2>/dev/null)\n\n# Extract previous date and count if formatted correctly\nif [[ \"$currentBuild\" == *\".\"* ]]; then\n previousDate=$(echo \"$currentBuild\" | cut -d '.' -f 1)\n previousCount=$(echo \"$currentBuild\" | cut -d '.' -f 2)\nelse\n previousDate=\"\"\n previousCount=0\nfi\n\n# If the date has changed, reset count to 1, otherwise increment\nif [[ \"$previousDate\" == \"$currentDate\" ]]; then\n newCount=$((previousCount + 1))\nelse\n newCount=1\nfi\n\n# Combine into new build number\nnewBuild=\"${currentDate}.${newCount}\"\n\n# Set new build number in plist\n/usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $newBuild\" \"$plistPath\"\n\necho \"Build number set to $newBuild\"\n"; - }; 05229DCF2D99507C00D88E73 /* Run SwiftLint */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -3727,6 +3708,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -3759,6 +3741,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -3771,6 +3754,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -3803,6 +3787,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 2529719b..5ac6908f 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -16,7 +16,7 @@ CFBundleVersion - 250410.3 + $(CURRENT_PROJECT_VERSION) KAKAO_AUTH_APP_KEY ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes From d674ede5449e9d40c0259b0a6934e4d40c5fee81 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 10 Apr 2025 23:27:47 +0900 Subject: [PATCH 090/393] =?UTF-8?q?fix/#103:=20=EC=95=BC=EB=9E=84=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B=20=EC=8B=9C=EC=9E=91=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - agvtool을 실행하기 위해 프로젝트 파일이 있는 경로로 이동하도록 수정 --- .github/workflows/deploy_on_release.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index f67a25a4..5d0247f4 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -37,7 +37,9 @@ jobs: run: sudo xcode-select -s /Applications/Xcode_16.2.app - name: 📈 Bump Bundle Version # agvtool을 사용하여 번들 버전을 자동 증가시킴 - run: agvtool next-version + run: | + cd Poppool + agvtool next-version - name: ⚙️ Generate xcconfig run: | From da384d9c3966275cdabeefebbf435c86153a0f85 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 10 Apr 2025 23:57:31 +0900 Subject: [PATCH 091/393] =?UTF-8?q?fix/#103:=20info.plist=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EB=94=94=EC=8A=A4=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9B=B9=ED=9B=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존에 수출 관리 규정 우회를 pbxproj에서 관리되던 것을 info.plist로 수정 - 테스트플라이트 배포 성공 시 Discord로 알림이 가도록 웹훅 추가 --- .github/workflows/deploy_on_release.yml | 29 +++++++++++++++++++++++ Poppool/Poppool.xcodeproj/project.pbxproj | 2 -- Poppool/Poppool/Resource/Info.plist | 2 ++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 5d0247f4..1b3ce513 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -102,3 +102,32 @@ jobs: issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} + + - name: 📣 Notify to Discord + if: success() + run: | + MARKETING_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist) + BUNDLE_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist) + + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [ + { + \"title\": \"🚀 TestFlight 배포 완료!\", + \"description\": \"앱이 성공적으로 업로드되었습니다.\", + \"fields\": [ + { + \"name\": \"🔖 마케팅 버전 (CFBundleShortVersionString)\", + \"value\": \"$MARKETING_VERSION\" + }, + { + \"name\": \"📦 빌드 버전 (CFBundleVersion)\", + \"value\": \"$BUNDLE_VERSION\" + } + ], + \"color\": 3066993 + } + ] + }" \ + ${{ secrets.TESTFLIGHT_WEBHOOK_URL }} diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 99403291..22a67e02 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3714,7 +3714,6 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; - INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "사용자의 현재 위치를 기반으로 주변 게시글을 추천하는 데 사용됩니다."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 사진라이브러리에 접근합니다."; @@ -3760,7 +3759,6 @@ ENABLE_USER_SCRIPT_SANDBOXING = NO; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Poppool/Resource/Info.plist; - INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; INFOPLIST_KEY_NSCameraUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 카메라를 사용합니다."; INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = "사용자의 현재 위치를 기반으로 주변 게시글을 추천하는 데 사용됩니다."; INFOPLIST_KEY_NSPhotoLibraryUsageDescription = "사용자가 프로필 사진을 업로드하거나 댓글에 사진을 추가할 수 있도록 사진라이브러리에 접근합니다."; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 5ac6908f..560ae1a9 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,6 +2,8 @@ + ITSAppUsesNonExemptEncryption + CFBundleShortVersionString $(MARKETING_VERSION) CFBundleURLTypes From d40cebadcac6cee9b6c96068dd716cf9791ae40f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 11 Apr 2025 00:26:50 +0900 Subject: [PATCH 092/393] =?UTF-8?q?fix/#103:=20=EC=88=98=EB=8F=99=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=EB=84=98=EB=B2=84=EB=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=ED=95=B4=EC=95=BC=EB=90=A0=EA=B2=83=20=EA=B0=99?= =?UTF-8?q?=EC=9D=8C...=20=E3=85=A0=E3=85=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 5 +---- Poppool/Poppool.xcodeproj/project.pbxproj | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 1b3ce513..82a2e41b 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -36,10 +36,7 @@ jobs: - name: 🛠️ Set up Xcode # Xcode 16.2 선택 run: sudo xcode-select -s /Applications/Xcode_16.2.app - - name: 📈 Bump Bundle Version # agvtool을 사용하여 번들 버전을 자동 증가시킴 - run: | - cd Poppool - agvtool next-version + # 추후에 자동 빌드 넘버링 추가가 필요(뭔 짓을 해도 안돼서 포기... ㅠㅠ) - name: ⚙️ Generate xcconfig run: | diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 22a67e02..f3dc67d0 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3708,7 +3708,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -3753,7 +3753,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; + CURRENT_PROJECT_VERSION = 3; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; From 3ffa2013d8fbed38ef3a64b4cf2ca9ae591c3cf6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 11 Apr 2025 01:33:23 +0900 Subject: [PATCH 093/393] =?UTF-8?q?fix/#103:=20=EC=97=AC=EB=9F=AC=EA=B0=80?= =?UTF-8?q?=EC=A7=80=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95(=EC=84=A4?= =?UTF-8?q?=EB=AA=85=20=ED=99=95=EC=9D=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 자동 빌드 번호 부여하기 찐막 - 웹훅 단순화 - xcconfig가 제대로 적용되지 않는 문제 수정(테플에서 키가 제대로 안먹었음) --- .github/workflows/deploy_on_release.yml | 32 +++++++++-------------- Poppool/Poppool.xcodeproj/project.pbxproj | 4 +-- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 82a2e41b..afddd19e 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -35,17 +35,21 @@ jobs: - name: 🛠️ Set up Xcode # Xcode 16.2 선택 run: sudo xcode-select -s /Applications/Xcode_16.2.app - - # 추후에 자동 빌드 넘버링 추가가 필요(뭔 짓을 해도 안돼서 포기... ㅠㅠ) + + - name: "#️⃣ Set Build Number" + run: | + BUILD_NUMBER=$(date +%y%m%d.%H%M) + cd Poppool + agvtool new-version -all "$BUILD_NUMBER" - name: ⚙️ Generate xcconfig run: | cat < Poppool/Poppool/Resource/Debug.xcconfig - KAKAO_AUTH_APP_KEY=${{ secrets.KAKAO_AUTH_APP_KEY }} - NAVER_MAP_CLIENT_ID=${{ secrets.NAVER_MAP_CLIENT_ID }} - POPPOOL_BASE_URL=${{ secrets.POPPOOL_BASE_URL }} - POPPOOL_S3_BASE_URL=${{ secrets.POPPOOL_S3_BASE_URL }} - POPPOOL_API_KEY=${{ secrets.POPPOOL_API_KEY }} + POPPOOL_BASE_URL = ${{ secrets.POPPOOL_BASE_URL }} + POPPOOL_S3_BASE_URL = ${{ secrets.POPPOOL_S3_BASE_URL }} + POPPOOL_API_KEY = ${{ secrets.POPPOOL_API_KEY }} + KAKAO_AUTH_APP_KEY = ${{ secrets.KAKAO_AUTH_APP_KEY }} + NAVER_MAP_CLIENT_ID = ${{ secrets.NAVER_MAP_CLIENT_ID }} EOF - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 @@ -111,18 +115,8 @@ jobs: -d "{ \"embeds\": [ { - \"title\": \"🚀 TestFlight 배포 완료!\", - \"description\": \"앱이 성공적으로 업로드되었습니다.\", - \"fields\": [ - { - \"name\": \"🔖 마케팅 버전 (CFBundleShortVersionString)\", - \"value\": \"$MARKETING_VERSION\" - }, - { - \"name\": \"📦 빌드 버전 (CFBundleVersion)\", - \"value\": \"$BUNDLE_VERSION\" - } - ], + \"title\": \"## 🚀 TestFlight 배포 완료\", + \"description\": \"v$MARKETING_VERSION ($BUNDLE_VERSION)\", \"color\": 3066993 } ] diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index f3dc67d0..22a67e02 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3708,7 +3708,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; @@ -3753,7 +3753,7 @@ CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; ENABLE_USER_SCRIPT_SANDBOXING = NO; From 3b064c46ef5534ffc2bf163d56682a1f4a61a347 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 11 Apr 2025 01:56:49 +0900 Subject: [PATCH 094/393] =?UTF-8?q?debug/#103:=20xcconfig=EA=B0=80=20?= =?UTF-8?q?=EC=9E=98=20=EC=A0=81=EC=9A=A9=EB=90=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EB=8A=94=EA=B2=83=EC=9C=BC=EB=A1=9C=20=EB=B3=B4=EC=9D=B4?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EB=94=94=EB=B2=84=EA=B9=85?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 27 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index afddd19e..99f83c8b 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -38,20 +38,29 @@ jobs: - name: "#️⃣ Set Build Number" run: | - BUILD_NUMBER=$(date +%y%m%d.%H%M) + BUILD_NUMBER=$(TZ=Asia/Seoul date +%y%m%d.%H%M) cd Poppool agvtool new-version -all "$BUILD_NUMBER" - name: ⚙️ Generate xcconfig run: | - cat < Poppool/Poppool/Resource/Debug.xcconfig - POPPOOL_BASE_URL = ${{ secrets.POPPOOL_BASE_URL }} - POPPOOL_S3_BASE_URL = ${{ secrets.POPPOOL_S3_BASE_URL }} - POPPOOL_API_KEY = ${{ secrets.POPPOOL_API_KEY }} - KAKAO_AUTH_APP_KEY = ${{ secrets.KAKAO_AUTH_APP_KEY }} - NAVER_MAP_CLIENT_ID = ${{ secrets.NAVER_MAP_CLIENT_ID }} - EOF - + echo "POPPOOL_BASE_URL=${POPPOOL_BASE_URL}" > Poppool/Poppool/Resource/Debug.xcconfig + echo "POPPOOL_S3_BASE_URL=${POPPOOL_S3_BASE_URL}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "POPPOOL_API_KEY=${POPPOOL_API_KEY}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "KAKAO_AUTH_APP_KEY=${KAKAO_AUTH_APP_KEY}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "NAVER_MAP_CLIENT_ID=${NAVER_MAP_CLIENT_ID}" >> Poppool/Poppool/Resource/Debug.xcconfig + env: + POPPOOL_BASE_URL: ${{ secrets.POPPOOL_BASE_URL }} + POPPOOL_S3_BASE_URL: ${{ secrets.POPPOOL_S3_BASE_URL }} + POPPOOL_API_KEY: ${{ secrets.POPPOOL_API_KEY }} + KAKAO_AUTH_APP_KEY: ${{ secrets.KAKAO_AUTH_APP_KEY }} + NAVER_MAP_CLIENT_ID: ${{ secrets.NAVER_MAP_CLIENT_ID }} + + - name: 🔒 Verify xcconfig keys (masked) + run: | + echo "Masked xcconfig preview:" + awk -F= '{prefix=substr($2, 1, 8); masked="****************"; print $1 "=" prefix masked}' Poppool/Poppool/Resource/Debug.xcconfig + - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 run: | security create-keychain -p "" "$KEYCHAIN" From a4f3b0aa9adf68c603aca2c3641aeade28dbfe44 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 11 Apr 2025 02:21:54 +0900 Subject: [PATCH 095/393] =?UTF-8?q?refactor/#103:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20step=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20em?= =?UTF-8?q?bed=20=EB=82=B4=EC=9A=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy_on_release.yml | 36 ++++++++++++++----------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 99f83c8b..7c12c739 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -36,13 +36,13 @@ jobs: - name: 🛠️ Set up Xcode # Xcode 16.2 선택 run: sudo xcode-select -s /Applications/Xcode_16.2.app - - name: "#️⃣ Set Build Number" + - name: "#️⃣ Set Build Number" # 자동 빌드 넘버 세팅 run: | BUILD_NUMBER=$(TZ=Asia/Seoul date +%y%m%d.%H%M) cd Poppool agvtool new-version -all "$BUILD_NUMBER" - - name: ⚙️ Generate xcconfig + - name: ⚙️ Generate xcconfig # 빌드에 필요한 xcconfig 생성 run: | echo "POPPOOL_BASE_URL=${POPPOOL_BASE_URL}" > Poppool/Poppool/Resource/Debug.xcconfig echo "POPPOOL_S3_BASE_URL=${POPPOOL_S3_BASE_URL}" >> Poppool/Poppool/Resource/Debug.xcconfig @@ -55,11 +55,6 @@ jobs: POPPOOL_API_KEY: ${{ secrets.POPPOOL_API_KEY }} KAKAO_AUTH_APP_KEY: ${{ secrets.KAKAO_AUTH_APP_KEY }} NAVER_MAP_CLIENT_ID: ${{ secrets.NAVER_MAP_CLIENT_ID }} - - - name: 🔒 Verify xcconfig keys (masked) - run: | - echo "Masked xcconfig preview:" - awk -F= '{prefix=substr($2, 1, 8); masked="****************"; print $1 "=" prefix masked}' Poppool/Poppool/Resource/Debug.xcconfig - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 run: | @@ -69,7 +64,7 @@ jobs: security unlock-keychain -p "" "$KEYCHAIN" security set-keychain-settings - - name : ©️ Configure Code Signing + - name : ©️ Configure Code Signing # 코드 사이닝 추가 run: | # certificate 복호화 gpg -d -o "$DECRYPTED_CERT_FILE_PATH" --pinentry-mode=loopback --passphrase "$CERT_ENCRYPTION_KEY" "$ENCRYPTED_CERT_FILE_PATH" @@ -101,10 +96,6 @@ jobs: run: | xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist .github/secrets/ExportOptions.plist -exportPath . -allowProvisioningUpdates - - name: 🔍 Check final CFBundleVersion - run: | - /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist - - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 uses: apple-actions/upload-testflight-build@v1 with: @@ -124,9 +115,24 @@ jobs: -d "{ \"embeds\": [ { - \"title\": \"## 🚀 TestFlight 배포 완료\", - \"description\": \"v$MARKETING_VERSION ($BUNDLE_VERSION)\", - \"color\": 3066993 + \"title\": \"🚀 TestFlight 배포 완료\", + \"description\": \"Poppool 앱이 성공적으로 TestFlight에 업로드되었습니다!\", + \"color\": 3066993, + \"fields\": [ + { + \"name\": \"🏷️ 마케팅 버전\", + \"value\": \"$MARKETING_VERSION\", + \"inline\": true + }, + { + \"name\": \"🛠️ 빌드 번호\", + \"value\": \"$BUNDLE_VERSION\", + \"inline\": true + } + ], + \"footer\": { + \"text\": \"TestFlight에서 위 버전을 설치하세요\" + } } ] }" \ From afc8c8df89ef3a964b2139deb32dda46e8e8e3d2 Mon Sep 17 00:00:00 2001 From: YeongHoon Song Date: Wed, 9 Apr 2025 19:55:48 +0900 Subject: [PATCH 096/393] =?UTF-8?q?feat/#103:=20TestFlight=20=EB=B0=B0?= =?UTF-8?q?=ED=8F=AC=20=EC=9E=90=EB=8F=99=ED=99=94=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix/#103: 환경 변수를 찾지 못하는 문제 해결 - 프로젝트 이름 환경변수 제거 fix/#103: 프로비저닝 경로 이상 문제 해결 fix/#103: 파일 경로 다시 수정 - 멍청하게 파일명을 잘못 넣음 ㅠ fix/#103: 배포 자동화 문제 해결 - 자동화 코드에서 xcconfig를 생성하도록 수정 - 프로젝트 설정에 프로비저닝을 빌드용으로 변경 fix/#103: 빠져있던 추출 옵션 추가 및 파일 추적 경로 변경 fix/#103: ipa 파일명 추적하도록 수정 fix/#103: 빌드 번호 중복 문제 해결 - 테플에서 중복된 빌드 번호가 중복될 경우 배포가 안되는 문제를 해결하기 위해 빌드번호를 자동으로 추가하도록 수정 fix/#103: info.plsit 경로 문제 해결 fix/#103: 빌드 버전 자동 증가 run script 추가 - 빌드 번호 증가를 위한 Actions step 제거 fix/#103: 빌드번호 증가 Run Script 수정 - 같이 변경된 info도 동반할게요 chore/#103: 빌드 번호 echo하도록 추가 fix/#103: 빌드 번호 증가를 CD에 맡기도록 원복 chore/#103: 주석 추가 fix/#103: plist경로 문제 해결 fix/#103: 번들 버전 없으면 추가되도록 수정 fix/#103: 설마 너냐 fix/#103: 자동 빌드 번호 수정 정상화(찐막) - 빌드 번호 자동 증가를 run script에서 하도록 수정 - run script는 archive 동작에만 변경되도록 수정 - info.plist에 CFBundleVersion, CFBundleShortVersion 추가 fix/#103: agvtool을 이용해서 빌드 범프를 하도록 수정 - Bundle version이 CFBundleVersion을 따르지 않았음 - Info를 기반으로 pbxproj를 수정하는 방식은 옳지 않다 생각했음 - 커스텀된 빌드 버전 넘버링을 버리고 agvtool을 이용하도록 수정 fix/#103: 야랄 커밋 시작함 - agvtool을 실행하기 위해 프로젝트 파일이 있는 경로로 이동하도록 수정 fix/#103: info.plist 수정 및 디스코드 웹훅 추가 - 기존에 수출 관리 규정 우회를 pbxproj에서 관리되던 것을 info.plist로 수정 - 테스트플라이트 배포 성공 시 Discord로 알림이 가도록 웹훅 추가 fix/#103: 수동 빌드 넘버링으로 해야될것 같음... ㅠㅠ fix/#103: 여러가지 에러 수정(설명 확인) - 자동 빌드 번호 부여하기 찐막 - 웹훅 단순화 - xcconfig가 제대로 적용되지 않는 문제 수정(테플에서 키가 제대로 안먹었음) debug/#103: xcconfig가 잘 적용되지 않는것으로 보이는 문제 디버깅중 refactor/#103: 불필요한 step 제거 및 embed 내용 수정 --- .github/secrets/ExportOptions.plist | 29 +++++ .github/workflows/deploy_on_release.yml | 139 ++++++++++++++++++++++ Poppool/Poppool.xcodeproj/project.pbxproj | 16 +-- Poppool/Poppool/Resource/Info.plist | 28 +++-- 4 files changed, 193 insertions(+), 19 deletions(-) create mode 100644 .github/secrets/ExportOptions.plist create mode 100644 .github/workflows/deploy_on_release.yml diff --git a/.github/secrets/ExportOptions.plist b/.github/secrets/ExportOptions.plist new file mode 100644 index 00000000..940c9086 --- /dev/null +++ b/.github/secrets/ExportOptions.plist @@ -0,0 +1,29 @@ + + + + + destination + export + manageAppVersionAndBuildNumber + + method + app-store-connect + provisioningProfiles + + com.poppoolIOS.poppool + PoppoolGitHubAction + + signingCertificate + 82F980617C0479150A4BCB89DC90498DCB319F8F + signingStyle + manual + stripSwiftSymbols + + teamID + W5QTRMS954 + testFlightInternalTestingOnly + + uploadSymbols + + + diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml new file mode 100644 index 00000000..7c12c739 --- /dev/null +++ b/.github/workflows/deploy_on_release.yml @@ -0,0 +1,139 @@ +name: Distribution to TestFlight + +on: + pull_request: + branches: [ release/* ] + +jobs: + deploy: + name: 🚀 Distribution to TestFlight Workflow + runs-on: macos-15 # 최신 macOS 15 환경에서 실행 + env: + # app archive 및 export 에 쓰일 환경 변수 설정 + XC_PROJECT: Poppool/Poppool.xcodeproj + XC_SCHEME: Poppool + XC_ARCHIVE: Poppool.xcarchive + + # certificate + ENCRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12.gpg' }} + DECRYPTED_CERT_FILE_PATH: ${{ '.github/secrets/certification.p12' }} + CERT_ENCRYPTION_KEY: ${{ secrets.CERT_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 + + # provisioning + ENCRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGitHubAction.mobileprovision.gpg' + DECRYPTED_PROVISION_FILE_PATH: '.github/secrets/PoppoolGitHubAction.mobileprovision' + PROVISIONING_ENCRYPTION_KEY: ${{ secrets.PROVISION_ENCRYPTION_PWD }} # gpg로 파일 암호화할 때 사용한 암호 + + # certification export key + CERT_EXPORT_KEY: ${{ secrets.CERT_EXPORT_PWD }} + + KEYCHAIN: ${{ 'test.keychain' }} + + steps: + - name: Checkout Repository # 저장소 코드 체크아웃 + uses: actions/checkout@v4 + + - name: 🛠️ Set up Xcode # Xcode 16.2 선택 + run: sudo xcode-select -s /Applications/Xcode_16.2.app + + - name: "#️⃣ Set Build Number" # 자동 빌드 넘버 세팅 + run: | + BUILD_NUMBER=$(TZ=Asia/Seoul date +%y%m%d.%H%M) + cd Poppool + agvtool new-version -all "$BUILD_NUMBER" + + - name: ⚙️ Generate xcconfig # 빌드에 필요한 xcconfig 생성 + run: | + echo "POPPOOL_BASE_URL=${POPPOOL_BASE_URL}" > Poppool/Poppool/Resource/Debug.xcconfig + echo "POPPOOL_S3_BASE_URL=${POPPOOL_S3_BASE_URL}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "POPPOOL_API_KEY=${POPPOOL_API_KEY}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "KAKAO_AUTH_APP_KEY=${KAKAO_AUTH_APP_KEY}" >> Poppool/Poppool/Resource/Debug.xcconfig + echo "NAVER_MAP_CLIENT_ID=${NAVER_MAP_CLIENT_ID}" >> Poppool/Poppool/Resource/Debug.xcconfig + env: + POPPOOL_BASE_URL: ${{ secrets.POPPOOL_BASE_URL }} + POPPOOL_S3_BASE_URL: ${{ secrets.POPPOOL_S3_BASE_URL }} + POPPOOL_API_KEY: ${{ secrets.POPPOOL_API_KEY }} + KAKAO_AUTH_APP_KEY: ${{ secrets.KAKAO_AUTH_APP_KEY }} + NAVER_MAP_CLIENT_ID: ${{ secrets.NAVER_MAP_CLIENT_ID }} + + - name: 🔑 Configure Keychain # 키체인 초기화 -> 임시 키체인 생성 + run: | + security create-keychain -p "" "$KEYCHAIN" + security list-keychains -s "$KEYCHAIN" + security default-keychain -s "$KEYCHAIN" + security unlock-keychain -p "" "$KEYCHAIN" + security set-keychain-settings + + - name : ©️ Configure Code Signing # 코드 사이닝 추가 + run: | + # certificate 복호화 + gpg -d -o "$DECRYPTED_CERT_FILE_PATH" --pinentry-mode=loopback --passphrase "$CERT_ENCRYPTION_KEY" "$ENCRYPTED_CERT_FILE_PATH" + + # provisioning 복호화 + gpg -d -o "$DECRYPTED_PROVISION_FILE_PATH" --pinentry-mode=loopback --passphrase "$PROVISIONING_ENCRYPTION_KEY" "$ENCRYPTED_PROVISION_FILE_PATH" + + # security를 사용하여 인증서와 개인 키를 새로 만든 키 체인으로 가져옴 + security import "$DECRYPTED_CERT_FILE_PATH" -k "$KEYCHAIN" -P "$CERT_EXPORT_KEY" -A + security set-key-partition-list -S apple-tool:,apple: -s -k "" "$KEYCHAIN" + + # Xcode에서 찾을 수 있는 프로비저닝 프로필 설치하기 위해 우선 프로비저닝 디렉토리를 생성 + mkdir -p "$HOME/Library/MobileDevice/Provisioning Profiles" + + # 디버깅 용 echo 명령어 + echo `ls .github/secrets/*.mobileprovision` + # 모든 프로비저닝 프로파일을 rename 하고 위에서 만든 디렉토리로 복사하는 과정 + for PROVISION in `ls .github/secrets/*.mobileprovision` + do + UUID=`/usr/libexec/PlistBuddy -c 'Print :UUID' /dev/stdin <<< $(security cms -D -i ./$PROVISION)` + cp "./$PROVISION" "$HOME/Library/MobileDevice/Provisioning Profiles/$UUID.mobileprovision" + done + + - name: ⬇️ Archive app # 빌드 및 아카이브 + run: | + xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE + + - name: ⬆️ Export app # export 를 통해 ipa 파일 만듦 + run: | + xcodebuild -exportArchive -archivePath $XC_ARCHIVE -exportOptionsPlist .github/secrets/ExportOptions.plist -exportPath . -allowProvisioningUpdates + + - name: 🚀 Upload app to TestFlight # TestFlight에 아카이브된 앱 등록 + uses: apple-actions/upload-testflight-build@v1 + with: + app-path: 'Poppool.ipa' + issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} + api-key-id: ${{ secrets.APPSTORE_API_KEY_ID }} + api-private-key: ${{ secrets.APPSTORE_API_PRIVATE_KEY }} + + - name: 📣 Notify to Discord + if: success() + run: | + MARKETING_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist) + BUNDLE_VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" Poppool.xcarchive/Products/Applications/Poppool.app/Info.plist) + + curl -H "Content-Type: application/json" \ + -X POST \ + -d "{ + \"embeds\": [ + { + \"title\": \"🚀 TestFlight 배포 완료\", + \"description\": \"Poppool 앱이 성공적으로 TestFlight에 업로드되었습니다!\", + \"color\": 3066993, + \"fields\": [ + { + \"name\": \"🏷️ 마케팅 버전\", + \"value\": \"$MARKETING_VERSION\", + \"inline\": true + }, + { + \"name\": \"🛠️ 빌드 번호\", + \"value\": \"$BUNDLE_VERSION\", + \"inline\": true + } + ], + \"footer\": { + \"text\": \"TestFlight에서 위 버전을 설치하세요\" + } + } + ] + }" \ + ${{ secrets.TESTFLIGHT_WEBHOOK_URL }} diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 91185e2b..22a67e02 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3359,7 +3359,6 @@ BD9103652CF6149D00BBCCAE /* HomeAPIEndpoint.swift in Sources */, 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */, 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */, - 083A258D2CF361F90099B58E /* ConventionCollectionViewCell.swift in Sources */, 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */, 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */, 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */, @@ -3676,6 +3675,7 @@ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "Apple Distribution"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; @@ -3706,7 +3706,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3728,11 +3728,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -3740,6 +3740,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; }; name = Debug; }; @@ -3750,7 +3751,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3772,11 +3773,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.0; + MARKETING_VERSION = 1.0.1; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -3784,6 +3785,7 @@ SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; + VERSIONING_SYSTEM = "apple-generic"; }; name = Release; }; diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index c4884906..560ae1a9 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -2,16 +2,10 @@ - KAKAO_AUTH_APP_KEY - ${KAKAO_AUTH_APP_KEY} - POPPOOL_BASE_URL - ${POPPOOL_BASE_URL} - POPPOOL_S3_BASE_URL - ${POPPOOL_S3_BASE_URL} - POPPOOL_API_KEY - ${POPPOOL_API_KEY} - NAVER_MAP_CLIENT_ID - ${NAVER_MAP_CLIENT_ID} + ITSAppUsesNonExemptEncryption + + CFBundleShortVersionString + $(MARKETING_VERSION) CFBundleURLTypes @@ -23,8 +17,10 @@ - ITSAppUsesNonExemptEncryption - + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + KAKAO_AUTH_APP_KEY + ${KAKAO_AUTH_APP_KEY} LSApplicationQueriesSchemes instagram @@ -35,6 +31,14 @@ kakaokompassauth kakaotalk + NAVER_MAP_CLIENT_ID + ${NAVER_MAP_CLIENT_ID} + POPPOOL_API_KEY + ${POPPOOL_API_KEY} + POPPOOL_BASE_URL + ${POPPOOL_BASE_URL} + POPPOOL_S3_BASE_URL + ${POPPOOL_S3_BASE_URL} UIAppFonts GothicA1-Bold.ttf From c6f406e8fb576f38a77571c14f29f34e544c4684 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Tue, 8 Apr 2025 10:02:40 +0900 Subject: [PATCH 097/393] =?UTF-8?q?refactor/#102:=20PopUpRegisterViewContr?= =?UTF-8?q?oller=20Reactor=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopUpStoreRegisterReactor.swift | 778 +++++++- .../PopUpStoreRegisterViewController.swift | 1771 +++++------------ 2 files changed, 1236 insertions(+), 1313 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index f3acf044..7b5e4539 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -1,110 +1,846 @@ +import UIKit + +import CoreLocation + import ReactorKit import RxCocoa import RxSwift + final class PopUpStoreRegisterReactor: Reactor { + // MARK: - Properties + private let adminUseCase: AdminUseCase + private let presignedService: PreSignedService + private let isEditMode: Bool + private let editingStoreId: Int64? + + init(adminUseCase: AdminUseCase, presignedService: PreSignedService, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { + self.adminUseCase = adminUseCase + self.presignedService = presignedService + self.isEditMode = editingStore != nil + self.editingStoreId = editingStore?.id + + // 초기 상태 설정 + let initialState = State(isEditMode: self.isEditMode) + self.initialState = initialState + } + // MARK: - Action enum Action { + // 폼 데이터 업데이트 case updateName(String) case updateAddress(String) case updateLat(String) case updateLon(String) case updateDescription(String) case selectCategory(String) - case addImage(ExtendedImage) + case updateMarkerTitle(String) + case updateMarkerSnippet(String) + + // 날짜/시간 관련 + case selectDateRange(start: Date, end: Date) + case selectTimeRange(start: Date, end: Date) + + // 이미지 관련 + case addImages([ExtendedImage]) case removeImage(Int) - case tapSave + case removeAllImages + case toggleMainImage(Int) + case markImageDeleted(String, Int64) + + // 네트워크 관련 + case loadStoreDetail(Int64) + case geocodeAddress(String) + case save + + // 기타 + case clearError + case dismissSuccess } // MARK: - Mutation enum Mutation { + // 폼 데이터 설정 case setName(String) case setAddress(String) case setLat(String) case setLon(String) case setDescription(String) case setCategory(String) - case addImage(ExtendedImage) + case setMarkerTitle(String) + case setMarkerSnippet(String) + + // 날짜/시간 설정 + case setDateRange(start: Date, end: Date) + case setTimeRange(start: Date, end: Date) + + // 이미지 관리 + case setImages([ExtendedImage]) + case addImages([ExtendedImage]) case removeImage(Int) + case removeAllImages + case toggleMainImage(Int) + case addDeletedImage(id: Int64, path: String) + + // 기존 스토어 데이터 설정 + case setStoreDetail(GetAdminPopUpStoreDetailResponseDTO) + case setOriginalImageIds([String: Int64]) + + // UI 상태 관리 + case setLoading(Bool) case setSaveEnabled(Bool) + case setSuccess(Bool) + case setError(String?) } // MARK: - State struct State { + // 폼 데이터 var name: String = "" var address: String = "" var lat: String = "" var lon: String = "" var description: String = "" var category: String = "" + var categoryId: Int64 = 0 + var markerTitle: String = "마커 제목" + var markerSnippet: String = "마커 설명" + + // 날짜 및 시간 + var selectedStartDate: Date? + var selectedEndDate: Date? + var selectedStartTime: Date? + var selectedEndTime: Date? + + // 이미지 관련 var images: [ExtendedImage] = [] + var originalImageIds: [String: Int64] = [:] + var deletedImageIds: [Int64] = [] + var deletedImagePaths: [String] = [] + + // UI 상태 + var isLoading: Bool = false var isSaveEnabled: Bool = false + var isSuccess: Bool = false + var errorMessage: String? + + // 모드 구분 + var isEditMode: Bool + + init(isEditMode: Bool = false) { + self.isEditMode = isEditMode + } } - let initialState = State() + let initialState: State // MARK: - Mutate func mutate(action: Action) -> Observable { switch action { + // 폼 데이터 업데이트 액션 case let .updateName(name): return .just(.setName(name)) + case let .updateAddress(address): return .just(.setAddress(address)) + case let .updateLat(lat): return .just(.setLat(lat)) + case let .updateLon(lon): return .just(.setLon(lon)) + case let .updateDescription(desc): return .just(.setDescription(desc)) + case let .selectCategory(category): return .just(.setCategory(category)) - case let .addImage(image): - return .just(.addImage(image)) + + case let .updateMarkerTitle(title): + return .just(.setMarkerTitle(title)) + + case let .updateMarkerSnippet(snippet): + return .just(.setMarkerSnippet(snippet)) + + // 날짜/시간 관련 액션 + case let .selectDateRange(start, end): + return .just(.setDateRange(start: start, end: end)) + + case let .selectTimeRange(start, end): + return .just(.setTimeRange(start: start, end: end)) + + // 이미지 관련 액션 + case let .addImages(newImages): + return .just(.addImages(newImages)) + case let .removeImage(index): return .just(.removeImage(index)) - case .tapSave: - // API 호출 등 저장 로직은 여기서 처리하거나 별도 Service로 위임합니다. - // 이 예제에서는 저장 전 폼 유효성 검증만 진행합니다. - return .empty() + + case .removeAllImages: + return .just(.removeAllImages) + + case let .toggleMainImage(index): + return .just(.toggleMainImage(index)) + + case let .markImageDeleted(path, id): + return .just(.addDeletedImage(id: id, path: path)) + + // 주소 지오코딩 + case let .geocodeAddress(address): + return geocodeAddress(address: address) + .flatMap { location -> Observable in + guard let location = location else { + return .just(.setError("주소를 찾을 수 없습니다.")) + } + + let latMutation = Mutation.setLat(String(format: "%.6f", location.coordinate.latitude)) + let lonMutation = Mutation.setLon(String(format: "%.6f", location.coordinate.longitude)) + + return .concat([ + .just(latMutation), + .just(lonMutation) + ]) + } + + + + // 스토어 상세 정보 로드 (수정 모드) + case let .loadStoreDetail(storeId): + return Observable.concat([ + .just(.setLoading(true)), + loadStoreDetail(storeId: storeId) + .catch { error in .just(.setError(error.localizedDescription)) }, + .just(.setLoading(false)) + ]) + + // 저장 액션 + case .save: + return Observable.concat([ + .just(.setLoading(true)), + saveStore() + .catch { error in .just(.setError(error.localizedDescription)) }, + .just(.setLoading(false)) + ]) + + // 오류 초기화 + case .clearError: + return .just(.setError(nil)) + + // 성공 상태 초기화 + case .dismissSuccess: + return .just(.setSuccess(false)) } } + // MARK: - Transform + func transform(mutation: Observable) -> Observable { + return mutation + .map { mutation -> [Mutation] in + // 특정 mutation이 발생한 경우 상태에 따라 추가 mutation을 발생시킴 + var mutations: [Mutation] = [mutation] + + if case .setLoading(true) = mutation { + mutations.append(.setError(nil)) + } + + return mutations + } + .flatMap { Observable.from($0) } + } + // MARK: - Reduce func reduce(state: State, mutation: Mutation) -> State { var newState = state + switch mutation { + // 폼 데이터 설정 case let .setName(name): newState.name = name + case let .setAddress(address): newState.address = address + case let .setLat(lat): newState.lat = lat + case let .setLon(lon): newState.lon = lon + case let .setDescription(desc): newState.description = desc + case let .setCategory(category): newState.category = category - case let .addImage(image): - newState.images.append(image) + newState.categoryId = Int64(getCategoryId(from: category)) + + case let .setMarkerTitle(title): + newState.markerTitle = title + + case let .setMarkerSnippet(snippet): + newState.markerSnippet = snippet + + // 날짜/시간 설정 + case let .setDateRange(start, end): + newState.selectedStartDate = start + newState.selectedEndDate = end + + case let .setTimeRange(start, end): + newState.selectedStartTime = start + newState.selectedEndTime = end + + // 이미지 관련 + case let .setImages(images): + newState.images = images + + case let .addImages(newImages): + newState.images.append(contentsOf: newImages) + + // 이미지가 이제 있고 대표 이미지가 없으면 첫 번째를 대표로 설정 + if !newState.images.isEmpty && !newState.images.contains(where: { $0.isMain }) { + newState.images[0].isMain = true + } + case let .removeImage(index): if index < newState.images.count { + let wasMainImage = newState.images[index].isMain + let removedImagePath = newState.images[index].filePath + + // 기존 이미지인 경우 삭제 목록에 추가 + if let imageId = newState.originalImageIds[removedImagePath] { + if !newState.deletedImageIds.contains(imageId) { + newState.deletedImageIds.append(imageId) + newState.deletedImagePaths.append(removedImagePath) + } + } + + // 이미지 배열에서 제거 newState.images.remove(at: index) + + // 대표 이미지가 삭제되었고 다른 이미지가 있으면 첫 번째를 대표로 설정 + if wasMainImage && !newState.images.isEmpty { + newState.images[0].isMain = true + } + } + + case .removeAllImages: + // 기존 이미지인 경우 모두 삭제 목록에 추가 + for image in newState.images { + if let imageId = newState.originalImageIds[image.filePath] { + if !newState.deletedImageIds.contains(imageId) { + newState.deletedImageIds.append(imageId) + newState.deletedImagePaths.append(image.filePath) + } + } + } + + newState.images.removeAll() + + case let .toggleMainImage(index): + if index < newState.images.count { + for i in 0.. Bool { + // 필수 필드 검사 + guard !state.name.isEmpty, + !state.address.isEmpty, + !state.lat.isEmpty, + !state.lon.isEmpty, + !state.description.isEmpty, + !state.category.isEmpty, + !state.images.isEmpty, + state.images.contains(where: { $0.isMain }), + state.selectedStartDate != nil, + state.selectedEndDate != nil else { + return false + } + + // 위도/경도 유효성 검사 + guard let latVal = Double(state.lat), + let lonVal = Double(state.lon), + latVal != 0 || lonVal != 0 else { + return false + } + + // 날짜 순서 검사 + if let startDate = state.selectedStartDate, + let endDate = state.selectedEndDate, + startDate > endDate { + return false + } + + return true + } + + // 주소 지오코딩 + private func geocodeAddress(address: String) -> Observable { + return Observable.create { observer in + let geocoder = CLGeocoder() + let fullAddress = "\(address), Korea" + + geocoder.geocodeAddressString( + fullAddress, + in: nil, + preferredLocale: Locale(identifier: "ko_KR") + ) { placemarks, error in + if let error = error { + Logger.log(message: "Geocoding error: \(error.localizedDescription)", category: .error) + observer.onNext(nil) + observer.onCompleted() + return + } + + if let location = placemarks?.first?.location { + observer.onNext(location) + } else { + observer.onNext(nil) + } + observer.onCompleted() + } + + return Disposables.create() + } + } + + // S3에서 이미지 삭제 + private func deleteImagesFromS3(_ imagePaths: [String]) { + guard !imagePaths.isEmpty else { return } + + presignedService.tryDelete(targetPaths: .init(objectKeyList: imagePaths)) + .subscribe( + onCompleted: { + Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) + }, + onError: { error in + Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) + } + ) + .disposed(by: DisposeBag()) + } + + // 카테고리 ID 매핑 + private func getCategoryId(from title: String) -> Int { + let cleanTitle = title.replacingOccurrences(of: " ▾", with: "") + Logger.log(message: "카테고리 매핑 시작 - 타이틀: \(cleanTitle)", category: .debug) + + let categoryMap: [String: Int64] = [ + "패션": 1, + "라이프스타일": 2, + "뷰티": 3, + "음식/요리": 4, + "예술": 5, + "반려동물": 6, + "여행": 7, + "엔터테인먼트": 8, + "애니메이션": 9, + "키즈": 10, + "스포츠": 11, + "게임": 12 + ] + + if let id = categoryMap[cleanTitle] { + Logger.log(message: "카테고리 매핑 성공: \(cleanTitle) -> \(id)", category: .debug) + return Int(id) + } else { + Logger.log(message: "카테고리 매핑 실패: \(cleanTitle)에 해당하는 ID를 찾을 수 없음", category: .error) + return 1 // 기본값 + } + } + + // 날짜 형식 변환 + private func getFormattedDate(from date: Date?) -> String { + guard let date = date else { return "" } + + let formatter = DateFormatter() + formatter.timeZone = TimeZone(identifier: "Asia/Seoul") + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" + + return formatter.string(from: date) + } + + // 날짜 및 시간 결합 + private func createDateTime(date: Date?, time: Date?) -> Date? { + guard let date = date else { return nil } + + if let time = time { + let calendar = Calendar.current + + // 날짜 부분 추출 + var components = calendar.dateComponents([.year, .month, .day], from: date) + + // 시간 부분 추출 + let timeComponents = calendar.dateComponents([.hour, .minute], from: time) + + // 날짜와 시간 결합 + components.hour = timeComponents.hour + components.minute = timeComponents.minute + components.second = 0 + components.timeZone = TimeZone(identifier: "Asia/Seoul") + + return calendar.date(from: components) + } + + return date + } + + // 날짜/시간 준비 + private func prepareDateTime(state: State) -> (startDate: String, endDate: String) { + // 시작일/시간 결합 + let startDateTime = createDateTime(date: state.selectedStartDate, time: state.selectedStartTime) + + // 종료일/시간 결합 + let endDateTime = createDateTime(date: state.selectedEndDate, time: state.selectedEndTime) + + let startDate = getFormattedDate(from: startDateTime) + let endDate = getFormattedDate(from: endDateTime) + + return (startDate: startDate, endDate: endDate) + } + + // 스토어 상세 정보 로드 + private func loadStoreDetail(storeId: Int64) -> Observable { + return adminUseCase.fetchStoreDetail(id: storeId) + .flatMap { [weak self] storeDetail -> Observable in + guard let self = self else { return .empty() } + + // 이미지 ID 매핑 초기화 및 설정 + var originalImageIds: [String: Int64] = [:] + for image in storeDetail.imageList { + originalImageIds[image.imageUrl] = image.id + } + + // 중복 및 삭제된 이미지 제외를 위한 집합 + var loadedImageUrls = Set() + let deletedIdSet = Set(self.currentState.deletedImageIds) + + // 이미지 로드 및 변환 + let mainImageUrl = storeDetail.mainImageUrl + var loadedImages: [ExtendedImage] = [] + let dispatchGroup = DispatchGroup() + + let imageObservable = Observable.create { observer in + for imageData in storeDetail.imageList { + // 중복 이미지 건너뛰기 + if loadedImageUrls.contains(imageData.imageUrl) { + continue + } + + // 삭제된 이미지 건너뛰기 + if deletedIdSet.contains(imageData.id) { + continue + } + + loadedImageUrls.insert(imageData.imageUrl) + + dispatchGroup.enter() + + if let imageURL = self.presignedService.fullImageURL(from: imageData.imageUrl) { + URLSession.shared.dataTask(with: imageURL) { data, response, error in + defer { dispatchGroup.leave() } + + if let error = error { + Logger.log(message: "이미지 로드 오류: \(error.localizedDescription)", category: .error) + return + } + + guard let data = data, + let image = UIImage(data: data) else { + Logger.log(message: "이미지 변환 실패", category: .error) + return + } + + let isMain = (imageData.imageUrl == mainImageUrl) + let extendedImage = ExtendedImage(filePath: imageData.imageUrl, image: image, isMain: isMain) + + DispatchQueue.main.async { + loadedImages.append(extendedImage) + } + }.resume() + } else { + dispatchGroup.leave() + } + } + + dispatchGroup.notify(queue: .main) { + if !loadedImages.isEmpty && !loadedImages.contains(where: { $0.isMain }) { + loadedImages[0].isMain = true + } + + observer.onNext(.setImages(loadedImages)) + observer.onNext(.setOriginalImageIds(originalImageIds)) + observer.onCompleted() + } + + return Disposables.create() + } + + return Observable.concat([ + .just(.setStoreDetail(storeDetail)), + imageObservable + ]) + } + } + + // 저장 액션 처리 + private func saveStore() -> Observable { + let state = self.currentState + + // 유효성 검사 + if !validateForm(state: state) { + return .just(.setError("필수 항목을 모두 입력해 주세요.")) + } + + if state.isEditMode { + return updateStore() + } else { + return createStore() + } + } + + // 이미지 업로드 + private func uploadImages() -> Observable<[String]> { + let uuid = UUID().uuidString + let updatedImages = currentState.images.enumerated().map { index, image in + let filePath = "PopUpImage/\(currentState.name)/\(uuid)/\(index).jpg" + return ExtendedImage( + filePath: filePath, + image: image.image, + isMain: image.isMain) + } + + return presignedService.tryUpload(datas: updatedImages.map { + PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) + }) + .asObservable() // Single을 Observable로 변환 + .map { _ in updatedImages.map { $0.filePath } } + } + + + // 신규 스토어 등록 + private func createStore() -> Observable { + return uploadImages() + .flatMap { [weak self] imagePaths -> Observable in + guard let self = self else { return .empty() } + + let state = self.currentState + let dates = self.prepareDateTime(state: state) + + // 메인 이미지 찾기 + let mainImage = imagePaths.first { path in + if let index = state.images.firstIndex(where: { $0.filePath == path }) { + return state.images[index].isMain + } + return false + } ?? imagePaths.first ?? "" + + let request = CreatePopUpStoreRequestDTO( + name: state.name, + categoryId: state.categoryId, + desc: state.description, + address: state.address, + startDate: dates.startDate, + endDate: dates.endDate, + mainImageUrl: mainImage, + imageUrlList: imagePaths, + latitude: Double(state.lat) ?? 0, + longitude: Double(state.lon) ?? 0, + markerTitle: state.markerTitle, + markerSnippet: state.markerSnippet, + startDateBeforeEndDate: true + ) + + return self.adminUseCase.createStore(request: request) + .map { _ in .setSuccess(true) } + } + } + + // 기존 스토어 수정 + private func updateStore() -> Observable { + // 기존에 저장된 이미지는 재업로드하지 않고 그대로 사용 + // 새로 추가된 이미지만 업로드 + let state = self.currentState + + // 새로 추가된 이미지만 필터링 + let newImages = state.images.filter { image in + return !state.originalImageIds.keys.contains(image.filePath) + } + + // 새 이미지가 없으면 바로 스토어 정보 업데이트 + if newImages.isEmpty { + return updateStoreInfo(nil) + } + + // 새 이미지 업로드 + return uploadNewImages(newImages) + .flatMap { [weak self] newImagePaths -> Observable in + guard let self = self else { return .empty() } + return self.updateStoreInfo(newImagePaths) + } + } + + // 새 이미지 업로드 + private func uploadNewImages(_ newImages: [ExtendedImage]) -> Observable<[String]> { + let uuid = UUID().uuidString + let updatedImages = newImages.enumerated().map { index, image in + let filePath = "PopUpImage/\(currentState.name)/\(uuid)/\(index).jpg" + return ExtendedImage( + filePath: filePath, + image: image.image, + isMain: image.isMain) + } + + return presignedService.tryUpload(datas: updatedImages.map { + PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) + }) + .asObservable() + .map { _ in updatedImages.map { $0.filePath } } + } + + + // 스토어 정보 업데이트 + private func updateStoreInfo(_ newImagePaths: [String]?) -> Observable { + guard let storeId = editingStoreId else { + return .just(.setError("스토어 ID가 없습니다.")) + } + + let state = self.currentState + let dates = prepareDateTime(state: state) + + // 모든 이미지 경로 (기존 이미지 + 새 이미지) + var allPaths: [String] = [] + + // 삭제되지 않은 기존 이미지 경로 추가 + let deletedIdSet = Set(state.deletedImageIds) + for (path, id) in state.originalImageIds { + if !deletedIdSet.contains(id) { + allPaths.append(path) + } + } + + // 새로 업로드된 이미지 경로 추가 + if let newPaths = newImagePaths { + allPaths.append(contentsOf: newPaths) + } + + // 메인 이미지 경로 결정 + let mainImage: String + if let mainImg = state.images.first(where: { $0.isMain }) { + if state.originalImageIds.keys.contains(mainImg.filePath) { + // 기존 이미지가 메인 + mainImage = mainImg.filePath + } else { + // 새 이미지가 메인인 경우 + // 현재 이미지 배열에서의 위치 찾기 + let idx = state.images.firstIndex(where: { $0.filePath == mainImg.filePath }) ?? 0 + + // 해당 위치가 새 이미지 배열 범위 내에 있는지 확인 + if let newPaths = newImagePaths, idx < newPaths.count { + mainImage = newPaths[idx] + } else if !allPaths.isEmpty { + mainImage = allPaths[0] + } else { + mainImage = "" + } + } + } else if !allPaths.isEmpty { + mainImage = allPaths[0] + } else { + mainImage = "" + } + + // 업데이트 요청 생성 + let request = UpdatePopUpStoreRequestDTO( + popUpStore: .init( + id: storeId, + name: state.name, + categoryId: state.categoryId, + desc: state.description, + address: state.address, + startDate: dates.startDate, + endDate: dates.endDate, + mainImageUrl: mainImage, + bannerYn: !mainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + imageUrl: allPaths, + startDateBeforeEndDate: true + ), + location: .init( + latitude: Double(state.lat) ?? 0, + longitude: Double(state.lon) ?? 0, + markerTitle: state.markerTitle, + markerSnippet: state.markerSnippet + ), + imagesToAdd: newImagePaths ?? [], + imagesToDelete: state.deletedImageIds + ) + + // 서버에 스토어 정보 업데이트 요청 + return adminUseCase.updateStore(request: request) + .flatMap { [weak self] _ -> Observable in + guard let self = self else { return .empty() } + + // S3에서 삭제된 이미지 제거 + if !state.deletedImagePaths.isEmpty { + self.deleteImagesFromS3(state.deletedImagePaths) + } + + return .just(.setSuccess(true)) + } + + } } + diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 3c21a2e2..ac9dd346 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -9,14 +9,11 @@ import RxSwift import SnapKit import Then -final class PopUpStoreRegisterViewController: BaseViewController { +final class PopUpStoreRegisterViewController: BaseViewController, View { + typealias Reactor = PopUpStoreRegisterReactor - // MARK: - Navigation/Header - var completionHandler: (() -> Void)? - private var selectedImages: [UIImage] = [] - private var selectedMainImageIndex: Int? - private var imageFileNames: [String] = [] - private var images: [ExtendedImage] = [] + // MARK: - Properties + var disposeBag = DisposeBag() private var pickerViewController: PHPickerViewController? private let adminUseCase: AdminUseCase private var nameField: UITextField? @@ -24,33 +21,11 @@ final class PopUpStoreRegisterViewController: BaseViewController { private var latField: UITextField? private var lonField: UITextField? private var descTV: UITextView? - - private let popupName: String = "" - private var originalImageIds: [String: Int64] = [:] - private var deletedImageIds: [Int64] = [] - private var deletedImagePaths: [String] = [] - - private let editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? - let presignedService = PreSignedService() - - var disposeBag = DisposeBag() + var completionHandler: (() -> Void)? private let nickname: String - private let navContainer = UIView() - - init(nickname: String, adminUseCase: AdminUseCase, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { - self.nickname = nickname - self.adminUseCase = adminUseCase - self.editingStore = editingStore - super.init() - self.accountIdLabel.text = nickname + "님" - if editingStore != nil { - pageTitleLabel.text = "팝업스토어 수정" - } - } - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + // MARK: - UI Components + private let navContainer = UIView() private lazy var imagesCollectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() @@ -87,7 +62,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return btn }() - // MARK: - Title (Back button + label) private let titleContainer = UIView() private let backButton: UIButton = { let btn = UIButton(type: .system) @@ -114,11 +88,9 @@ final class PopUpStoreRegisterViewController: BaseViewController { $0.setTitleColor(.red, for: .normal) } - // MARK: - Scroll private let scrollView = UIScrollView() private let contentView = UIView() - // MARK: - Form Background private let formBackgroundView = UIView().then { $0.backgroundColor = .white $0.layer.borderWidth = 1 @@ -128,7 +100,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { private let verticalStack = UIStackView() - // MARK: - Bottom Save Button private let saveButton: UIButton = { let btn = UIButton(type: .system) btn.setTitle("저장", for: .normal) @@ -140,16 +111,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { return btn }() - // MARK: - DateTimePicker - private var selectedStartDate: Date? - private var selectedEndDate: Date? - private var selectedStartTime: Date? - private var selectedEndTime: Date? - - // MARK: - Categories - private var categories: [String] = ["게임", "라이프스타일", "반려동물", "뷰티", "스포츠", "애니메이션", "엔터테인먼트", "여행", "예술", "음식/요리", "키즈", "패션"] - - // MARK: - UI Elements private let categoryButton: UIButton = { let btn = UIButton(type: .system) btn.setTitle("카테고리 선택 ▾", for: .normal) @@ -188,260 +149,294 @@ final class PopUpStoreRegisterViewController: BaseViewController { btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() + private func extractDateRange(from state: Reactor.State) -> (Date, Date)? { + guard let start = state.selectedStartDate, + let end = state.selectedEndDate else { + return nil + } + return (start, end) + } + + private func extractTimeRange(from state: Reactor.State) -> (Date, Date)? { + guard let start = state.selectedStartTime, + let end = state.selectedEndTime else { + return nil + } + return (start, end) + } + + private func areDateRangesEqual(_ prev: (Date, Date), _ current: (Date, Date)) -> Bool { + return prev.0 == current.0 && prev.1 == current.1 + } + // MARK: - Initializer + init(nickname: String, adminUseCase: AdminUseCase, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { + self.nickname = nickname + self.adminUseCase = adminUseCase + + super.init() + + let presignedService = PreSignedService() + let reactor = PopUpStoreRegisterReactor( + adminUseCase: adminUseCase, + presignedService: presignedService, + editingStore: editingStore + ) + + self.reactor = reactor + self.accountIdLabel.text = nickname + "님" + + if editingStore != nil { + pageTitleLabel.text = "팝업스토어 수정" + + // 편집 모드일 경우 스토어 상세 정보 로드 + if let storeId = editingStore?.id { + reactor.action.onNext(.loadStoreDetail(storeId)) + } + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } // MARK: - Lifecycle override func viewDidLoad() { super.viewDidLoad() + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) tapGesture.cancelsTouchesInView = false view.addGestureRecognizer(tapGesture) view.backgroundColor = UIColor(white: 0.95, alpha: 1) - if let store = editingStore { - // 삭제된 이미지 ID 복원 - if let savedIds = UserDefaults.standard.array(forKey: "deletedImageIds_\(store.id)") as? [Int64] { - self.deletedImageIds = savedIds - Logger.log(message: "저장된 삭제 이미지 ID 복원: \(savedIds.count)개", category: .debug) - } - - // 삭제된 이미지 경로 복원 - if let savedPaths = UserDefaults.standard.array(forKey: "deletedImagePaths_\(store.id)") as? [String] { - self.deletedImagePaths = savedPaths - Logger.log(message: "저장된 삭제 이미지 경로 복원: \(savedPaths.count)개", category: .debug) - } - - loadStoreDetail(for: store.id) - } setupNavigation() setupLayout() setupRows() setupImageCollectionUI() - setupImageCollectionActions() setupKeyboardHandling() - setupAddressField() - setupAllFieldListeners() - } - // MARK: - Navigation - private func setupNavigation() { - backButton.addTarget(self, action: #selector(onBack), for: .touchUpInside) - } + // MARK: - ReactorKit Binding + func bind(reactor: Reactor) { - @objc private func handleTap() { - view.endEditing(true) - } - @objc private func onBack() { - navigationController?.popViewController(animated: true) - } - @objc private func fieldDidChange(_ textField: UITextField) { - if textField == addressField { - Logger.log(message: "주소 값 변경: \(textField.text ?? "nil")", category: .debug) - } else if textField == latField { - Logger.log(message: "위도 값 변경: \(textField.text ?? "nil")", category: .debug) - } else if textField == lonField { - Logger.log(message: "경도 값 변경: \(textField.text ?? "nil")", category: .debug) - updateSaveButtonState() + // 텍스트 필드 바인딩 + nameField?.rx.text.orEmpty + .distinctUntilChanged() + .skip(1) + .map { Reactor.Action.updateName($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - } - } - private func fillFormWithExistingData(_ storeDetail: GetAdminPopUpStoreDetailResponseDTO) { - // 기본 필드 채우기 - nameField?.text = storeDetail.name - categoryButton.setTitle("\(storeDetail.categoryName) ▾", for: .normal) - addressField?.text = storeDetail.address - latField?.text = String(storeDetail.latitude) - lonField?.text = String(storeDetail.longitude) - descTV?.text = storeDetail.desc - - // 중요: ID와 URL 매핑 초기화 및 설정 - self.originalImageIds.removeAll() - // deletedImageIds와 deletedImagePaths는 초기화하지 않음 (기존 삭제 정보 유지) - - // 중요: 여기서 originalImageIds 맵을 세팅합니다 - for image in storeDetail.imageList { - self.originalImageIds[image.imageUrl] = image.id - Logger.log(message: "이미지 ID 매핑: \(image.imageUrl) -> \(image.id)", category: .debug) - } + addressField?.rx.text.orEmpty + .distinctUntilChanged() + .skip(1) + .map { Reactor.Action.updateAddress($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - // 날짜 설정 - let isoFormatter = ISO8601DateFormatter() + latField?.rx.text.orEmpty + .distinctUntilChanged() + .skip(1) + .map { Reactor.Action.updateLat($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - if let startDate = isoFormatter.date(from: storeDetail.startDate), - let endDate = isoFormatter.date(from: storeDetail.endDate) { - self.selectedStartDate = startDate - self.selectedEndDate = endDate - self.updatePeriodButtonTitle() - } + lonField?.rx.text.orEmpty + .distinctUntilChanged() + .skip(1) + .map { Reactor.Action.updateLon($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - // 중요: 기존 이미지는 먼저 모두 제거 - self.images.removeAll() + descTV?.rx.text.orEmpty + .distinctUntilChanged() + .skip(1) + .map { Reactor.Action.updateDescription($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - // 이미지 목록 디버깅 - 실제 서버에서 받은 목록 확인 - Logger.log(message: "서버에서 받은 이미지 목록 (총 \(storeDetail.imageList.count)개):", category: .debug) - for (index, image) in storeDetail.imageList.enumerated() { - Logger.log(message: " \(index+1). ID: \(image.id), URL: \(image.imageUrl)", category: .debug) - } + // 주소 변경 시 지오코딩 요청 + addressField?.rx.text.orEmpty + .distinctUntilChanged() + .debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .filter { !$0.isEmpty } + .map { Reactor.Action.geocodeAddress($0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) - // 삭제된 이미지 ID 집합 생성 (빠른 검색을 위해) - let deletedIdSet = Set(self.deletedImageIds) - Logger.log(message: "삭제된 이미지 ID 목록: \(deletedIdSet)", category: .debug) + // 이미지 버튼 바인딩 + addImageButton.rx.tap + .bind { [weak self] in + self?.showImagePicker() + } + .disposed(by: disposeBag) - // 중복 및 삭제된 이미지 제외를 위한 집합 - var loadedImageUrls = Set() + removeAllButton.rx.tap + .map { Reactor.Action.removeAllImages } + .bind(to: reactor.action) + .disposed(by: disposeBag) - let dispatchGroup = DispatchGroup() - let mainImageUrl = storeDetail.mainImageUrl - Logger.log(message: "대표 이미지 URL: \(mainImageUrl)", category: .debug) - - for imageData in storeDetail.imageList { - // 중복 이미지 건너뛰기 - if loadedImageUrls.contains(imageData.imageUrl) { - Logger.log(message: "중복 이미지 스킵: \(imageData.imageUrl)", category: .debug) - continue + // 다양한 버튼 바인딩 + categoryButton.rx.tap + .bind { [weak self] in + self?.showCategoryPicker() } + .disposed(by: disposeBag) - // 삭제된 이미지 건너뛰기 - if deletedIdSet.contains(imageData.id) { - Logger.log(message: "삭제된 이미지 스킵: ID \(imageData.id), URL: \(imageData.imageUrl)", category: .debug) - continue + periodButton.rx.tap + .bind { [weak self] in + self?.showDateRangePicker() } + .disposed(by: disposeBag) - loadedImageUrls.insert(imageData.imageUrl) - - dispatchGroup.enter() - - if let imageURL = presignedService.fullImageURL(from: imageData.imageUrl) { - Logger.log(message: "이미지 로드 시작: \(imageData.imageUrl)", category: .debug) - - URLSession.shared.dataTask(with: imageURL) { [weak self] data, response, error in - defer { dispatchGroup.leave() } - - // 응답 상태 코드 확인 추가 - if let httpResponse = response as? HTTPURLResponse { - Logger.log(message: "이미지 로드 응답 코드: \(httpResponse.statusCode) - URL: \(imageData.imageUrl)", category: .debug) - if httpResponse.statusCode != 200 { - Logger.log(message: "이미지 로드 실패 - 상태 코드: \(httpResponse.statusCode)", category: .error) - return - } - } - - if let error = error { - Logger.log(message: "이미지 로드 오류: \(error.localizedDescription)", category: .error) - return - } - - guard let self = self, - let data = data, - let image = UIImage(data: data) else { - Logger.log(message: "이미지 변환 실패", category: .error) - return - } - - let isMain = (imageData.imageUrl == mainImageUrl) - - let extendedImage = ExtendedImage(filePath: imageData.imageUrl, image: image, isMain: isMain) - - DispatchQueue.main.async { - self.images.append(extendedImage) - Logger.log(message: "이미지 로드 완료: \(imageData.imageUrl), isMain: \(isMain)", category: .debug) - } - }.resume() - } else { - Logger.log(message: "이미지 URL 생성 실패: \(imageData.imageUrl)", category: .error) - dispatchGroup.leave() + timeButton.rx.tap + .bind { [weak self] in + self?.showTimeRangePicker() } - } + .disposed(by: disposeBag) - dispatchGroup.notify(queue: .main) { [weak self] in - guard let self = self else { return } + // 저장 버튼 + saveButton.rx.tap + .map { Reactor.Action.save } + .bind(to: reactor.action) + .disposed(by: disposeBag) - if !self.images.isEmpty && !self.images.contains(where: { $0.isMain }) { - self.images[0].isMain = true - Logger.log(message: "대표 이미지가 없어 첫 번째 이미지를 대표로 설정", category: .debug) - } + // Outputs (Reactor -> View) - Logger.log(message: "모든 이미지 로드 완료: 총 \(self.images.count)개", category: .debug) - self.imagesCollectionView.reloadData() - self.updateSaveButtonState() - } - } + // 저장 버튼 활성화 상태 + reactor.state.map { $0.isSaveEnabled } + .distinctUntilChanged() + .bind(onNext: { [weak self] isEnabled in + self?.saveButton.isEnabled = isEnabled + self?.saveButton.backgroundColor = isEnabled ? .systemBlue : .lightGray + }) + .disposed(by: disposeBag) + + // 로딩 상태 + reactor.state.map { $0.isLoading } + .distinctUntilChanged() + .bind(onNext: { [weak self] isLoading in + if isLoading { + // 로딩 인디케이터 표시 + self?.showLoadingIndicator() + } else { + // 로딩 인디케이터 숨김 + self?.hideLoadingIndicator() + } + }) + .disposed(by: disposeBag) - func loadStoreDetail(for storeId: Int64) { - Logger.log(message: "상세 정보 요청 시작 - Store ID: \(storeId)", category: .debug) + // 에러 메시지 + reactor.state.map { $0.errorMessage } + .distinctUntilChanged() + .compactMap { $0 } + .bind(onNext: { [weak self] message in + self?.showErrorAlert(message: message) + reactor.action.onNext(.clearError) + }) + .disposed(by: disposeBag) - adminUseCase.fetchStoreDetail(id: storeId) - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] storeDetail in - Logger.log(message: "상세 정보 요청 성공", category: .info) - self?.fillFormWithExistingData(storeDetail) - }, onError: { error in - Logger.log(message: "상세 정보 요청 실패: \(error.localizedDescription)", category: .error) + // 성공 상태 + reactor.state.map { $0.isSuccess } + .distinctUntilChanged() + .filter { $0 } + .bind(onNext: { [weak self] _ in + self?.showSuccessAlert(isUpdate: reactor.currentState.isEditMode) + reactor.action.onNext(.dismissSuccess) }) .disposed(by: disposeBag) - } - private func setupKeyboardHandling() { - NotificationCenter.default.addObserver( - self, - selector: #selector(keyboardWillShow), - name: UIResponder.keyboardWillShowNotification, - object: nil - ) - NotificationCenter.default.addObserver( - self, - selector: #selector(keyboardWillHide), - name: UIResponder.keyboardWillHideNotification, - object: nil - ) + // 이미지 목록 업데이트 + reactor.state.map { $0.images } + .distinctUntilChanged() + .bind(onNext: { [weak self] _ in + self?.imagesCollectionView.reloadData() + }) + .disposed(by: disposeBag) - // 스크롤뷰 키보드 처리 설정 - scrollView.keyboardDismissMode = .interactive - } + // 필드 값 업데이트 + reactor.state.map { $0.name } + .distinctUntilChanged() + .bind(onNext: { [weak self] name in + if self?.nameField?.text != name { + self?.nameField?.text = name + } + }) + .disposed(by: disposeBag) - @objc private func keyboardWillShow(_ notification: Notification) { - guard let userInfo = notification.userInfo, - let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { - return - } + reactor.state.map { $0.address } + .distinctUntilChanged() + .bind(onNext: { [weak self] address in + if self?.addressField?.text != address { + self?.addressField?.text = address + } + }) + .disposed(by: disposeBag) - let keyboardHeight = keyboardFrame.height - let contentInset = UIEdgeInsets( - top: 0, - left: 0, - bottom: keyboardHeight, - right: 0 - ) + reactor.state.map { $0.lat } + .distinctUntilChanged() + .bind(onNext: { [weak self] lat in + if self?.latField?.text != lat { + self?.latField?.text = lat + } + }) + .disposed(by: disposeBag) - scrollView.contentInset = contentInset - scrollView.scrollIndicatorInsets = contentInset + reactor.state.map { $0.lon } + .distinctUntilChanged() + .bind(onNext: { [weak self] lon in + if self?.lonField?.text != lon { + self?.lonField?.text = lon + } + }) + .disposed(by: disposeBag) - // 현재 활성화된 필드가 키보드에 가려지는지 확인 - if let activeField = view.findFirstResponder() { - let activeRect = activeField.convert(activeField.bounds, to: scrollView) - let bottomOffset = activeRect.maxY + 20 // 여유 공간 + reactor.state.map { $0.description } + .distinctUntilChanged() + .bind(onNext: { [weak self] description in + if self?.descTV?.text != description { + self?.descTV?.text = description + } + }) + .disposed(by: disposeBag) - if bottomOffset > (scrollView.frame.height - keyboardHeight) { - let scrollPoint = CGPoint( - x: 0, - y: bottomOffset - (scrollView.frame.height - keyboardHeight) - ) - scrollView.setContentOffset(scrollPoint, animated: true) - } - } - } + reactor.state.map { $0.category } + .distinctUntilChanged() + .filter { !$0.isEmpty } + .bind(onNext: { [weak self] category in + self?.updateCategoryButtonTitle(with: category) + }) + .disposed(by: disposeBag) - @objc private func keyboardWillHide(_ notification: Notification) { - UIView.animate(withDuration: 0.3) { - self.scrollView.contentInset = .zero - self.scrollView.scrollIndicatorInsets = .zero - } + // 날짜 범위 업데이트 + reactor.state + .compactMap { [weak self] state in + self?.extractDateRange(from: state) + } + .distinctUntilChanged(areDateRangesEqual) + .bind(onNext: { [weak self] dateRange in + self?.updatePeriodButtonTitle(start: dateRange.0, end: dateRange.1) + }) + .disposed(by: disposeBag) + + // 시간 범위 업데이트 + reactor.state + .compactMap { [weak self] state in + self?.extractTimeRange(from: state) + } + .distinctUntilChanged(areDateRangesEqual) + .bind(onNext: { [weak self] timeRange in + self?.updateTimeButtonTitle(start: timeRange.0, end: timeRange.1) + }) + .disposed(by: disposeBag) + } + // MARK: - UI Setup + private func setupNavigation() { + backButton.addTarget(self, action: #selector(onBack), for: .touchUpInside) } - // MARK: - Layout private func setupLayout() { - // (1) 상단 컨테이너 + // 상단 컨테이너 view.addSubview(navContainer) navContainer.snp.makeConstraints { make in make.top.equalTo(view.safeAreaLayoutGuide) @@ -470,7 +465,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.width.height.equalTo(32) } - // (2) 타이틀 컨테이너 + // 타이틀 컨테이너 view.addSubview(titleContainer) titleContainer.snp.makeConstraints { make in make.top.equalTo(navContainer.snp.bottom) @@ -491,7 +486,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.left.equalTo(backButton.snp.right).offset(4) } - // (3) 스크롤뷰 + // 스크롤뷰 view.addSubview(scrollView) scrollView.snp.makeConstraints { make in make.top.equalTo(titleContainer.snp.bottom) @@ -505,7 +500,17 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.width.equalTo(scrollView.snp.width) } - // (4) 이미지 영역 추가 + // 저장 버튼 + view.addSubview(saveButton) + saveButton.snp.makeConstraints { make in + make.left.right.equalToSuperview().inset(16) + make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) + make.height.equalTo(44) + } + } + + private func setupImageCollectionUI() { + // 버튼 스택 let buttonStack = UIStackView(arrangedSubviews: [addImageButton, removeAllButton]) buttonStack.axis = .horizontal buttonStack.distribution = .fillEqually @@ -518,6 +523,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.height.equalTo(40) } + // 이미지 컬렉션 뷰 contentView.addSubview(imagesCollectionView) imagesCollectionView.snp.makeConstraints { make in make.top.equalTo(buttonStack.snp.bottom).offset(8) @@ -525,7 +531,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.height.equalTo(130) } - // (5) 폼 배경 + // 폼 배경 contentView.addSubview(formBackgroundView) formBackgroundView.snp.makeConstraints { make in make.top.equalTo(imagesCollectionView.snp.bottom).offset(16) @@ -533,80 +539,35 @@ final class PopUpStoreRegisterViewController: BaseViewController { make.bottom.equalToSuperview().offset(-16) } + // 수직 스택 formBackgroundView.addSubview(verticalStack) verticalStack.axis = .vertical verticalStack.spacing = 0 verticalStack.snp.makeConstraints { make in make.edges.equalToSuperview() } - - // (6) 저장 버튼 - view.addSubview(saveButton) - saveButton.snp.makeConstraints { make in - make.left.right.equalToSuperview().inset(16) - make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) - make.height.equalTo(44) - } - } - private func getCurrentFormattedTime() -> String { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy.MM.dd HH:mm" - formatter.timeZone = TimeZone(identifier: "Asia/Seoul") // 한국 시간대 명시적 설정 - return formatter.string(from: Date()) - } - private func setupCreationTimeLabel() -> UILabel { - let currentTime = getCurrentFormattedTime() - return makeSimpleLabel(currentTime) } - private func setupAllFieldListeners() { - // 이름 필드 - nameField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) - - // 주소, 위도, 경도 필드 - addressField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) - latField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) - lonField?.addTarget(self, action: #selector(anyFieldDidChange(_:)), for: .editingChanged) - - // 설명 필드 (UITextView는 다르게 처리해야 함) - descTV?.delegate = self - - // 로그 추가 - Logger.log(message: "모든 필드에 변경 리스너가 설정되었습니다", category: .debug) - } - @objc private func anyFieldDidChange(_ textField: UITextField) { - Logger.log(message: "\(textField.accessibilityIdentifier ?? "알 수 없는 필드") 값 변경: \(textField.text ?? "nil")", category: .debug) - updateSaveButtonState() - } - // MARK: - Setup Rows private func setupRows() { + // 이름 필드 추가 addRowTextField(leftTitle: "이름", placeholder: "팝업스토어 이름을 입력해 주세요.") -// addRowTextField(leftTitle: "이미지", placeholder: "팝업스토어 대표 이미지를 업로드 해주세요.") - categoryButton.addTarget(self, action: #selector(didTapCategoryButton), for: .touchUpInside) + // 카테고리 버튼 추가 addRowCustom(leftTitle: "카테고리", rightView: categoryButton) - // (위치) => 2줄 - // 1) 주소 (TextField) + // 위치 필드 추가 (주소, 위도, 경도) let addressField = makeRoundedTextField("팝업스토어 주소를 입력해 주세요.") self.addressField = addressField - addressField.snp.makeConstraints { _ in - addressField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) - - } - // 2) (위도 Label + TF) + (경도 Label + TF) let latLabel = makePlainLabel("위도") let latField = makeRoundedTextField("") latField.textAlignment = .center - self.latField = latField // latField와 연결 - latField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) + self.latField = latField let lonLabel = makePlainLabel("경도") let lonField = makeRoundedTextField("") - self.lonField = lonField // lonField와 연결 lonField.textAlignment = .center - lonField.addTarget(self, action: #selector(fieldDidChange(_:)), for: .editingChanged) + self.lonField = lonField let latStack = UIStackView(arrangedSubviews: [latLabel, latField]) latStack.axis = .horizontal @@ -623,27 +584,21 @@ final class PopUpStoreRegisterViewController: BaseViewController { latLonRow.spacing = 16 latLonRow.distribution = .fillEqually - // 수직 스택(주소, latLonRow) let locationVStack = UIStackView(arrangedSubviews: [addressField, latLonRow]) locationVStack.axis = .vertical locationVStack.spacing = 8 locationVStack.distribution = .fillEqually - // 한 행에 왼쪽 "위치", 오른쪽 2줄(주소 / 위도경도) addRowCustom(leftTitle: "위치", rightView: locationVStack, rowHeight: nil, totalHeight: 80) - // (마커) => 2줄 - // 1) (마커명 Label + TF) + // 마커 필드 추가 let markerLabel = makePlainLabel("마커명") - let markerField = makeRoundedTextField("") - let markerStackH = UIStackView(arrangedSubviews: [markerLabel, markerField]) markerStackH.axis = .horizontal markerStackH.spacing = 8 markerStackH.distribution = .fillProportionally - // 2) (스니펫 Label + TF) let snippetLabel = makePlainLabel("스니펫") let snippetField = makeRoundedTextField("") let snippetStackH = UIStackView(arrangedSubviews: [snippetLabel, snippetField]) @@ -651,133 +606,60 @@ final class PopUpStoreRegisterViewController: BaseViewController { snippetStackH.spacing = 8 snippetStackH.distribution = .fillProportionally - // 수직 let markerVStack = UIStackView(arrangedSubviews: [markerStackH, snippetStackH]) markerVStack.axis = .vertical markerVStack.spacing = 8 markerVStack.distribution = .fillEqually - // 한 행 => "마커" 라벨, 오른쪽 2줄 (마커명, 스니펫) addRowCustom(leftTitle: "마커", rightView: markerVStack, rowHeight: nil, totalHeight: 80) - // (10) 기간 - periodButton.addTarget(self, action: #selector(didTapPeriodButton), for: .touchUpInside) + // 기간 및 시간 addRowCustom(leftTitle: "기간", rightView: periodButton) - - // (11) 시간 - timeButton.addTarget(self, action: #selector(didTapTimeButton), for: .touchUpInside) addRowCustom(leftTitle: "시간", rightView: timeButton) - // (12) 작성자 + // 작성자 및 작성시간 let writerLbl = makeSimpleLabel(nickname) addRowCustom(leftTitle: "작성자", rightView: writerLbl) - // (13) 작성시간 let timeLbl = setupCreationTimeLabel() addRowCustom(leftTitle: "작성시간", rightView: timeLbl) - // (14) 상태값 + // 상태값 let statusLbl = makeSimpleLabel("진행") addRowCustom(leftTitle: "상태값", rightView: statusLbl) - // (15) 설명 + // 설명 let descTV = makeRoundedTextView() - self.descTV = descTV // 설명 필드 연결 + self.descTV = descTV addRowCustom(leftTitle: "설명", rightView: descTV, rowHeight: nil, totalHeight: 120) - } - // MARK: - Row - - private func addRowTextField(leftTitle: String, placeholder: String) { - let tf = makeRoundedTextField(placeholder) - if leftTitle == "이름" { - nameField = tf // 이름 필드 연결 - } else if leftTitle == "주소" { - addressField = tf // 주소 필드 연결 - } - addRowCustom(leftTitle: leftTitle, rightView: tf) - } - - private func setupImageCollectionUI() { - // 1) 상단 버튼들 (Add / RemoveAll) - let buttonStack = UIStackView(arrangedSubviews: [addImageButton, removeAllButton]) - buttonStack.axis = .horizontal - buttonStack.distribution = .fillEqually - buttonStack.spacing = 16 - - contentView.addSubview(buttonStack) - buttonStack.snp.makeConstraints { make in - make.top.equalToSuperview().offset(16) - make.left.right.equalToSuperview().inset(16) - make.height.equalTo(40) - } - - // 2) CollectionView - contentView.addSubview(imagesCollectionView) - imagesCollectionView.snp.makeConstraints { make in - make.top.equalTo(buttonStack.snp.bottom).offset(8) - make.left.right.equalToSuperview().inset(16) - make.height.equalTo(130) // 셀 높이(120) + 패딩 - } - - // formBackgroundView를 아래로 조금 내려야 한다면? - formBackgroundView.snp.remakeConstraints { make in - make.top.equalTo(imagesCollectionView.snp.bottom).offset(16) - make.left.right.equalToSuperview().inset(16) - make.bottom.equalToSuperview() - } - } - private func setupImageCollectionActions() { - // (1) 이미지 추가 버튼 -> 앨범 열기 - addImageButton.rx.tap - .bind { [weak self] in - self?.showImagePicker() - } - .disposed(by: disposeBag) - - // (2) 전체 삭제 버튼 - removeAllButton.rx.tap - .bind { [weak self] in - self?.images.removeAll() - self?.imagesCollectionView.reloadData() - self?.updateSaveButtonState() - } - .disposed(by: disposeBag) + private func setupKeyboardHandling() { + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillShow), + name: UIResponder.keyboardWillShowNotification, + object: nil + ) + NotificationCenter.default.addObserver( + self, + selector: #selector(keyboardWillHide), + name: UIResponder.keyboardWillHideNotification, + object: nil + ) - saveButton.rx.tap - .bind { [weak self] in - guard let self = self else { return } - // 1) 유효성 검사 - if self.validateForm() { - // 2) OK -> 등록 로직 - self.doRegister() - } else { - // 3) 실패 -> Alert/toast - let alert = UIAlertController( - title: "필수값 미입력", - message: "필수 항목을 모두 입력해 주세요.", - preferredStyle: .alert - ) - alert.addAction(UIAlertAction(title: "확인", style: .default)) - self.present(alert, animated: true, completion: nil) - } - } - .disposed(by: disposeBag) + scrollView.keyboardDismissMode = .interactive } - private func updateSaveButtonState() { - // 디버깅을 위한 로깅 추가 - Logger.log(message: "updateSaveButtonState 호출됨", category: .debug) - - let isFormValid = validateForm() - // 이전 상태와 새 상태가 다를 때만 로그 출력 - if saveButton.isEnabled != isFormValid { - Logger.log(message: "저장 버튼 상태 변경: \(saveButton.isEnabled) -> \(isFormValid)", category: .debug) + // MARK: - Helper Methods + private func addRowTextField(leftTitle: String, placeholder: String) { + let tf = makeRoundedTextField(placeholder) + if leftTitle == "이름" { + nameField = tf + } else if leftTitle == "주소" { + addressField = tf } - - saveButton.isEnabled = isFormValid - saveButton.backgroundColor = isFormValid ? .systemBlue : .lightGray + addRowCustom(leftTitle: leftTitle, rightView: tf) } private func addRowCustom(leftTitle: String, @@ -846,136 +728,18 @@ final class PopUpStoreRegisterViewController: BaseViewController { verticalStack.addArrangedSubview(row) } - @objc private func didTapPeriodButton() { - DateTimePickerManager.shared.showDateRange(on: self) { [weak self] start, end in - guard let self = self else { return } - self.selectedStartDate = start - self.selectedEndDate = end - self.updatePeriodButtonTitle() - self.updateSaveButtonState() // 날짜 선택 후 저장 버튼 상태 업데이트 - } - } - - @objc private func didTapTimeButton() { - DateTimePickerManager.shared.showTimeRange(on: self) { [weak self] st, et in - guard let self = self else { return } - self.selectedStartTime = st - self.selectedEndTime = et - - // 디버깅을 위한 로그 추가 - let formatter = DateFormatter() - formatter.dateFormat = "HH:mm:ss" - Logger.log(message: "시간 선택 완료 - 시작: \(formatter.string(from: st)), 종료: \(formatter.string(from: et))", category: .debug) - - self.updateTimeButtonTitle() - self.updateSaveButtonState() - } - } - - private func updatePeriodButtonTitle() { - guard let selectedStartDate = selectedStartDate, let selectedEndDate = selectedEndDate else { return } - let df = DateFormatter() - df.dateFormat = "yyyy.MM.dd" - let sStr = df.string(from: selectedStartDate) - let eStr = df.string(from: selectedEndDate) - - periodButton.setTitle("\(sStr) ~ \(eStr)", for: .normal) - } - - private func updateTimeButtonTitle() { - guard let st = selectedStartTime, let et = selectedEndTime else { return } - let df = DateFormatter() - df.dateFormat = "HH:mm" - let stStr = df.string(from: st) - let etStr = df.string(from: et) - timeButton.setTitle("\(stStr) ~ \(etStr)", for: .normal) - } - - // MARK: - Category Selection - - @objc private func didTapCategoryButton() { - let alertController = UIAlertController(title: "카테고리 선택", message: nil, preferredStyle: .actionSheet) - - // 기존 카테고리 목록 추가 - for category in categories { - let action = UIAlertAction(title: category, style: .default) { [weak self] _ in - self?.updateCategoryButtonTitle(with: category) - } - alertController.addAction(action) - } - - // '카테고리 추가' 옵션 추가 - let addAction = UIAlertAction(title: "카테고리 추가", style: .default) { [weak self] _ in - self?.presentAddCategoryAlert() - } - alertController.addAction(addAction) - - // 취소 버튼 추가 - let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil) - alertController.addAction(cancelAction) - - // iPad에서 액션 시트가 크래시되지 않도록 설정 - if let popoverController = alertController.popoverPresentationController { - popoverController.sourceView = categoryButton - popoverController.sourceRect = categoryButton.bounds - } - - present(alertController, animated: true, completion: nil) - } - - private func presentAddCategoryAlert() { - let alert = UIAlertController(title: "새 카테고리 추가", message: "추가할 카테고리 이름을 입력하세요.", preferredStyle: .alert) - - alert.addTextField { textField in - textField.placeholder = "카테고리 이름" - } - - let addAction = UIAlertAction(title: "추가", style: .default) { [weak self] _ in - guard let self = self else { return } - if let newCategory = alert.textFields?.first?.text?.trimmingCharacters(in: .whitespacesAndNewlines), !newCategory.isEmpty { - // 중복 체크 - if self.categories.contains(newCategory) { - self.presentDuplicateCategoryAlert() - } else { - self.categories.append(newCategory) - self.updateCategoryButtonTitle(with: newCategory) - } - } - } - - let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil) - - alert.addAction(addAction) - alert.addAction(cancelAction) - - present(alert, animated: true, completion: nil) - } - private func showImagePicker() { - // 1) PHPicker 설정 - var configuration = PHPickerConfiguration() - configuration.filter = .images // 이미지만 - configuration.selectionLimit = 0 // 0이면 무제한, 혹은 10, 5 등 제한 가능 - - let picker = PHPickerViewController(configuration: configuration) - picker.delegate = self - self.pickerViewController = picker - - // 2) 모달 표시 - present(picker, animated: true, completion: nil) - } - - private func presentDuplicateCategoryAlert() { - let alert = UIAlertController(title: "중복", message: "이미 존재하는 카테고리입니다.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) - present(alert, animated: true, completion: nil) + private func getCurrentFormattedTime() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM.dd HH:mm" + formatter.timeZone = TimeZone(identifier: "Asia/Seoul") + return formatter.string(from: Date()) } - private func updateCategoryButtonTitle(with category: String) { - categoryButton.setTitle("\(category) ▾", for: .normal) -     updateSaveButtonState() + private func setupCreationTimeLabel() -> UILabel { + let currentTime = getCurrentFormattedTime() + return makeSimpleLabel(currentTime) } - // MARK: - UI Helpers private func makeRoundedTextField(_ placeholder: String) -> UITextField { let tf = UITextField() tf.placeholder = placeholder @@ -989,548 +753,163 @@ final class PopUpStoreRegisterViewController: BaseViewController { return tf } - private func makeRoundedButton(_ title: String) -> UIButton { - let btn = UIButton(type: .system) - btn.setTitle(title, for: .normal) - btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) - btn.layer.cornerRadius = 8 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.lightGray.cgColor - btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) - return btn - } - - private func makeIconButton(_ title: String, iconName: String) -> UIButton { - let btn = makeRoundedButton(title) - if let icon = UIImage(named: iconName) { - btn.setImage(icon, for: .normal) - btn.imageView?.contentMode = .scaleAspectFit - btn.titleEdgeInsets = UIEdgeInsets(top: 0, left: 6, bottom: 0, right: 0) - } - return btn + private func makeRoundedTextView() -> UITextView { + let tv = UITextView() + tv.font = UIFont.systemFont(ofSize: 14) + tv.textColor = .darkGray + tv.layer.cornerRadius = 8 + tv.layer.borderWidth = 1 + tv.layer.borderColor = UIColor.lightGray.cgColor + tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) + tv.isScrollEnabled = true + return tv } - private func makeSimpleLabel(_ text: String) -> UILabel { + private func makePlainLabel(_ text: String) -> UILabel { let lbl = UILabel() lbl.text = text lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray + lbl.textAlignment = .right + lbl.setContentHuggingPriority(.required, for: .horizontal) return lbl } - private func makePlainLabel(_ text: String) -> UILabel { - // 작은 라벨(위도/경도/마커명/스니펫 등) + private func makeSimpleLabel(_ text: String) -> UILabel { let lbl = UILabel() lbl.text = text lbl.font = UIFont.systemFont(ofSize: 14) lbl.textColor = .darkGray - lbl.textAlignment = .right - lbl.setContentHuggingPriority(.required, for: .horizontal) return lbl } - private func makeRoundedTextView() -> UITextView { - let tv = UITextView() - tv.font = UIFont.systemFont(ofSize: 14) - tv.textColor = .darkGray - tv.layer.cornerRadius = 8 - tv.layer.borderWidth = 1 - tv.layer.borderColor = UIColor.lightGray.cgColor - tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) - tv.isScrollEnabled = true - return tv + // MARK: - UI Interaction Methods + @objc private func handleTap() { + view.endEditing(true) } -} -// MARK: - Padding -private extension UITextField { - func setLeftPaddingPoints(_ amount: CGFloat) { - let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) - leftView = paddingView - leftViewMode = .always - } -} -extension PopUpStoreRegisterViewController: UICollectionViewDataSource, UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return images.count + @objc private func onBack() { + navigationController?.popViewController(animated: true) } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: ImageCell.identifier, - for: indexPath - ) as? ImageCell else { - return UICollectionViewCell() + @objc private func keyboardWillShow(_ notification: Notification) { + guard let userInfo = notification.userInfo, + let keyboardFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect else { + return } - let item = images[indexPath.item] - cell.configure(with: item) - - // 대표이미지 변경 - cell.onMainCheckToggled = { [weak self] in - self?.toggleMainImage(index: indexPath.item) - } - // 개별 삭제 - cell.onDeleteTapped = { [weak self] in - self?.deleteImage(index: indexPath.item) - } + let keyboardHeight = keyboardFrame.height + let contentInset = UIEdgeInsets( + top: 0, + left: 0, + bottom: keyboardHeight, + right: 0 + ) - return cell - } -} + scrollView.contentInset = contentInset + scrollView.scrollIndicatorInsets = contentInset -// 헬퍼 메서드들 -private extension PopUpStoreRegisterViewController { - /// 대표이미지를 단 하나만 허용 -> 누른 index만 isMain = true - func toggleMainImage(index: Int) { - for imageIndex in 0.. (scrollView.frame.height - keyboardHeight) { + let scrollPoint = CGPoint( + x: 0, + y: bottomOffset - (scrollView.frame.height - keyboardHeight) + ) + scrollView.setContentOffset(scrollPoint, animated: true) } } - - // S3 삭제 로직 제거 - 서버 업데이트 후로 이동 - - // 이미지 배열에서 제거 - images.remove(at: index) - - // 대표 이미지가 삭제되었고, 다른 이미지가 남아있다면 첫 번째 이미지를 대표 이미지로 설정 - if wasMainImage && !images.isEmpty { - images[0].isMain = true - } - - imagesCollectionView.reloadData() - updateSaveButtonState() } -} -extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { - func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { - picker.dismiss(animated: true) - guard !results.isEmpty else { return } - - // nameField로부터 이름을 가져옴 - let name = self.nameField?.text ?? "unnamed" - let uuid = UUID().uuidString - - var newImages = [ExtendedImage]() - let itemProviders = results.map(\.itemProvider) - let dispatchGroup = DispatchGroup() - - // 이미 로드된 이미지 경로 목록 (중복 방지) - let existingPaths = Set(self.images.map { $0.filePath }) - Logger.log(message: "기존 이미지 경로 수: \(existingPaths.count)", category: .debug) - - for (index, provider) in itemProviders.enumerated() { - if provider.canLoadObject(ofClass: UIImage.self) { - dispatchGroup.enter() - provider.loadObject(ofClass: UIImage.self) { [weak self] object, _ in - defer { dispatchGroup.leave() } - guard let self = self, - let image = object as? UIImage else { return } - - let filePath = "PopUpImage/\(name)/\(uuid)/\(index).jpg" - - // 이미 같은 경로가 있는지 확인 (거의 발생하지 않겠지만 안전장치) - if existingPaths.contains(filePath) { - Logger.log(message: "중복된 이미지 경로 발견: \(filePath)", category: .debug) - return - } - - let extended = ExtendedImage( - filePath: filePath, - image: image, - isMain: false - ) - newImages.append(extended) - } - } + @objc private func keyboardWillHide(_ notification: Notification) { + UIView.animate(withDuration: 0.3) { + self.scrollView.contentInset = .zero + self.scrollView.scrollIndicatorInsets = .zero } + } - dispatchGroup.notify(queue: .main) { - if newImages.isEmpty { - Logger.log(message: "추가할 새 이미지가 없음", category: .debug) - return - } - - Logger.log(message: "새 이미지 \(newImages.count)개 추가", category: .debug) - self.images.append(contentsOf: newImages) + private func showImagePicker() { + var configuration = PHPickerConfiguration() + configuration.filter = .images + configuration.selectionLimit = 0 // 무제한 - if !self.images.isEmpty && !self.images.contains(where: { $0.isMain }) { - self.images[0].isMain = true // 첫 번째 이미지를 대표 이미지로 - Logger.log(message: "대표 이미지 설정: \(self.images[0].filePath)", category: .debug) - } + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = self + self.pickerViewController = picker - self.imagesCollectionView.reloadData() - self.updateSaveButtonState() - } + present(picker, animated: true, completion: nil) } -} - -private extension PopUpStoreRegisterViewController { - private func validateForm() -> Bool { - // (1) 팝업스토어 이름 - Logger.log(message: "nameField.text = \(nameField?.text ?? "nil")", category: .debug) - guard let nameField = nameField, !(nameField.text ?? "").isEmpty else { - Logger.log( - message: "이름 필드가 비어 있습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false - } - - // (2) 카테고리 선택 - if categoryButton.title(for: .normal) == "카테고리 선택 ▾" { - Logger.log( - message: "카테고리가 선택되지 않았습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false - } - // (3) 주소 - Logger.log(message: "addressField = \(addressField != nil ? "초기화됨" : "nil")", category: .debug) - Logger.log(message: "addressField.text = \(addressField?.text ?? "nil")", category: .debug) - guard let addressField = addressField, !(addressField.text ?? "").isEmpty else { - Logger.log(message: "주소 필드가 비어 있습니다.", category: .debug) - return false - } + private func showCategoryPicker() { + let categories = ["게임", "라이프스타일", "반려동물", "뷰티", "스포츠", "애니메이션", "엔터테인먼트", "여행", "예술", "음식/요리", "키즈", "패션"] - // (4) 위도/경도 - Logger.log(message: "latField.text = \(latField?.text ?? "nil")", category: .debug) - Logger.log(message: "lonField.text = \(lonField?.text ?? "nil")", category: .debug) - guard let latField = latField, - let lonField = lonField, - let latText = latField.text, !latText.isEmpty, - let lonText = lonField.text, !lonText.isEmpty, - let latVal = Double(latText), let lonVal = Double(lonText), - latVal != 0 || lonVal != 0 else { - Logger.log( - message: "위도/경도 값이 잘못되었습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false - } + let alertController = UIAlertController(title: "카테고리 선택", message: nil, preferredStyle: .actionSheet) - // (5) 설명 - guard let descTV = descTV, !(descTV.text ?? "").isEmpty else { - Logger.log( - message: "설명 필드가 비어 있습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false + for category in categories { + let action = UIAlertAction(title: category, style: .default) { [weak self] _ in + self?.reactor?.action.onNext(.selectCategory(category)) + } + alertController.addAction(action) } - // (6) 이미지 ≥ 1장 - if images.isEmpty { - Logger.log( - message: "이미지가 추가되지 않았습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false - } + let cancelAction = UIAlertAction(title: "취소", style: .cancel, handler: nil) + alertController.addAction(cancelAction) - // (7) 대표 이미지 설정 여부 - if !images.contains(where: { $0.isMain }) { - Logger.log( - message: "대표 이미지가 설정되지 않았습니다.", - category: .debug, - fileName: #file, - line: #line - ) - return false + if let popoverController = alertController.popoverPresentationController { + popoverController.sourceView = categoryButton + popoverController.sourceRect = categoryButton.bounds } - Logger.log( - message: "모든 조건이 충족되었습니다.", - category: .info, - fileName: #file, - line: #line - ) - return true + present(alertController, animated: true, completion: nil) } - private func doRegister() { - Logger.log(message: "doRegister() 호출됨", category: .debug) - // 1. 폼 데이터 검증 - guard validateFormData() else { return } - - if let editingStore = editingStore { - // 수정 모드 - updateStore(editingStore) - } else { - // 새로 등록 모드 - // 2. 이미지 업로드 실행 - uploadImages() + private func showDateRangePicker() { + DateTimePickerManager.shared.showDateRange(on: self) { [weak self] start, end in + self?.reactor?.action.onNext(.selectDateRange(start: start, end: end)) } } - // 폼 데이터 검증 - private func validateFormData() -> Bool { - guard let name = nameField?.text, - let address = addressField?.text, - let latitude = latField?.text, Double(latitude) != nil, - let longitude = lonField?.text, Double(longitude) != nil, - let description = descTV?.text, - !images.isEmpty else { - Logger.log(message: "폼 데이터 검증 실패", category: .error) - return false + private func showTimeRangePicker() { + DateTimePickerManager.shared.showTimeRange(on: self) { [weak self] start, end in + self?.reactor?.action.onNext(.selectTimeRange(start: start, end: end)) } - Logger.log(message: "폼 데이터 검증 성공", category: .debug) - return true } - private func updateStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - // 기존에 저장된 이미지는 재업로드하지 않고 그대로 사용 - // 새로 추가된 이미지만 업로드 - // 새로 추가된 이미지만 필터링 - let newImages = images.filter { image in - return !originalImageIds.keys.contains(image.filePath) - } - - if !newImages.isEmpty { - // 새 이미지만 업로드 - uploadNewImagesForUpdate(store, newImages: newImages) - } else { - // 새 이미지가 없으면 바로 스토어 정보 업데이트 - updateStoreInfo(store, updatedImagePaths: nil) - } + private func updateCategoryButtonTitle(with category: String) { + categoryButton.setTitle("\(category) ▾", for: .normal) } - private func uploadNewImagesForUpdate(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore, newImages: [ExtendedImage]) { - let uuid = UUID().uuidString - let updatedImages = newImages.enumerated().map { index, image in - let filePath = "PopUpImage/\(nameField?.text ?? "")/\(uuid)/\(index).jpg" - return ExtendedImage( - filePath: filePath, - image: image.image, - isMain: image.isMain) - } - - presignedService.tryUpload(datas: updatedImages.map { - PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) - }) - .subscribe( - onSuccess: { [weak self] _ in - guard let self = self else { return } - Logger.log(message: "새 이미지 업로드 성공", category: .info) - - // 모든 이미지 경로 (기존 이미지 + 새 이미지) - var allPaths: [String] = [] - - // 삭제되지 않은 기존 이미지 경로 추가 - let deletedIdSet = Set(self.deletedImageIds) - for (path, id) in self.originalImageIds { - if !deletedIdSet.contains(id) { - allPaths.append(path) - } - } - // 새로 업로드된 이미지 경로 추가 - let newPaths = updatedImages.map { $0.filePath } - allPaths.append(contentsOf: newPaths) - - // 메인 이미지 경로 결정 - let mainImage: String - if let mainImg = self.images.first(where: { $0.isMain }) { - if self.originalImageIds.keys.contains(mainImg.filePath) { - // 기존 이미지가 메인 - mainImage = mainImg.filePath - } else { - // 새 이미지가 메인인 경우, 새 경로 찾기 - let idx = self.images.firstIndex(where: { $0.filePath == mainImg.filePath }) ?? 0 - if idx < updatedImages.count { - mainImage = updatedImages[idx].filePath - } else { - mainImage = updatedImages.first?.filePath ?? "" - } - } - } else if !allPaths.isEmpty { - mainImage = allPaths[0] - } else { - mainImage = "" - } + private func updatePeriodButtonTitle(start: Date, end: Date) { + let df = DateFormatter() + df.dateFormat = "yyyy.MM.dd" + let sStr = df.string(from: start) + let eStr = df.string(from: end) - self.updateStoreInfo(store, updatedImagePaths: allPaths, mainImage: mainImage) - }, - onError: { [weak self] error in - Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) - self?.showErrorAlert(message: "이미지 업로드 실패: \(error.localizedDescription)") - } - ) - .disposed(by: disposeBag) + periodButton.setTitle("\(sStr) ~ \(eStr)", for: .normal) } - private func uploadImagesForUpdate(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - let uuid = UUID().uuidString - let updatedImages = images.enumerated().map { index, image in - let filePath = "PopUpImage/\(nameField?.text ?? "")/\(uuid)/\(index).jpg" - return ExtendedImage( - filePath: filePath, - image: image.image, - isMain: image.isMain) - } + private func updateTimeButtonTitle(start: Date, end: Date) { + let df = DateFormatter() + df.dateFormat = "HH:mm" + let stStr = df.string(from: start) + let etStr = df.string(from: end) - presignedService.tryUpload(datas: updatedImages.map { - PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) - }) - .subscribe( - onSuccess: { [weak self] _ in - guard let self = self else { return } - Logger.log(message: "이미지 업로드 성공", category: .info) - let imagePaths = updatedImages.map { $0.filePath } - self.updateStoreInfo(store, updatedImagePaths: imagePaths) - }, - onError: { [weak self] error in - Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) - self?.showErrorAlert(message: "이미지 업로드 실패: \(error.localizedDescription)") - } - ) - .disposed(by: disposeBag) + timeButton.setTitle("\(stStr) ~ \(etStr)", for: .normal) } - private func updateStoreInfo(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore, updatedImagePaths: [String]?, mainImage: String? = nil) { - guard let name = nameField?.text, - let address = addressField?.text, - let latitude = Double(latField?.text ?? ""), - let longitude = Double(lonField?.text ?? ""), - let description = descTV?.text, - let categoryTitle = categoryButton.title(for: .normal)?.replacingOccurrences(of: " ▾", with: "") else { - return - } - - // 메인 이미지 결정 - let finalMainImage: String - if let mainImagePath = mainImage { - finalMainImage = mainImagePath - } else if let firstImage = updatedImagePaths?.first { - finalMainImage = firstImage - } else { - // 이미지가 없는 경우 기존 메인 이미지 사용 - finalMainImage = store.mainImageUrl - } - - // 이미지 URL 목록 결정 - let imageUrls: [String] - if let paths = updatedImagePaths, !paths.isEmpty { - imageUrls = paths - } else { - // 이미지 변동이 없을 경우 - imageUrls = [store.mainImageUrl] - } - - // 서버에 스토어 정보 업데이트 요청 - let request = UpdatePopUpStoreRequestDTO( - popUpStore: .init( - id: store.id, - name: name, - categoryId: Int64(getCategoryId(from: categoryTitle)), - desc: description, - address: address, - startDate: getFormattedDate(from: selectedStartDate), - endDate: getFormattedDate(from: selectedEndDate), - mainImageUrl: finalMainImage, - bannerYn: !finalMainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, - imageUrl: imageUrls, - startDateBeforeEndDate: true - ), - location: .init( - latitude: latitude, - longitude: longitude, - markerTitle: "마커 제목", - markerSnippet: "마커 설명" - ), - imagesToAdd: updatedImagePaths?.filter { !originalImageIds.keys.contains($0) } ?? [], - imagesToDelete: deletedImageIds - ) - - // 요청 데이터 로깅 - Logger.log(message: "업데이트 요청 데이터: \(request)", category: .debug) - - adminUseCase.updateStore(request: request) - .subscribe( - onNext: { [weak self] _ in - guard let self = self else { return } - Logger.log(message: "updateStore API 호출 성공", category: .info) - - // 서버 응답이 성공하면 S3에서 이미지 삭제 수행 - self.deleteImagesFromS3() - - // 성공 시 저장된 삭제 이미지 정보 초기화 - if let storeId = self.editingStore?.id { - UserDefaults.standard.removeObject(forKey: "deletedImageIds_\(storeId)") - UserDefaults.standard.removeObject(forKey: "deletedImagePaths_\(storeId)") - Logger.log(message: "삭제된 이미지 정보 영구 저장소에서 제거 완료", category: .debug) - } - - // 메모리 내 삭제 목록도 초기화 - self.deletedImageIds.removeAll() - self.deletedImagePaths.removeAll() - - self.showSuccessAlert(isUpdate: true) - }, - onError: { [weak self] error in - Logger.log(message: "updateStore API 호출 실패: \(error.localizedDescription)", category: .error) - self?.showErrorAlert(message: error.localizedDescription) - } - ) - .disposed(by: disposeBag) - + private func showLoadingIndicator() { + // 로딩 인디케이터 표시 로직 구현 + // 예: Activity Indicator 또는 커스텀 로딩 뷰 표시 } - private func deleteImagesFromS3() { - // 삭제해야 할 이미지가 없으면 바로 리턴 - guard !deletedImagePaths.isEmpty else { return } - - // 모든 이미지 한 번에 삭제 - presignedService.tryDelete(targetPaths: .init(objectKeyList: deletedImagePaths)) - .subscribe( - onCompleted: { - Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(self.deletedImagePaths.count)개", category: .info) - }, - onError: { error in - Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) - } - ) - .disposed(by: disposeBag) + + private func hideLoadingIndicator() { + // 로딩 인디케이터 숨김 로직 구현 } - private func showSuccessAlert(isUpdate: Bool = false) { + private func showSuccessAlert(isUpdate: Bool) { let message = isUpdate ? "팝업스토어가 성공적으로 수정되었습니다." : "팝업스토어가 성공적으로 등록되었습니다." let alert = UIAlertController( title: isUpdate ? "수정 성공" : "등록 성공", @@ -1544,229 +923,112 @@ private extension PopUpStoreRegisterViewController { present(alert, animated: true) } - // 이미지 업로드 - private func uploadImages() { - let uuid = UUID().uuidString - // let baseS3URL = Secrets.popPoolS3BaseURL.rawValue - let updatedImages = images.enumerated().map { index, image in - let filePath = "PopUpImage/\(nameField?.text ?? "")/\(uuid)/\(index).jpg" - return ExtendedImage( - filePath: filePath, - image: image.image, - isMain: image.isMain) - } - - // let presignedService = PreSignedService() - presignedService.tryUpload(datas: updatedImages.map { - PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) - }) - // .observe(on: MainScheduler.instance) - .subscribe( - onSuccess: { [weak self] _ in - guard let self = self else { return } - Logger.log(message: "이미지 업로드 성공", category: .info) - - let imagePaths = updatedImages.map { $0.filePath } - let mainImage = updatedImages.first { $0.isMain }?.filePath ?? "" - self.callCreateStoreAPI(mainImage: mainImage, imagePaths: imagePaths) // baseURL 제거 - }, - onError: { error in - Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) - self.showErrorAlert(message: "이미지 업로드 실패: \(error.localizedDescription)") - } + private func showErrorAlert(message: String) { + let alert = UIAlertController( + title: "오류", + message: message, + preferredStyle: .alert ) - .disposed(by: disposeBag) + alert.addAction(UIAlertAction(title: "확인", style: .default)) + present(alert, animated: true) + } +} +// MARK: - UICollectionView DataSource & Delegate +extension PopUpStoreRegisterViewController: UICollectionViewDataSource, UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return reactor?.currentState.images.count ?? 0 } - // createStore API 호출 - private func callCreateStoreAPI(mainImage: String, imagePaths: [String]) { - guard let name = nameField?.text, - let address = addressField?.text, - let latitude = Double(latField?.text ?? ""), - let longitude = Double(lonField?.text ?? ""), - let description = descTV?.text, - let categoryTitle = categoryButton.title(for: .normal)?.replacingOccurrences(of: " ▾", with: "") else { - Logger.log(message: "필수 입력값이 비어 있음", category: .error) - return + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: ImageCell.identifier, + for: indexPath + ) as? ImageCell, + let images = reactor?.currentState.images, + indexPath.item < images.count else { + return UICollectionViewCell() } - let categoryId = getCategoryId(from: categoryTitle) - - Logger.log( - message: """ - 팝업스토어 등록 요청: - - 이름: \(name) - - 카테고리: \(categoryTitle) (ID: \(categoryId)) - - 주소: \(address) - - 위도/경도: (\(latitude), \(longitude)) - - 설명: \(description) - - 시작일: \(getFormattedDate(from: selectedStartDate)) - - 종료일: \(getFormattedDate(from: selectedEndDate)) - - 메인이미지: \(mainImage) - - 전체이미지: \(imagePaths) - """, - category: .network - ) - - let bannerYn = !mainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty - let dates = prepareDateTime() - let isValidDateOrder = validateDates(start: selectedStartDate, end: selectedEndDate) - - let request = CreatePopUpStoreRequestDTO( - name: name, - categoryId: Int64(categoryId), - desc: description, - address: address, - startDate: dates.startDate, - endDate: dates.endDate, - mainImageUrl: mainImage, - imageUrlList: imagePaths, - latitude: latitude, - longitude: longitude, - markerTitle: "마커 제목", - markerSnippet: "마커 설명", - startDateBeforeEndDate: isValidDateOrder - ) + let item = images[indexPath.item] + cell.configure(with: item) - adminUseCase.createStore(request: request) - .subscribe( - onNext: { [weak self] _ in - Logger.log(message: "createStore API 호출 성공", category: .info) - self?.showSuccessAlert() - }, - onError: { [weak self] error in - Logger.log(message: "createStore API 호출 실패: \(error.localizedDescription)", category: .error) - self?.showErrorAlert(message: error.localizedDescription) - } - ) - .disposed(by: disposeBag) - } - private func getCategoryId(from title: String) -> Int { - Logger.log(message: "카테고리 매핑 시작 - 타이틀: \(title)", category: .debug) - - let categoryMap: [String: Int64] = [ - "패션": 1, - "라이프스타일": 2, - "뷰티": 3, - "음식/요리": 4, - "예술": 5, - "반려동물": 6, - "여행": 7, - "엔터테인먼트": 8, - "애니메이션": 9, - "키즈": 10, - "스포츠": 11, - "게임": 12 - ] - - if let id = categoryMap[title] { - Logger.log(message: "카테고리 매핑 성공: \(title) -> \(id)", category: .debug) - return Int(id) - } else { - Logger.log(message: "카테고리 매핑 실패: \(title)에 해당하는 ID를 찾을 수 없음", category: .error) - return 1 // 기본값 + // 대표이미지 변경 + cell.onMainCheckToggled = { [weak self] in + self?.reactor?.action.onNext(.toggleMainImage(indexPath.item)) } - } - - private func createDateTime(date: Date?, time: Date?) -> Date? { - guard let date = date else { return nil } - - if let time = time { - let calendar = Calendar.current - - // 날짜 부분 추출 - var components = calendar.dateComponents([.year, .month, .day], from: date) - - // 시간 부분 추출 - let timeComponents = calendar.dateComponents([.hour, .minute], from: time) - // 날짜와 시간 결합 - components.hour = timeComponents.hour - components.minute = timeComponents.minute - components.second = 0 - - // 명시적으로 한국 시간대 지정 - components.timeZone = TimeZone(identifier: "Asia/Seoul") - - return calendar.date(from: components) + // 개별 삭제 + cell.onDeleteTapped = { [weak self] in + self?.reactor?.action.onNext(.removeImage(indexPath.item)) } - return date + return cell } +} - private func getFormattedDate(from date: Date?) -> String { - guard let date = date else { return "" } +// MARK: - PHPickerViewController Delegate +extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { + func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true) + guard !results.isEmpty else { return } - // 한국 시간대를 명시적으로 지정하고 ISO 8601 형식으로 변환 - let formatter = DateFormatter() - formatter.timeZone = TimeZone(identifier: "Asia/Seoul") // 한국 시간대로 명시 - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" + let itemProviders = results.map(\.itemProvider) + let dispatchGroup = DispatchGroup() + var newImages = [ExtendedImage]() - // Z 표기를 추가하지 않음 (시간대 정보 없음) - return formatter.string(from: date) - } + // 이미 로드된 이미지 경로 목록 (중복 방지) + let existingPaths = Set(reactor?.currentState.images.map { $0.filePath } ?? []) - private func prepareDateTime() -> (startDate: String, endDate: String) { - // 시작일/시간 결합 - let startDateTime = createDateTime(date: selectedStartDate, time: selectedStartTime) + for (index, provider) in itemProviders.enumerated() { + if provider.canLoadObject(ofClass: UIImage.self) { + dispatchGroup.enter() + provider.loadObject(ofClass: UIImage.self) { [weak self] object, _ in + defer { dispatchGroup.leave() } + guard let image = object as? UIImage else { return } - // 종료일/시간 결합 - let endDateTime = createDateTime(date: selectedEndDate, time: selectedEndTime) + let name = self?.reactor?.currentState.name ?? "unnamed" + let uuid = UUID().uuidString + let filePath = "PopUpImage/\(name)/\(uuid)/\(index).jpg" - // 디버그용 로그 - if let startTime = selectedStartTime, let endTime = selectedEndTime { - let timeFormatter = DateFormatter() - timeFormatter.dateFormat = "HH:mm" - Logger.log(message: "선택된 시작 시간: \(timeFormatter.string(from: startTime))", category: .debug) - Logger.log(message: "선택된 종료 시간: \(timeFormatter.string(from: endTime))", category: .debug) + // 이미 같은 경로가 있는지 확인 + if !existingPaths.contains(filePath) { + let extended = ExtendedImage( + filePath: filePath, + image: image, + isMain: false + ) + newImages.append(extended) + } + } + } } - // 결합된 날짜/시간 로깅 - if let start = startDateTime, let end = endDateTime { - let dateTimeFormatter = DateFormatter() - dateTimeFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss" - Logger.log(message: "결합된 시작 일시: \(dateTimeFormatter.string(from: start))", category: .debug) - Logger.log(message: "결합된 종료 일시: \(dateTimeFormatter.string(from: end))", category: .debug) + dispatchGroup.notify(queue: .main) { [weak self] in + if !newImages.isEmpty { + self?.reactor?.action.onNext(.addImages(newImages)) + } } - - let startDate = getFormattedDate(from: startDateTime) - let endDate = getFormattedDate(from: endDateTime) - - Logger.log(message: "서버로 전송될 시작 일시: \(startDate)", category: .debug) - Logger.log(message: "서버로 전송될 종료 일시: \(endDate)", category: .debug) - - return (startDate: startDate, endDate: endDate) } +} - // 새로운 검증 함수 추가 (prepareDateTime 함수 아래에 추가) - private func validateDates(start: Date?, end: Date?) -> Bool { - guard let start = start, let end = end else { return false } - return start < end +// MARK: - UITextView Delegate +extension PopUpStoreRegisterViewController: UITextViewDelegate { + func textViewDidChange(_ textView: UITextView) { + if textView == descTV { + reactor?.action.onNext(.updateDescription(textView.text)) + } } +} - private func showSuccessAlert() { - let alert = UIAlertController( - title: "등록 성공", - message: "팝업스토어가 성공적으로 등록되었습니다.", - preferredStyle: .alert - ) - alert.addAction(UIAlertAction(title: "확인", style: .default, handler: { [weak self] _ in - // 성공 후 닫기와 핸들러 호출 - self?.completionHandler?() - self?.navigationController?.popViewController(animated: true) - })) - present(alert, animated: true) - } - private func showErrorAlert(message: String) { - let alert = UIAlertController( - title: "등록 실패", - message: message, - preferredStyle: .alert - ) - alert.addAction(UIAlertAction(title: "확인", style: .default)) - present(alert, animated: true) +// MARK: - Helper Extensions +extension UITextField { + func setLeftPaddingPoints(_ amount: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) + leftView = paddingView + leftViewMode = .always } } + extension UIView { func findFirstResponder() -> UIView? { if isFirstResponder { @@ -1782,78 +1044,3 @@ extension UIView { return nil } } -extension PopUpStoreRegisterViewController: UITextFieldDelegate { - private func setupAddressField() { - // RxCocoa를 사용한 텍스트 필드 바인딩 - addressField?.rx.text.orEmpty - .distinctUntilChanged() - .debounce(.milliseconds(500), scheduler: MainScheduler.instance) - .filter { !$0.isEmpty } - .flatMapLatest { [weak self] address -> Observable in - return Observable.create { observer in - let geocoder = CLGeocoder() - let fullAddress = "\(address), Korea" - - geocoder.geocodeAddressString( - fullAddress, - in: nil, - preferredLocale: Locale(identifier: "ko_KR") - ) { placemarks, error in - if let error = error { - print("Geocoding error: \(error.localizedDescription)") - observer.onNext(nil) - observer.onCompleted() - return - } - - if let location = placemarks?.first?.location { - observer.onNext(location) - } else { - observer.onNext(nil) - } - observer.onCompleted() - } - - return Disposables.create() - } - } - .observe(on: MainScheduler.instance) - .subscribe(onNext: { [weak self] location in - guard let location = location else { return } - self?.latField?.text = String(format: "%.6f", location.coordinate.latitude) - self?.lonField?.text = String(format: "%.6f", location.coordinate.longitude) - self?.updateSaveButtonState() - }) - .disposed(by: disposeBag) - } - - @objc private func addressFieldDidChange(_ textField: UITextField) { - guard let address = textField.text, !address.isEmpty else { return } - - // 한국 주소임을 명시 - let geocoder = CLGeocoder() - let addressWithCountry = address + ", South Korea" - - geocoder.geocodeAddressString(addressWithCountry) { [weak self] placemarks, error in - if let error = error { - print("Geocoding error: \(error.localizedDescription)") - return - } - - guard let location = placemarks?.first?.location else { return } - - DispatchQueue.main.async { - self?.latField?.text = String(format: "%.6f", location.coordinate.latitude) - self?.lonField?.text = String(format: "%.6f", location.coordinate.longitude) - self?.updateSaveButtonState() - } - } - } - -} -extension PopUpStoreRegisterViewController: UITextViewDelegate { - func textViewDidChange(_ textView: UITextView) { - Logger.log(message: "설명 필드 값 변경: \(textView.text.count) 글자", category: .debug) - updateSaveButtonState() - } -} From 29738f810413fa3bf484e9a948fed3ce4bd1eb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Fri, 11 Apr 2025 11:50:41 +0900 Subject: [PATCH 098/393] =?UTF-8?q?refactor/#102:=20RegisterVC=20View?= =?UTF-8?q?=EC=99=80=20PopUpImagesColletionView=EB=A1=9C=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 12 +- .../PopUpImagesCollectionView.swift | 97 ++ .../PopUpStoreRegisterReactor.swift | 90 +- .../PopUpStoreRegisterView.swift | 547 ++++++--- .../PopUpStoreRegisterViewController.swift | 1081 +++++------------ 5 files changed, 888 insertions(+), 939 deletions(-) create mode 100644 Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpImagesCollectionView.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 22a67e02..8227f3a4 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -429,6 +429,7 @@ 4EDE57032D5E70650014D924 /* LocationPermissionBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; 4EE5A3D32D40E4A600A2469A /* MapGuideReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */; }; + 4EEA13072DA7CDDA00775256 /* PopUpImagesCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */; }; 4EEA1D8F2D352012003E7DE9 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */; }; 4EEA1D912D352027003E7DE9 /* ExtendedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */; }; 4EECA3942D56770B00A07CCA /* MapPopUpStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBB2D12CEB6001EF91C /* MapPopUpStore.swift */; }; @@ -891,6 +892,7 @@ 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimePickerManager.swift; sourceTree = ""; }; 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPermissionBottomSheet.swift; sourceTree = ""; }; 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapGuideReactor.swift; sourceTree = ""; }; + 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpImagesCollectionView.swift; sourceTree = ""; }; 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCell.swift; sourceTree = ""; }; 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedImage.swift; sourceTree = ""; }; 4EED9BAB2D22730400B288E7 /* FilterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterType.swift; sourceTree = ""; }; @@ -2736,6 +2738,7 @@ 4E9C12802D2BE0A6006744D6 /* PopUpStoreRegisterViewController.swift */, 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */, 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */, + 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */, ); path = AdminRegister; sourceTree = ""; @@ -3165,6 +3168,7 @@ 086DD9362D00963900B97D3B /* SearchTitleSection.swift in Sources */, 086F89D92D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift in Sources */, 083C864F2D0DD3A6003F441C /* AddCommentImageSection.swift in Sources */, + 4EEA13072DA7CDDA00775256 /* PopUpImagesCollectionView.swift in Sources */, 08B191372CF366680057BC04 /* UICollectionViewCell+.swift in Sources */, 083A25B42CF362670099B58E /* Responsable.swift in Sources */, 08CBEA0D2D38ED0D00248007 /* CountButtonView.swift in Sources */, @@ -3706,7 +3710,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3732,7 +3736,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*]" = poppoolProvisioningProfile; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -3751,7 +3755,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -3777,7 +3781,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*]" = poppoolProvisioningProfile; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpImagesCollectionView.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpImagesCollectionView.swift new file mode 100644 index 00000000..e36be825 --- /dev/null +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpImagesCollectionView.swift @@ -0,0 +1,97 @@ +import UIKit + +import SnapKit + +final class PopUpImagesCollectionView: UICollectionView { + // MARK: - Properties + enum Constant { + static let imageWidth: CGFloat = 80 + static let imageHeight: CGFloat = 120 + static let imageSpacing: CGFloat = 8 + } + + var onImageSelected: ((Int) -> Void)? + var onMainImageToggled: ((Int) -> Void)? + var onImageDeleted: ((Int) -> Void)? + + private var images: [ExtendedImage] = [] + + // MARK: - init + init() { + let layout = UICollectionViewFlowLayout() + layout.scrollDirection = .horizontal + layout.itemSize = CGSize(width: Constant.imageWidth, height: Constant.imageHeight) + layout.minimumLineSpacing = Constant.imageSpacing + + super.init(frame: .zero, collectionViewLayout: layout) + + self.addViews() + self.setupContstraints() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - Setup +private extension PopUpImagesCollectionView { + func addViews() { } + + func setupContstraints() { } + + func configureUI() { + self.backgroundColor = .clear + self.register(ImageCell.self, forCellWithReuseIdentifier: ImageCell.identifier) + self.dataSource = self + self.delegate = self + self.showsHorizontalScrollIndicator = false + } +} + +// MARK: - Public Methods +extension PopUpImagesCollectionView { + func updateImages(_ images: [ExtendedImage]) { + self.images = images + self.reloadData() + } +} + +// MARK: - UICollectionViewDataSource +extension PopUpImagesCollectionView: UICollectionViewDataSource { + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.images.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + guard let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: ImageCell.identifier, + for: indexPath + ) as? ImageCell else { + return UICollectionViewCell() + } + + let item = self.images[indexPath.item] + cell.configure(with: item) + + // 대표이미지 변경 + cell.onMainCheckToggled = { [weak self] in + self?.onMainImageToggled?(indexPath.item) + } + + // 개별 삭제 + cell.onDeleteTapped = { [weak self] in + self?.onImageDeleted?(indexPath.item) + } + + return cell + } +} + +// MARK: - UICollectionViewDelegate +extension PopUpImagesCollectionView: UICollectionViewDelegate { + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.onImageSelected?(indexPath.item) + } +} diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 7b5e4539..124408ac 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -15,6 +15,9 @@ final class PopUpStoreRegisterReactor: Reactor { private let isEditMode: Bool private let editingStoreId: Int64? + private var disposeBag = DisposeBag() + + init(adminUseCase: AdminUseCase, presignedService: PreSignedService, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { self.adminUseCase = adminUseCase self.presignedService = presignedService @@ -142,6 +145,8 @@ final class PopUpStoreRegisterReactor: Reactor { case let .updateName(name): return .just(.setName(name)) + + case let .updateAddress(address): return .just(.setAddress(address)) @@ -396,24 +401,76 @@ final class PopUpStoreRegisterReactor: Reactor { // 폼 유효성 검사 private func validateForm(state: State) -> Bool { - // 필수 필드 검사 - guard !state.name.isEmpty, - !state.address.isEmpty, - !state.lat.isEmpty, - !state.lon.isEmpty, - !state.description.isEmpty, - !state.category.isEmpty, - !state.images.isEmpty, - state.images.contains(where: { $0.isMain }), - state.selectedStartDate != nil, - state.selectedEndDate != nil else { + Logger.log(message: "폼 유효성 검사 시작", category: .debug) + + // 이름 필드 검사 + guard !state.name.isEmpty else { + Logger.log(message: "유효성 검사 실패: 이름 비어있음", category: .debug) + return false + } + + // 주소 필드 검사 + guard !state.address.isEmpty else { + Logger.log(message: "유효성 검사 실패: 주소 비어있음", category: .debug) + return false + } + + // 위도/경도 필드 검사 + guard !state.lat.isEmpty else { + Logger.log(message: "유효성 검사 실패: 위도 비어있음", category: .debug) + return false + } + + guard !state.lon.isEmpty else { + Logger.log(message: "유효성 검사 실패: 경도 비어있음", category: .debug) + return false + } + + // 설명 필드 검사 + guard !state.description.isEmpty else { + Logger.log(message: "유효성 검사 실패: 설명 비어있음", category: .debug) + return false + } + + // 카테고리 필드 검사 + guard !state.category.isEmpty else { + Logger.log(message: "유효성 검사 실패: 카테고리 비어있음", category: .debug) + return false + } + + // 이미지 검사 + guard !state.images.isEmpty else { + Logger.log(message: "유효성 검사 실패: 이미지 없음", category: .debug) + return false + } + + // 대표 이미지 검사 + guard state.images.contains(where: { $0.isMain }) else { + Logger.log(message: "유효성 검사 실패: 대표 이미지 없음", category: .debug) + return false + } + + // 날짜 검사 + guard state.selectedStartDate != nil else { + Logger.log(message: "유효성 검사 실패: 시작 날짜 없음", category: .debug) + return false + } + + guard state.selectedEndDate != nil else { + Logger.log(message: "유효성 검사 실패: 종료 날짜 없음", category: .debug) return false } // 위도/경도 유효성 검사 guard let latVal = Double(state.lat), - let lonVal = Double(state.lon), - latVal != 0 || lonVal != 0 else { + let lonVal = Double(state.lon) else { + Logger.log(message: "유효성 검사 실패: 위도/경도 형식 오류", category: .debug) + return false + } + + // 위도/경도 값이 유효한지 검사 + guard latVal != 0 || lonVal != 0 else { + Logger.log(message: "유효성 검사 실패: 위도/경도 값이 모두 0", category: .debug) return false } @@ -421,14 +478,19 @@ final class PopUpStoreRegisterReactor: Reactor { if let startDate = state.selectedStartDate, let endDate = state.selectedEndDate, startDate > endDate { + Logger.log(message: "유효성 검사 실패: 시작일이 종료일보다 늦음", category: .debug) return false } + Logger.log(message: "유효성 검사 성공", category: .debug) return true } + // 주소 지오코딩 private func geocodeAddress(address: String) -> Observable { + Logger.log(message: "지오코딩 함수 호출: \(address)", category: .debug) + return Observable.create { observer in let geocoder = CLGeocoder() let fullAddress = "\(address), Korea" @@ -470,7 +532,7 @@ final class PopUpStoreRegisterReactor: Reactor { Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) } ) - .disposed(by: DisposeBag()) + .disposed(by: disposeBag) } // 카테고리 ID 매핑 diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift index 5b4b04fc..d523fc06 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift @@ -1,15 +1,29 @@ -import RxCocoa -import RxSwift -import SnapKit import UIKit -final class PopUpStoreRegisterView: UIView { - // 상단 네비게이션 영역 +import SnapKit + +final class PopUpRegisterView: UIView { + // MARK: - Properties + + enum Constant { + static let navigationHeight: CGFloat = 44 + static let logoWidth: CGFloat = 22 + static let logoHeight: CGFloat = 35 + static let edgeInset: CGFloat = 16 + static let buttonSize: CGFloat = 32 + static let cornerRadius: CGFloat = 8 + static let verticalSpacing: CGFloat = 8 + static let formLabelWidth: CGFloat = 80 + } + + // 네비게이션 영역 + let navigationContainer = UIView() + let logoImageView: UIImageView = { - let iv = UIImageView() - iv.image = UIImage(named: "image_login_logo") - iv.contentMode = .scaleAspectFit - return iv + let imageView = UIImageView() + imageView.image = UIImage(named: "image_login_logo") + imageView.contentMode = .scaleAspectFit + return imageView }() let accountIdLabel: UILabel = { @@ -27,6 +41,8 @@ final class PopUpStoreRegisterView: UIView { }() // 타이틀 영역 + let titleContainer = UIView() + let backButton: UIButton = { let btn = UIButton(type: .system) btn.setImage(UIImage(systemName: "chevron.left"), for: .normal) @@ -42,14 +58,47 @@ final class PopUpStoreRegisterView: UIView { return lbl }() - // 입력 폼 영역 - let nameTextField: UITextField = { - let tf = UITextField() - tf.placeholder = "팝업스토어 이름을 입력해 주세요." - tf.font = UIFont.systemFont(ofSize: 14) - tf.borderStyle = .roundedRect - return tf - }() + // 스크롤 영역 + let scrollView = UIScrollView() + let contentView = UIView() + + // 이미지 영역 + let addImageButton = UIButton(type: .system).then { + $0.setTitle("이미지 추가", for: .normal) + $0.setTitleColor(.systemBlue, for: .normal) + } + + let removeAllButton = UIButton(type: .system).then { + $0.setTitle("전체 삭제", for: .normal) + $0.setTitleColor(.red, for: .normal) + } + + let imagesCollectionView: PopUpImagesCollectionView = PopUpImagesCollectionView() + + // 폼 영역 + let formBackgroundView = UIView().then { + $0.backgroundColor = .white + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.layer.cornerRadius = 8 + } + + let verticalStack = UIStackView().then { + $0.axis = .vertical + $0.spacing = 0 + } + + // 폼 필드들 + let nameField = UITextField().then { + $0.placeholder = "팝업스토어 이름을 입력해 주세요." + $0.font = UIFont.systemFont(ofSize: 14) + $0.textColor = .darkGray + $0.borderStyle = .none + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.setLeftPaddingPoints(8) + } let categoryButton: UIButton = { let btn = UIButton(type: .system) @@ -64,68 +113,76 @@ final class PopUpStoreRegisterView: UIView { return btn }() - let addressTextField: UITextField = { - let tf = UITextField() - tf.placeholder = "팝업스토어 주소를 입력해 주세요." - tf.font = UIFont.systemFont(ofSize: 14) - tf.borderStyle = .roundedRect - return tf - }() - - let latTextField: UITextField = { - let tf = UITextField() - tf.placeholder = "위도" - tf.font = UIFont.systemFont(ofSize: 14) - tf.textAlignment = .center - tf.borderStyle = .roundedRect - return tf - }() + let addressField = UITextField().then { + $0.placeholder = "팝업스토어 주소를 입력해 주세요." + $0.font = UIFont.systemFont(ofSize: 14) + $0.textColor = .darkGray + $0.borderStyle = .none + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.setLeftPaddingPoints(8) + } - let lonTextField: UITextField = { - let tf = UITextField() - tf.placeholder = "경도" - tf.font = UIFont.systemFont(ofSize: 14) - tf.textAlignment = .center - tf.borderStyle = .roundedRect - return tf - }() + let latField = UITextField().then { + $0.placeholder = "" + $0.font = UIFont.systemFont(ofSize: 14) + $0.textColor = .darkGray + $0.borderStyle = .none + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.textAlignment = .center + $0.setLeftPaddingPoints(8) + } - let descriptionTextView: UITextView = { - let tv = UITextView() - tv.font = UIFont.systemFont(ofSize: 14) - tv.layer.cornerRadius = 8 - tv.layer.borderWidth = 1 - tv.layer.borderColor = UIColor.lightGray.cgColor - tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) - return tv - }() + let lonField = UITextField().then { + $0.placeholder = "" + $0.font = UIFont.systemFont(ofSize: 14) + $0.textColor = .darkGray + $0.borderStyle = .none + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.textAlignment = .center + $0.setLeftPaddingPoints(8) + } - // 이미지 관련 영역 - let addImageButton: UIButton = { + let periodButton: UIButton = { let btn = UIButton(type: .system) - btn.setTitle("이미지 추가", for: .normal) - btn.setTitleColor(.systemBlue, for: .normal) + btn.setTitle("기간 선택 ▾", for: .normal) + btn.setTitleColor(.darkGray, for: .normal) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) + btn.layer.cornerRadius = 8 + btn.layer.borderWidth = 1 + btn.layer.borderColor = UIColor.lightGray.cgColor + btn.contentHorizontalAlignment = .left + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() - let removeAllButton: UIButton = { + let timeButton: UIButton = { let btn = UIButton(type: .system) - btn.setTitle("전체 삭제", for: .normal) - btn.setTitleColor(.red, for: .normal) + btn.setTitle("시간 선택 ▾", for: .normal) + btn.setTitleColor(.darkGray, for: .normal) + btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) + btn.layer.cornerRadius = 8 + btn.layer.borderWidth = 1 + btn.layer.borderColor = UIColor.lightGray.cgColor + btn.contentHorizontalAlignment = .left + btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) return btn }() - lazy var imagesCollectionView: UICollectionView = { - let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .horizontal - layout.itemSize = CGSize(width: 80, height: 120) - layout.minimumLineSpacing = 8 - let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) - cv.backgroundColor = .clear - // 셀 등록 등은 실제 프로젝트에 맞게 설정합니다. - cv.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "ImageCell") - return cv - }() + let descriptionTextView = UITextView().then { + $0.font = UIFont.systemFont(ofSize: 14) + $0.textColor = .darkGray + $0.layer.cornerRadius = 8 + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.lightGray.cgColor + $0.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) + $0.isScrollEnabled = true + } // 저장 버튼 let saveButton: UIButton = { @@ -139,127 +196,333 @@ final class PopUpStoreRegisterView: UIView { return btn }() - // 기타 UI 요소는 필요에 따라 추가하세요. + // MARK: - init + init() { + super.init(frame: .zero) - // MARK: - Initializer - override init(frame: CGRect) { - super.init(frame: frame) - backgroundColor = UIColor(white: 0.95, alpha: 1) - setupLayout() + self.addViews() + self.setupContstraints() + self.configureUI() } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension PopUpRegisterView { + func addViews() { + // 네비게이션 영역 + self.addSubview(self.navigationContainer) + self.navigationContainer.addSubview(self.logoImageView) + self.navigationContainer.addSubview(self.accountIdLabel) + self.navigationContainer.addSubview(self.menuButton) + + // 타이틀 영역 + self.addSubview(self.titleContainer) + self.titleContainer.addSubview(self.backButton) + self.titleContainer.addSubview(self.pageTitleLabel) + + // 스크롤 영역 + self.addSubview(self.scrollView) + self.scrollView.addSubview(self.contentView) + + // 이미지 영역 + let buttonStack = UIStackView(arrangedSubviews: [self.addImageButton, self.removeAllButton]) + buttonStack.axis = .horizontal + buttonStack.distribution = .fillEqually + buttonStack.spacing = 16 + + self.contentView.addSubview(buttonStack) + self.contentView.addSubview(self.imagesCollectionView) + + // 폼 영역 + self.contentView.addSubview(self.formBackgroundView) + self.formBackgroundView.addSubview(self.verticalStack) + + // 저장 버튼 + self.addSubview(self.saveButton) } - // MARK: - Layout Setup - private func setupLayout() { - // 예시로 상단 네비게이션과 입력폼 일부만 배치합니다. - let navContainer = UIView() - addSubview(navContainer) - navContainer.snp.makeConstraints { make in - make.top.equalTo(self.safeAreaLayoutGuide.snp.top) + func setupContstraints() { + // 네비게이션 영역 + self.navigationContainer.snp.makeConstraints { make in + make.top.equalTo(self.safeAreaLayoutGuide) make.left.right.equalToSuperview() - make.height.equalTo(44) + make.height.equalTo(Constant.navigationHeight) } - navContainer.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in + self.logoImageView.snp.makeConstraints { make in make.left.equalToSuperview().offset(8) make.centerY.equalToSuperview() - make.width.equalTo(22) - make.height.equalTo(35) + make.width.equalTo(Constant.logoWidth) + make.height.equalTo(Constant.logoHeight) } - navContainer.addSubview(accountIdLabel) - accountIdLabel.snp.makeConstraints { make in + self.accountIdLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.left.equalTo(logoImageView.snp.right).offset(8) + make.left.equalTo(self.logoImageView.snp.right).offset(8) } - navContainer.addSubview(menuButton) - menuButton.snp.makeConstraints { make in + self.menuButton.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.right.equalToSuperview().inset(16) - make.width.height.equalTo(32) + make.right.equalToSuperview().inset(Constant.edgeInset) + make.width.height.equalTo(Constant.buttonSize) } // 타이틀 영역 - let titleContainer = UIView() - addSubview(titleContainer) - titleContainer.snp.makeConstraints { make in - make.top.equalTo(navContainer.snp.bottom) + self.titleContainer.snp.makeConstraints { make in + make.top.equalTo(self.navigationContainer.snp.bottom) make.left.right.equalToSuperview() - make.height.equalTo(44) + make.height.equalTo(Constant.navigationHeight) } - titleContainer.addSubview(backButton) - backButton.snp.makeConstraints { make in + self.backButton.snp.makeConstraints { make in make.centerY.equalToSuperview() make.left.equalToSuperview().offset(8) make.width.height.equalTo(32) } - titleContainer.addSubview(pageTitleLabel) - pageTitleLabel.snp.makeConstraints { make in + self.pageTitleLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() - make.left.equalTo(backButton.snp.right).offset(4) + make.left.equalTo(self.backButton.snp.right).offset(4) } - // 입력폼 영역 (예시) - let formStack = UIStackView(arrangedSubviews: [nameTextField, categoryButton, addressTextField]) - formStack.axis = .vertical - formStack.spacing = 16 - addSubview(formStack) - formStack.snp.makeConstraints { make in - make.top.equalTo(titleContainer.snp.bottom).offset(16) - make.left.right.equalToSuperview().inset(16) + // 스크롤 영역 + self.scrollView.snp.makeConstraints { make in + make.top.equalTo(self.titleContainer.snp.bottom) + make.left.right.equalToSuperview() + make.bottom.equalTo(self.safeAreaLayoutGuide).offset(-74) } - // 위/경도는 수평 스택 - let latLonStack = UIStackView(arrangedSubviews: [latTextField, lonTextField]) - latLonStack.axis = .horizontal - latLonStack.spacing = 16 - latLonStack.distribution = .fillEqually - addSubview(latLonStack) - latLonStack.snp.makeConstraints { make in - make.top.equalTo(formStack.snp.bottom).offset(16) - make.left.right.equalToSuperview().inset(16) + self.contentView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.width.equalTo(self.scrollView.snp.width) } - // 설명 텍스트뷰 - addSubview(descriptionTextView) - descriptionTextView.snp.makeConstraints { make in - make.top.equalTo(latLonStack.snp.bottom).offset(16) + // 이미지 영역 + let buttonStack = self.contentView.subviews.first as! UIStackView + buttonStack.snp.makeConstraints { make in + make.top.equalToSuperview().offset(16) make.left.right.equalToSuperview().inset(16) - make.height.equalTo(120) + make.height.equalTo(40) } - // 이미지 영역 (Add/Remove 버튼과 CollectionView) - let imageButtonStack = UIStackView(arrangedSubviews: [addImageButton, removeAllButton]) - imageButtonStack.axis = .horizontal - imageButtonStack.distribution = .fillEqually - imageButtonStack.spacing = 16 - addSubview(imageButtonStack) - imageButtonStack.snp.makeConstraints { make in - make.top.equalTo(descriptionTextView.snp.bottom).offset(16) + self.imagesCollectionView.snp.makeConstraints { make in + make.top.equalTo(buttonStack.snp.bottom).offset(8) make.left.right.equalToSuperview().inset(16) - make.height.equalTo(40) + make.height.equalTo(130) } - addSubview(imagesCollectionView) - imagesCollectionView.snp.makeConstraints { make in - make.top.equalTo(imageButtonStack.snp.bottom).offset(8) + // 폼 영역 + self.formBackgroundView.snp.makeConstraints { make in + make.top.equalTo(self.imagesCollectionView.snp.bottom).offset(16) make.left.right.equalToSuperview().inset(16) - make.height.equalTo(130) + make.bottom.equalToSuperview().offset(-16) + } + + self.verticalStack.snp.makeConstraints { make in + make.edges.equalToSuperview() } // 저장 버튼 - addSubview(saveButton) - saveButton.snp.makeConstraints { make in + self.saveButton.snp.makeConstraints { make in make.left.right.equalToSuperview().inset(16) - make.bottom.equalTo(self.safeAreaLayoutGuide.snp.bottom).offset(-16) + make.bottom.equalTo(self.safeAreaLayoutGuide).offset(-16) make.height.equalTo(44) } } + + func configureUI() { + self.backgroundColor = UIColor(white: 0.95, alpha: 1) + + // 폼 요소 추가 + self.setupFormRows() + } + + func setupFormRows() { + // 이름 필드 추가 + self.addFormRow(leftTitle: "이름", rightView: self.nameField) + + // 카테고리 버튼 추가 + self.addFormRow(leftTitle: "카테고리", rightView: self.categoryButton) + + // 위치 필드 추가 (주소, 위도, 경도) + let latLabel = self.makePlainLabel("위도") + let lonLabel = self.makePlainLabel("경도") + + let latStack = UIStackView(arrangedSubviews: [latLabel, self.latField]) + latStack.axis = .horizontal + latStack.spacing = 8 + latStack.distribution = .fillProportionally + + let lonStack = UIStackView(arrangedSubviews: [lonLabel, self.lonField]) + lonStack.axis = .horizontal + lonStack.spacing = 8 + lonStack.distribution = .fillProportionally + + let latLonRow = UIStackView(arrangedSubviews: [latStack, lonStack]) + latLonRow.axis = .horizontal + latLonRow.spacing = 16 + latLonRow.distribution = .fillEqually + + let locationVStack = UIStackView(arrangedSubviews: [self.addressField, latLonRow]) + locationVStack.axis = .vertical + locationVStack.spacing = 8 + locationVStack.distribution = .fillEqually + + self.addFormRow(leftTitle: "위치", rightView: locationVStack, rowHeight: nil, totalHeight: 80) + + // 마커 필드 추가 + let markerLabel = self.makePlainLabel("마커명") + let markerField = self.makeRoundedTextField("") + let markerStackH = UIStackView(arrangedSubviews: [markerLabel, markerField]) + markerStackH.axis = .horizontal + markerStackH.spacing = 8 + markerStackH.distribution = .fillProportionally + + let snippetLabel = self.makePlainLabel("스니펫") + let snippetField = self.makeRoundedTextField("") + let snippetStackH = UIStackView(arrangedSubviews: [snippetLabel, snippetField]) + snippetStackH.axis = .horizontal + snippetStackH.spacing = 8 + snippetStackH.distribution = .fillProportionally + + let markerVStack = UIStackView(arrangedSubviews: [markerStackH, snippetStackH]) + markerVStack.axis = .vertical + markerVStack.spacing = 8 + markerVStack.distribution = .fillEqually + + self.addFormRow(leftTitle: "마커", rightView: markerVStack, rowHeight: nil, totalHeight: 80) + + // 기간 및 시간 + self.addFormRow(leftTitle: "기간", rightView: self.periodButton) + self.addFormRow(leftTitle: "시간", rightView: self.timeButton) + + // 작성자 및 작성시간 + let currentTime = self.getCurrentFormattedTime() + let writerLbl = self.makeSimpleLabel("") + let timeLbl = self.makeSimpleLabel(currentTime) + + self.addFormRow(leftTitle: "작성자", rightView: writerLbl) + self.addFormRow(leftTitle: "작성시간", rightView: timeLbl) + + // 상태값 + let statusLbl = self.makeSimpleLabel("진행") + self.addFormRow(leftTitle: "상태값", rightView: statusLbl) + + // 설명 + self.addFormRow(leftTitle: "설명", rightView: self.descriptionTextView, rowHeight: nil, totalHeight: 120) + } + + // 폼 행 추가 헬퍼 메서드 + func addFormRow(leftTitle: String, rightView: UIView, rowHeight: CGFloat? = 36, totalHeight: CGFloat? = nil) { + let row = UIView() + row.backgroundColor = .white + + let leftBG = UIView() + leftBG.backgroundColor = UIColor(white: 0.94, alpha: 1) + row.addSubview(leftBG) + leftBG.snp.makeConstraints { make in + make.top.bottom.left.equalToSuperview() + make.width.equalTo(80) + } + + let leftLabel = UILabel() + leftLabel.text = leftTitle + leftLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold) + leftLabel.textColor = .black + leftLabel.textAlignment = .center + leftBG.addSubview(leftLabel) + leftLabel.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.left.right.equalToSuperview().inset(8) + } + + let rightBG = UIView() + rightBG.backgroundColor = .white + row.addSubview(rightBG) + rightBG.snp.makeConstraints { make in + make.top.bottom.right.equalToSuperview() + make.left.equalTo(leftBG.snp.right) + } + + rightBG.addSubview(rightView) + rightView.snp.makeConstraints { make in + make.top.equalToSuperview().offset(7) + make.bottom.equalToSuperview().offset(-7) + make.left.equalToSuperview().offset(8) + make.right.equalToSuperview().offset(-8) + if let fixH = rowHeight { + make.height.equalTo(fixH).priority(.medium) + } + } + + if let totalH = totalHeight { + row.snp.makeConstraints { make in + make.height.equalTo(totalH).priority(.high) + } + } else { + row.snp.makeConstraints { make in + make.height.greaterThanOrEqualTo(41) + } + } + + let separator = UIView() + separator.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) + row.addSubview(separator) + separator.snp.makeConstraints { make in + make.left.right.bottom.equalToSuperview() + make.height.equalTo(1) + } + + self.verticalStack.addArrangedSubview(row) + } + + // 유틸리티 메서드 + func makeRoundedTextField(_ placeholder: String) -> UITextField { + let tf = UITextField() + tf.placeholder = placeholder + tf.font = UIFont.systemFont(ofSize: 14) + tf.textColor = .darkGray + tf.borderStyle = .none + tf.layer.cornerRadius = 8 + tf.layer.borderWidth = 1 + tf.layer.borderColor = UIColor.lightGray.cgColor + tf.setLeftPaddingPoints(8) + return tf + } + + func makePlainLabel(_ text: String) -> UILabel { + let lbl = UILabel() + lbl.text = text + lbl.font = UIFont.systemFont(ofSize: 14) + lbl.textColor = .darkGray + lbl.textAlignment = .right + lbl.setContentHuggingPriority(.required, for: .horizontal) + return lbl + } + + func makeSimpleLabel(_ text: String) -> UILabel { + let lbl = UILabel() + lbl.text = text + lbl.font = UIFont.systemFont(ofSize: 14) + lbl.textColor = .darkGray + return lbl + } + + func getCurrentFormattedTime() -> String { + let formatter = DateFormatter() + formatter.dateFormat = "yyyy.MM.dd HH:mm" + formatter.timeZone = TimeZone(identifier: "Asia/Seoul") + return formatter.string(from: Date()) + } +} +extension UITextField { + func setLeftPaddingPoints(_ amount: CGFloat) { + let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) + leftView = paddingView + leftViewMode = .always + } } diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index ac9dd346..ff17f694 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1,177 +1,28 @@ +import UIKit import CoreLocation import PhotosUI -import UIKit -import Alamofire -import ReactorKit +import SnapKit import RxCocoa import RxSwift -import SnapKit -import Then +import ReactorKit -final class PopUpStoreRegisterViewController: BaseViewController, View { - typealias Reactor = PopUpStoreRegisterReactor +final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Properties - var disposeBag = DisposeBag() private var pickerViewController: PHPickerViewController? private let adminUseCase: AdminUseCase - private var nameField: UITextField? - private var addressField: UITextField? - private var latField: UITextField? - private var lonField: UITextField? - private var descTV: UITextView? - var completionHandler: (() -> Void)? private let nickname: String + var completionHandler: (() -> Void)? + var disposeBag = DisposeBag() - // MARK: - UI Components - private let navContainer = UIView() - - private lazy var imagesCollectionView: UICollectionView = { - let layout = UICollectionViewFlowLayout() - layout.scrollDirection = .horizontal - layout.itemSize = CGSize(width: 80, height: 120) - layout.minimumLineSpacing = 8 - - let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) - cv.backgroundColor = .clear - cv.register(ImageCell.self, forCellWithReuseIdentifier: ImageCell.identifier) - cv.dataSource = self - cv.delegate = self - return cv - }() - - private let logoImageView: UIImageView = { - let iv = UIImageView() - iv.image = UIImage(named: "image_login_logo") - iv.contentMode = .scaleAspectFit - return iv - }() - - private let accountIdLabel: UILabel = { - let lbl = UILabel() - lbl.font = UIFont.systemFont(ofSize: 14, weight: .semibold) - lbl.textColor = .black - return lbl - }() - - private let menuButton: UIButton = { - let btn = UIButton(type: .system) - btn.setImage(UIImage(systemName: "adminlist"), for: .normal) - btn.tintColor = .black - return btn - }() - - private let titleContainer = UIView() - private let backButton: UIButton = { - let btn = UIButton(type: .system) - btn.setImage(UIImage(systemName: "chevron.left"), for: .normal) - btn.tintColor = .black - return btn - }() - - private let pageTitleLabel: UILabel = { - let lbl = UILabel() - lbl.text = "팝업스토어 등록" - lbl.font = UIFont.boldSystemFont(ofSize: 18) - lbl.textColor = .black - return lbl - }() - - private let addImageButton = UIButton(type: .system).then { - $0.setTitle("이미지 추가", for: .normal) - $0.setTitleColor(.systemBlue, for: .normal) - } - - private let removeAllButton = UIButton(type: .system).then { - $0.setTitle("전체 삭제", for: .normal) - $0.setTitleColor(.red, for: .normal) - } - - private let scrollView = UIScrollView() - private let contentView = UIView() - - private let formBackgroundView = UIView().then { - $0.backgroundColor = .white - $0.layer.borderWidth = 1 - $0.layer.borderColor = UIColor.lightGray.cgColor - $0.layer.cornerRadius = 8 - } - - private let verticalStack = UIStackView() - - private let saveButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("저장", for: .normal) - btn.setTitleColor(.white, for: .normal) - btn.backgroundColor = .lightGray - btn.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold) - btn.layer.cornerRadius = 8 - btn.isEnabled = false - return btn - }() - - private let categoryButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("카테고리 선택 ▾", for: .normal) - btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) - btn.layer.cornerRadius = 8 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.lightGray.cgColor - btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) - return btn - }() - - private let periodButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("기간 선택 ▾", for: .normal) - btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) - btn.layer.cornerRadius = 8 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.lightGray.cgColor - btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) - return btn - }() - - private let timeButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("시간 선택 ▾", for: .normal) - btn.setTitleColor(.darkGray, for: .normal) - btn.titleLabel?.font = UIFont.systemFont(ofSize: 14) - btn.layer.cornerRadius = 8 - btn.layer.borderWidth = 1 - btn.layer.borderColor = UIColor.lightGray.cgColor - btn.contentHorizontalAlignment = .left - btn.contentEdgeInsets = UIEdgeInsets(top: 7, left: 8, bottom: 7, right: 8) - return btn - }() - private func extractDateRange(from state: Reactor.State) -> (Date, Date)? { - guard let start = state.selectedStartDate, - let end = state.selectedEndDate else { - return nil - } - return (start, end) - } + private var mainView: PopUpRegisterView - private func extractTimeRange(from state: Reactor.State) -> (Date, Date)? { - guard let start = state.selectedStartTime, - let end = state.selectedEndTime else { - return nil - } - return (start, end) - } - - private func areDateRangesEqual(_ prev: (Date, Date), _ current: (Date, Date)) -> Bool { - return prev.0 == current.0 && prev.1 == current.1 - } // MARK: - Initializer init(nickname: String, adminUseCase: AdminUseCase, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { self.nickname = nickname self.adminUseCase = adminUseCase + self.mainView = PopUpRegisterView() super.init() @@ -183,10 +34,10 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { ) self.reactor = reactor - self.accountIdLabel.text = nickname + "님" + self.mainView.accountIdLabel.text = nickname + "님" if editingStore != nil { - pageTitleLabel.text = "팝업스토어 수정" + self.mainView.pageTitleLabel.text = "팝업스토어 수정" // 편집 모드일 경우 스토어 상세 정보 로드 if let storeId = editingStore?.id { @@ -196,600 +47,127 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + fatalError("\(#file), \(#function) Error") } +} - // MARK: - Lifecycle +// MARK: - Life Cycle +extension PopUpStoreRegisterViewController { override func viewDidLoad() { super.viewDidLoad() - let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handleTap)) - tapGesture.cancelsTouchesInView = false - view.addGestureRecognizer(tapGesture) - - view.backgroundColor = UIColor(white: 0.95, alpha: 1) + self.addViews() + self.setupContstraints() + self.configureUI() + self.setupHandlers() - setupNavigation() - setupLayout() - setupRows() - setupImageCollectionUI() - setupKeyboardHandling() + if let reactor = self.reactor as? PopUpStoreRegisterReactor { + self.bind(reactor: reactor) + } } +} - // MARK: - ReactorKit Binding - func bind(reactor: Reactor) { - - // 텍스트 필드 바인딩 - nameField?.rx.text.orEmpty - .distinctUntilChanged() - .skip(1) - .map { Reactor.Action.updateName($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - addressField?.rx.text.orEmpty - .distinctUntilChanged() - .skip(1) - .map { Reactor.Action.updateAddress($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - latField?.rx.text.orEmpty - .distinctUntilChanged() - .skip(1) - .map { Reactor.Action.updateLat($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) +// MARK: - Setup +private extension PopUpStoreRegisterViewController { + func addViews() { + self.view.addSubview(self.mainView) + } - lonField?.rx.text.orEmpty - .distinctUntilChanged() - .skip(1) - .map { Reactor.Action.updateLon($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) + func setupContstraints() { + self.mainView.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } - descTV?.rx.text.orEmpty - .distinctUntilChanged() - .skip(1) - .map { Reactor.Action.updateDescription($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) + func configureUI() { + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(self.handleTap)) + tapGesture.cancelsTouchesInView = false + self.view.addGestureRecognizer(tapGesture) + } - // 주소 변경 시 지오코딩 요청 - addressField?.rx.text.orEmpty - .distinctUntilChanged() - .debounce(.milliseconds(500), scheduler: MainScheduler.instance) - .filter { !$0.isEmpty } - .map { Reactor.Action.geocodeAddress($0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) + func setupHandlers() { + // 뒤로가기 버튼 + self.mainView.backButton.addTarget(self, action: #selector(self.onBack), for: .touchUpInside) - // 이미지 버튼 바인딩 - addImageButton.rx.tap + // 이미지 관련 버튼 + self.mainView.addImageButton.rx.tap .bind { [weak self] in self?.showImagePicker() } - .disposed(by: disposeBag) - - removeAllButton.rx.tap - .map { Reactor.Action.removeAllImages } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - // 다양한 버튼 바인딩 - categoryButton.rx.tap + .disposed(by: self.disposeBag) + self.mainView.removeAllButton.rx.tap + .map { PopUpStoreRegisterReactor.Action.removeAllImages } + .subscribe(onNext: { [weak self] action in + (self?.reactor as? PopUpStoreRegisterReactor)?.action.onNext(action) + }) + .disposed(by: self.disposeBag) + + self.mainView.categoryButton.rx.tap .bind { [weak self] in self?.showCategoryPicker() } - .disposed(by: disposeBag) + .disposed(by: self.disposeBag) - periodButton.rx.tap + self.mainView.periodButton.rx.tap .bind { [weak self] in self?.showDateRangePicker() } - .disposed(by: disposeBag) + .disposed(by: self.disposeBag) - timeButton.rx.tap + self.mainView.timeButton.rx.tap .bind { [weak self] in self?.showTimeRangePicker() } - .disposed(by: disposeBag) - - // 저장 버튼 - saveButton.rx.tap - .map { Reactor.Action.save } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - // Outputs (Reactor -> View) - - // 저장 버튼 활성화 상태 - reactor.state.map { $0.isSaveEnabled } - .distinctUntilChanged() - .bind(onNext: { [weak self] isEnabled in - self?.saveButton.isEnabled = isEnabled - self?.saveButton.backgroundColor = isEnabled ? .systemBlue : .lightGray - }) - .disposed(by: disposeBag) - - // 로딩 상태 - reactor.state.map { $0.isLoading } - .distinctUntilChanged() - .bind(onNext: { [weak self] isLoading in - if isLoading { - // 로딩 인디케이터 표시 - self?.showLoadingIndicator() - } else { - // 로딩 인디케이터 숨김 - self?.hideLoadingIndicator() - } - }) - .disposed(by: disposeBag) + .disposed(by: self.disposeBag) - // 에러 메시지 - reactor.state.map { $0.errorMessage } - .distinctUntilChanged() - .compactMap { $0 } - .bind(onNext: { [weak self] message in - self?.showErrorAlert(message: message) - reactor.action.onNext(.clearError) - }) - .disposed(by: disposeBag) - - // 성공 상태 - reactor.state.map { $0.isSuccess } - .distinctUntilChanged() - .filter { $0 } - .bind(onNext: { [weak self] _ in - self?.showSuccessAlert(isUpdate: reactor.currentState.isEditMode) - reactor.action.onNext(.dismissSuccess) - }) - .disposed(by: disposeBag) - - // 이미지 목록 업데이트 - reactor.state.map { $0.images } - .distinctUntilChanged() - .bind(onNext: { [weak self] _ in - self?.imagesCollectionView.reloadData() - }) - .disposed(by: disposeBag) - - // 필드 값 업데이트 - reactor.state.map { $0.name } - .distinctUntilChanged() - .bind(onNext: { [weak self] name in - if self?.nameField?.text != name { - self?.nameField?.text = name - } - }) - .disposed(by: disposeBag) - - reactor.state.map { $0.address } - .distinctUntilChanged() - .bind(onNext: { [weak self] address in - if self?.addressField?.text != address { - self?.addressField?.text = address - } - }) - .disposed(by: disposeBag) - - reactor.state.map { $0.lat } - .distinctUntilChanged() - .bind(onNext: { [weak self] lat in - if self?.latField?.text != lat { - self?.latField?.text = lat - } - }) - .disposed(by: disposeBag) - - reactor.state.map { $0.lon } - .distinctUntilChanged() - .bind(onNext: { [weak self] lon in - if self?.lonField?.text != lon { - self?.lonField?.text = lon - } - }) - .disposed(by: disposeBag) - - reactor.state.map { $0.description } - .distinctUntilChanged() - .bind(onNext: { [weak self] description in - if self?.descTV?.text != description { - self?.descTV?.text = description - } - }) - .disposed(by: disposeBag) - - reactor.state.map { $0.category } - .distinctUntilChanged() - .filter { !$0.isEmpty } - .bind(onNext: { [weak self] category in - self?.updateCategoryButtonTitle(with: category) - }) - .disposed(by: disposeBag) - - // 날짜 범위 업데이트 - reactor.state - .compactMap { [weak self] state in - self?.extractDateRange(from: state) - } - .distinctUntilChanged(areDateRangesEqual) - .bind(onNext: { [weak self] dateRange in - self?.updatePeriodButtonTitle(start: dateRange.0, end: dateRange.1) - }) - .disposed(by: disposeBag) - - // 시간 범위 업데이트 - reactor.state - .compactMap { [weak self] state in - self?.extractTimeRange(from: state) - } - .distinctUntilChanged(areDateRangesEqual) - .bind(onNext: { [weak self] timeRange in - self?.updateTimeButtonTitle(start: timeRange.0, end: timeRange.1) - }) - .disposed(by: disposeBag) - } - // MARK: - UI Setup - private func setupNavigation() { - backButton.addTarget(self, action: #selector(onBack), for: .touchUpInside) - } - - private func setupLayout() { - // 상단 컨테이너 - view.addSubview(navContainer) - navContainer.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide) - make.left.right.equalToSuperview() - make.height.equalTo(44) - } - - navContainer.addSubview(logoImageView) - logoImageView.snp.makeConstraints { make in - make.left.equalToSuperview().offset(8) - make.centerY.equalToSuperview() - make.width.equalTo(22) - make.height.equalTo(35) - } - - navContainer.addSubview(accountIdLabel) - accountIdLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.equalTo(logoImageView.snp.right).offset(8) - } - - navContainer.addSubview(menuButton) - menuButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.right.equalToSuperview().inset(16) - make.width.height.equalTo(32) - } - - // 타이틀 컨테이너 - view.addSubview(titleContainer) - titleContainer.snp.makeConstraints { make in - make.top.equalTo(navContainer.snp.bottom) - make.left.right.equalToSuperview() - make.height.equalTo(44) - } - - titleContainer.addSubview(backButton) - backButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.equalToSuperview().offset(8) - make.width.height.equalTo(32) + // 이미지 컬렉션뷰 핸들러 + self.mainView.imagesCollectionView.onMainImageToggled = { [weak self] index in + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.toggleMainImage(index)) } - titleContainer.addSubview(pageTitleLabel) - pageTitleLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.equalTo(backButton.snp.right).offset(4) - } - - // 스크롤뷰 - view.addSubview(scrollView) - scrollView.snp.makeConstraints { make in - make.top.equalTo(titleContainer.snp.bottom) - make.left.right.equalToSuperview() - make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-74) - } - - scrollView.addSubview(contentView) - contentView.snp.makeConstraints { make in - make.edges.equalToSuperview() - make.width.equalTo(scrollView.snp.width) + self.mainView.imagesCollectionView.onImageDeleted = { [weak self] index in + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.removeImage(index)) } // 저장 버튼 - view.addSubview(saveButton) - saveButton.snp.makeConstraints { make in - make.left.right.equalToSuperview().inset(16) - make.bottom.equalTo(view.safeAreaLayoutGuide).offset(-16) - make.height.equalTo(44) - } - } - - private func setupImageCollectionUI() { - // 버튼 스택 - let buttonStack = UIStackView(arrangedSubviews: [addImageButton, removeAllButton]) - buttonStack.axis = .horizontal - buttonStack.distribution = .fillEqually - buttonStack.spacing = 16 - - contentView.addSubview(buttonStack) - buttonStack.snp.makeConstraints { make in - make.top.equalToSuperview().offset(16) - make.left.right.equalToSuperview().inset(16) - make.height.equalTo(40) - } - - // 이미지 컬렉션 뷰 - contentView.addSubview(imagesCollectionView) - imagesCollectionView.snp.makeConstraints { make in - make.top.equalTo(buttonStack.snp.bottom).offset(8) - make.left.right.equalToSuperview().inset(16) - make.height.equalTo(130) - } - - // 폼 배경 - contentView.addSubview(formBackgroundView) - formBackgroundView.snp.makeConstraints { make in - make.top.equalTo(imagesCollectionView.snp.bottom).offset(16) - make.left.right.equalToSuperview().inset(16) - make.bottom.equalToSuperview().offset(-16) - } - - // 수직 스택 - formBackgroundView.addSubview(verticalStack) - verticalStack.axis = .vertical - verticalStack.spacing = 0 - verticalStack.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } + self.mainView.saveButton.rx.tap + .map { PopUpStoreRegisterReactor.Action.save } + .subscribe(onNext: { [weak self] action in + (self?.reactor as? PopUpStoreRegisterReactor)?.action.onNext(action) + }) + .disposed(by: self.disposeBag) - private func setupRows() { - // 이름 필드 추가 - addRowTextField(leftTitle: "이름", placeholder: "팝업스토어 이름을 입력해 주세요.") - - // 카테고리 버튼 추가 - addRowCustom(leftTitle: "카테고리", rightView: categoryButton) - - // 위치 필드 추가 (주소, 위도, 경도) - let addressField = makeRoundedTextField("팝업스토어 주소를 입력해 주세요.") - self.addressField = addressField - - let latLabel = makePlainLabel("위도") - let latField = makeRoundedTextField("") - latField.textAlignment = .center - self.latField = latField - - let lonLabel = makePlainLabel("경도") - let lonField = makeRoundedTextField("") - lonField.textAlignment = .center - self.lonField = lonField - - let latStack = UIStackView(arrangedSubviews: [latLabel, latField]) - latStack.axis = .horizontal - latStack.spacing = 8 - latStack.distribution = .fillProportionally - - let lonStack = UIStackView(arrangedSubviews: [lonLabel, lonField]) - lonStack.axis = .horizontal - lonStack.spacing = 8 - lonStack.distribution = .fillProportionally - - let latLonRow = UIStackView(arrangedSubviews: [latStack, lonStack]) - latLonRow.axis = .horizontal - latLonRow.spacing = 16 - latLonRow.distribution = .fillEqually - - let locationVStack = UIStackView(arrangedSubviews: [addressField, latLonRow]) - locationVStack.axis = .vertical - locationVStack.spacing = 8 - locationVStack.distribution = .fillEqually - - addRowCustom(leftTitle: "위치", rightView: locationVStack, rowHeight: nil, totalHeight: 80) - - // 마커 필드 추가 - let markerLabel = makePlainLabel("마커명") - let markerField = makeRoundedTextField("") - let markerStackH = UIStackView(arrangedSubviews: [markerLabel, markerField]) - markerStackH.axis = .horizontal - markerStackH.spacing = 8 - markerStackH.distribution = .fillProportionally - - let snippetLabel = makePlainLabel("스니펫") - let snippetField = makeRoundedTextField("") - let snippetStackH = UIStackView(arrangedSubviews: [snippetLabel, snippetField]) - snippetStackH.axis = .horizontal - snippetStackH.spacing = 8 - snippetStackH.distribution = .fillProportionally - - let markerVStack = UIStackView(arrangedSubviews: [markerStackH, snippetStackH]) - markerVStack.axis = .vertical - markerVStack.spacing = 8 - markerVStack.distribution = .fillEqually - - addRowCustom(leftTitle: "마커", rightView: markerVStack, rowHeight: nil, totalHeight: 80) - - // 기간 및 시간 - addRowCustom(leftTitle: "기간", rightView: periodButton) - addRowCustom(leftTitle: "시간", rightView: timeButton) - - // 작성자 및 작성시간 - let writerLbl = makeSimpleLabel(nickname) - addRowCustom(leftTitle: "작성자", rightView: writerLbl) - - let timeLbl = setupCreationTimeLabel() - addRowCustom(leftTitle: "작성시간", rightView: timeLbl) - - // 상태값 - let statusLbl = makeSimpleLabel("진행") - addRowCustom(leftTitle: "상태값", rightView: statusLbl) - - // 설명 - let descTV = makeRoundedTextView() - self.descTV = descTV - addRowCustom(leftTitle: "설명", rightView: descTV, rowHeight: nil, totalHeight: 120) + self.setupKeyboardHandling() } - private func setupKeyboardHandling() { + func setupKeyboardHandling() { NotificationCenter.default.addObserver( self, - selector: #selector(keyboardWillShow), + selector: #selector(self.keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil ) NotificationCenter.default.addObserver( self, - selector: #selector(keyboardWillHide), + selector: #selector(self.keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil ) - scrollView.keyboardDismissMode = .interactive - } - - // MARK: - Helper Methods - private func addRowTextField(leftTitle: String, placeholder: String) { - let tf = makeRoundedTextField(placeholder) - if leftTitle == "이름" { - nameField = tf - } else if leftTitle == "주소" { - addressField = tf - } - addRowCustom(leftTitle: leftTitle, rightView: tf) - } - - private func addRowCustom(leftTitle: String, - rightView: UIView, - rowHeight: CGFloat? = 36, - totalHeight: CGFloat? = nil) { - let row = UIView() - row.backgroundColor = .white - - let leftBG = UIView() - leftBG.backgroundColor = UIColor(white: 0.94, alpha: 1) - row.addSubview(leftBG) - leftBG.snp.makeConstraints { make in - make.top.bottom.left.equalToSuperview() - make.width.equalTo(80) - } - - let leftLabel = UILabel() - leftLabel.text = leftTitle - leftLabel.font = UIFont.systemFont(ofSize: 15, weight: .bold) - leftLabel.textColor = .black - leftLabel.textAlignment = .center - leftBG.addSubview(leftLabel) - leftLabel.snp.makeConstraints { make in - make.centerY.equalToSuperview() - make.left.right.equalToSuperview().inset(8) - } - - let rightBG = UIView() - rightBG.backgroundColor = .white - row.addSubview(rightBG) - rightBG.snp.makeConstraints { make in - make.top.bottom.right.equalToSuperview() - make.left.equalTo(leftBG.snp.right) - } - - rightBG.addSubview(rightView) - rightView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(7) - make.bottom.equalToSuperview().offset(-7) - make.left.equalToSuperview().offset(8) - make.right.equalToSuperview().offset(-8) - if let fixH = rowHeight { - make.height.equalTo(fixH).priority(.medium) - } - } - - if let totalH = totalHeight { - row.snp.makeConstraints { make in - make.height.equalTo(totalH).priority(.high) - } - } else { - row.snp.makeConstraints { make in - make.height.greaterThanOrEqualTo(41) - } - } - - let separator = UIView() - separator.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) - row.addSubview(separator) - separator.snp.makeConstraints { make in - make.left.right.bottom.equalToSuperview() - make.height.equalTo(1) - } - - verticalStack.addArrangedSubview(row) - } - - private func getCurrentFormattedTime() -> String { - let formatter = DateFormatter() - formatter.dateFormat = "yyyy.MM.dd HH:mm" - formatter.timeZone = TimeZone(identifier: "Asia/Seoul") - return formatter.string(from: Date()) - } - - private func setupCreationTimeLabel() -> UILabel { - let currentTime = getCurrentFormattedTime() - return makeSimpleLabel(currentTime) - } - - private func makeRoundedTextField(_ placeholder: String) -> UITextField { - let tf = UITextField() - tf.placeholder = placeholder - tf.font = UIFont.systemFont(ofSize: 14) - tf.textColor = .darkGray - tf.borderStyle = .none - tf.layer.cornerRadius = 8 - tf.layer.borderWidth = 1 - tf.layer.borderColor = UIColor.lightGray.cgColor - tf.setLeftPaddingPoints(8) - return tf - } - - private func makeRoundedTextView() -> UITextView { - let tv = UITextView() - tv.font = UIFont.systemFont(ofSize: 14) - tv.textColor = .darkGray - tv.layer.cornerRadius = 8 - tv.layer.borderWidth = 1 - tv.layer.borderColor = UIColor.lightGray.cgColor - tv.textContainerInset = UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7) - tv.isScrollEnabled = true - return tv - } - - private func makePlainLabel(_ text: String) -> UILabel { - let lbl = UILabel() - lbl.text = text - lbl.font = UIFont.systemFont(ofSize: 14) - lbl.textColor = .darkGray - lbl.textAlignment = .right - lbl.setContentHuggingPriority(.required, for: .horizontal) - return lbl - } - - private func makeSimpleLabel(_ text: String) -> UILabel { - let lbl = UILabel() - lbl.text = text - lbl.font = UIFont.systemFont(ofSize: 14) - lbl.textColor = .darkGray - return lbl + self.mainView.scrollView.keyboardDismissMode = .interactive } +} - // MARK: - UI Interaction Methods +// MARK: - UI Interaction Methods +extension PopUpStoreRegisterViewController { @objc private func handleTap() { - view.endEditing(true) + self.view.endEditing(true) } @objc private func onBack() { - navigationController?.popViewController(animated: true) + self.navigationController?.popViewController(animated: true) } @objc private func keyboardWillShow(_ notification: Notification) { @@ -806,28 +184,28 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { right: 0 ) - scrollView.contentInset = contentInset - scrollView.scrollIndicatorInsets = contentInset + self.mainView.scrollView.contentInset = contentInset + self.mainView.scrollView.scrollIndicatorInsets = contentInset // 현재 활성화된 필드가 키보드에 가려지는지 확인 - if let activeField = view.findFirstResponder() { - let activeRect = activeField.convert(activeField.bounds, to: scrollView) + if let activeField = self.view.findFirstResponder() { + let activeRect = activeField.convert(activeField.bounds, to: self.mainView.scrollView) let bottomOffset = activeRect.maxY + 20 // 여유 공간 - if bottomOffset > (scrollView.frame.height - keyboardHeight) { + if bottomOffset > (self.mainView.scrollView.frame.height - keyboardHeight) { let scrollPoint = CGPoint( x: 0, - y: bottomOffset - (scrollView.frame.height - keyboardHeight) + y: bottomOffset - (self.mainView.scrollView.frame.height - keyboardHeight) ) - scrollView.setContentOffset(scrollPoint, animated: true) + self.mainView.scrollView.setContentOffset(scrollPoint, animated: true) } } } @objc private func keyboardWillHide(_ notification: Notification) { UIView.animate(withDuration: 0.3) { - self.scrollView.contentInset = .zero - self.scrollView.scrollIndicatorInsets = .zero + self.mainView.scrollView.contentInset = .zero + self.mainView.scrollView.scrollIndicatorInsets = .zero } } @@ -840,7 +218,7 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { picker.delegate = self self.pickerViewController = picker - present(picker, animated: true, completion: nil) + self.present(picker, animated: true, completion: nil) } private func showCategoryPicker() { @@ -850,7 +228,8 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { for category in categories { let action = UIAlertAction(title: category, style: .default) { [weak self] _ in - self?.reactor?.action.onNext(.selectCategory(category)) + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.selectCategory(category)) } alertController.addAction(action) } @@ -859,45 +238,47 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { alertController.addAction(cancelAction) if let popoverController = alertController.popoverPresentationController { - popoverController.sourceView = categoryButton - popoverController.sourceRect = categoryButton.bounds + popoverController.sourceView = self.mainView.categoryButton + popoverController.sourceRect = self.mainView.categoryButton.bounds } - present(alertController, animated: true, completion: nil) + self.present(alertController, animated: true, completion: nil) } private func showDateRangePicker() { DateTimePickerManager.shared.showDateRange(on: self) { [weak self] start, end in - self?.reactor?.action.onNext(.selectDateRange(start: start, end: end)) + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.selectDateRange(start: start, end: end)) } } private func showTimeRangePicker() { DateTimePickerManager.shared.showTimeRange(on: self) { [weak self] start, end in - self?.reactor?.action.onNext(.selectTimeRange(start: start, end: end)) + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.selectTimeRange(start: start, end: end)) } } private func updateCategoryButtonTitle(with category: String) { - categoryButton.setTitle("\(category) ▾", for: .normal) + self.mainView.categoryButton.setTitle("\(category) ▾", for: .normal) } private func updatePeriodButtonTitle(start: Date, end: Date) { - let df = DateFormatter() - df.dateFormat = "yyyy.MM.dd" - let sStr = df.string(from: start) - let eStr = df.string(from: end) + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy.MM.dd" + let startString = dateFormatter.string(from: start) + let endString = dateFormatter.string(from: end) - periodButton.setTitle("\(sStr) ~ \(eStr)", for: .normal) + self.mainView.periodButton.setTitle("\(startString) ~ \(endString)", for: .normal) } private func updateTimeButtonTitle(start: Date, end: Date) { - let df = DateFormatter() - df.dateFormat = "HH:mm" - let stStr = df.string(from: start) - let etStr = df.string(from: end) + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "HH:mm" + let startString = dateFormatter.string(from: start) + let endString = dateFormatter.string(from: end) - timeButton.setTitle("\(stStr) ~ \(etStr)", for: .normal) + self.mainView.timeButton.setTitle("\(startString) ~ \(endString)", for: .normal) } private func showLoadingIndicator() { @@ -920,7 +301,7 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { self?.completionHandler?() // 목록 새로고침 self?.navigationController?.popViewController(animated: true) }) - present(alert, animated: true) + self.present(alert, animated: true) } private func showErrorAlert(message: String) { @@ -930,39 +311,199 @@ final class PopUpStoreRegisterViewController: BaseViewController, View { preferredStyle: .alert ) alert.addAction(UIAlertAction(title: "확인", style: .default)) - present(alert, animated: true) + self.present(alert, animated: true) } } -// MARK: - UICollectionView DataSource & Delegate -extension PopUpStoreRegisterViewController: UICollectionViewDataSource, UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return reactor?.currentState.images.count ?? 0 - } - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: ImageCell.identifier, - for: indexPath - ) as? ImageCell, - let images = reactor?.currentState.images, - indexPath.item < images.count else { - return UICollectionViewCell() - } +// MARK: - ReactorKit Binding +extension PopUpStoreRegisterViewController: View { + typealias Reactor = PopUpStoreRegisterReactor - let item = images[indexPath.item] - cell.configure(with: item) - // 대표이미지 변경 - cell.onMainCheckToggled = { [weak self] in - self?.reactor?.action.onNext(.toggleMainImage(indexPath.item)) - } + func bind(reactor: Reactor) { + // MARK: - Input (View -> Reactor) + // 텍스트 필드 바인딩 + self.mainView.nameField.rx.text.orEmpty + .distinctUntilChanged() + .map { Reactor.Action.updateName($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) - // 개별 삭제 - cell.onDeleteTapped = { [weak self] in - self?.reactor?.action.onNext(.removeImage(indexPath.item)) - } + self.mainView.addressField.rx.text.orEmpty + .distinctUntilChanged() + .map { Reactor.Action.updateAddress($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + self.mainView.latField.rx.text.orEmpty + .distinctUntilChanged() + .map { Reactor.Action.updateLat($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + self.mainView.lonField.rx.text.orEmpty + .distinctUntilChanged() + .map { Reactor.Action.updateLon($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + self.mainView.descriptionTextView.rx.text.orEmpty + .distinctUntilChanged() + .map { Reactor.Action.updateDescription($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + // 주소 변경 시 지오코딩 요청 + self.mainView.addressField.rx.text.orEmpty + .distinctUntilChanged() + .debounce(.milliseconds(500), scheduler: MainScheduler.instance) + .filter { !$0.isEmpty } + .map { Reactor.Action.geocodeAddress($0) } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + + // MARK: - Output (Reactor -> View) + // 저장 버튼 활성화 상태 + reactor.state.map { $0.isSaveEnabled } + .distinctUntilChanged() + .bind(onNext: { [weak self] isEnabled in + self?.mainView.saveButton.isEnabled = isEnabled + self?.mainView.saveButton.backgroundColor = isEnabled ? .systemBlue : .lightGray + }) + .disposed(by: self.disposeBag) + + // 로딩 상태 + reactor.state.map { $0.isLoading } + .distinctUntilChanged() + .bind(onNext: { [weak self] isLoading in + if isLoading { + self?.showLoadingIndicator() + } else { + self?.hideLoadingIndicator() + } + }) + .disposed(by: self.disposeBag) + + // 에러 메시지 + reactor.state.map { $0.errorMessage } + .distinctUntilChanged() + .compactMap { $0 } + .bind(onNext: { [weak self] message in + self?.showErrorAlert(message: message) + reactor.action.onNext(.clearError) + }) + .disposed(by: self.disposeBag) + + // 성공 상태 + reactor.state.map { $0.isSuccess } + .distinctUntilChanged() + .filter { $0 } + .bind(onNext: { [weak self] _ in + self?.showSuccessAlert(isUpdate: reactor.currentState.isEditMode) + reactor.action.onNext(.dismissSuccess) + }) + .disposed(by: self.disposeBag) + + // 이미지 목록 업데이트 + reactor.state.map { $0.images } + .distinctUntilChanged() + .bind(onNext: { [weak self] images in + self?.mainView.imagesCollectionView.updateImages(images) + }) + .disposed(by: self.disposeBag) + + // 필드 값 업데이트 + reactor.state.map { $0.name } + .distinctUntilChanged() + .bind(onNext: { [weak self] name in + if self?.mainView.nameField.text != name { + self?.mainView.nameField.text = name + } + }) + .disposed(by: self.disposeBag) + + reactor.state.map { $0.address } + .distinctUntilChanged() + .bind(onNext: { [weak self] address in + if self?.mainView.addressField.text != address { + self?.mainView.addressField.text = address + } + }) + .disposed(by: self.disposeBag) + + reactor.state.map { $0.lat } + .distinctUntilChanged() + .bind(onNext: { [weak self] lat in + if self?.mainView.latField.text != lat { + self?.mainView.latField.text = lat + } + }) + .disposed(by: self.disposeBag) + + reactor.state.map { $0.lon } + .distinctUntilChanged() + .bind(onNext: { [weak self] lon in + if self?.mainView.lonField.text != lon { + self?.mainView.lonField.text = lon + } + }) + .disposed(by: self.disposeBag) + + reactor.state.map { $0.description } + .distinctUntilChanged() + .bind(onNext: { [weak self] description in + if self?.mainView.descriptionTextView.text != description { + self?.mainView.descriptionTextView.text = description + } + }) + .disposed(by: self.disposeBag) + + reactor.state.map { $0.category } + .distinctUntilChanged() + .filter { !$0.isEmpty } + .bind(onNext: { [weak self] category in + self?.updateCategoryButtonTitle(with: category) + }) + .disposed(by: self.disposeBag) + + // 날짜 범위 업데이트 + let dateRangeObservable = reactor.state + .compactMap { state -> (Date, Date)? in + guard let start = state.selectedStartDate, + let end = state.selectedEndDate else { + return nil + } + return (start, end) + } + + dateRangeObservable + .distinctUntilChanged { prev, current in + return prev.0 == current.0 && prev.1 == current.1 + } + .bind(onNext: { [weak self] dateRange in + self?.updatePeriodButtonTitle(start: dateRange.0, end: dateRange.1) + }) + .disposed(by: self.disposeBag) + + // 시간 범위 업데이트 + let timeRangeObservable = reactor.state + .compactMap { state -> (Date, Date)? in + guard let start = state.selectedStartTime, + let end = state.selectedEndTime else { + return nil + } + return (start, end) + } + + timeRangeObservable + .distinctUntilChanged { prev, current in + return prev.0 == current.0 && prev.1 == current.1 + } + .bind(onNext: { [weak self] timeRange in + self?.updateTimeButtonTitle(start: timeRange.0, end: timeRange.1) + }) + .disposed(by: self.disposeBag) - return cell } } @@ -977,7 +518,7 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { var newImages = [ExtendedImage]() // 이미 로드된 이미지 경로 목록 (중복 방지) - let existingPaths = Set(reactor?.currentState.images.map { $0.filePath } ?? []) + let existingPaths = Set((self.reactor as? PopUpStoreRegisterReactor)?.currentState.images.map { $0.filePath } ?? []) for (index, provider) in itemProviders.enumerated() { if provider.canLoadObject(ofClass: UIImage.self) { @@ -986,7 +527,7 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { defer { dispatchGroup.leave() } guard let image = object as? UIImage else { return } - let name = self?.reactor?.currentState.name ?? "unnamed" + let name = (self?.reactor as? PopUpStoreRegisterReactor)?.currentState.name ?? "unnamed" let uuid = UUID().uuidString let filePath = "PopUpImage/\(name)/\(uuid)/\(index).jpg" @@ -1005,37 +546,19 @@ extension PopUpStoreRegisterViewController: PHPickerViewControllerDelegate { dispatchGroup.notify(queue: .main) { [weak self] in if !newImages.isEmpty { - self?.reactor?.action.onNext(.addImages(newImages)) + guard let reactor = self?.reactor as? PopUpStoreRegisterReactor else { return } + reactor.action.onNext(.addImages(newImages)) } } } } - -// MARK: - UITextView Delegate -extension PopUpStoreRegisterViewController: UITextViewDelegate { - func textViewDidChange(_ textView: UITextView) { - if textView == descTV { - reactor?.action.onNext(.updateDescription(textView.text)) - } - } -} - -// MARK: - Helper Extensions -extension UITextField { - func setLeftPaddingPoints(_ amount: CGFloat) { - let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: amount, height: frame.size.height)) - leftView = paddingView - leftViewMode = .always - } -} - extension UIView { func findFirstResponder() -> UIView? { - if isFirstResponder { + if self.isFirstResponder { return self } - for subview in subviews { + for subview in self.subviews { if let firstResponder = subview.findFirstResponder() { return firstResponder } From 2d5909d946cd754d04780ec1bddcc321bd05716f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Fri, 11 Apr 2025 14:52:33 +0900 Subject: [PATCH 099/393] =?UTF-8?q?chore/#102:=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20=EB=93=B1=EB=A1=9D=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=2015=EB=B6=84=20=EB=8B=A8=EC=9C=84=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 --- .../AdminRegister/PopUpStoreRegisterView.swift | 17 ++++++----------- .../PopUpStoreRegisterViewController.swift | 3 ++- .../Admin/Common/DateTimePickerManager.swift | 2 ++ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift index d523fc06..eeb03b80 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift @@ -88,7 +88,7 @@ final class PopUpRegisterView: UIView { $0.spacing = 0 } - // 폼 필드들 + let nameField = UITextField().then { $0.placeholder = "팝업스토어 이름을 입력해 주세요." $0.font = UIFont.systemFont(ofSize: 14) @@ -184,7 +184,6 @@ final class PopUpRegisterView: UIView { $0.isScrollEnabled = true } - // 저장 버튼 let saveButton: UIButton = { let btn = UIButton(type: .system) btn.setTitle("저장", for: .normal) @@ -326,7 +325,6 @@ private extension PopUpRegisterView { make.edges.equalToSuperview() } - // 저장 버튼 self.saveButton.snp.makeConstraints { make in make.left.right.equalToSuperview().inset(16) make.bottom.equalTo(self.safeAreaLayoutGuide).offset(-16) @@ -342,13 +340,10 @@ private extension PopUpRegisterView { } func setupFormRows() { - // 이름 필드 추가 self.addFormRow(leftTitle: "이름", rightView: self.nameField) - // 카테고리 버튼 추가 self.addFormRow(leftTitle: "카테고리", rightView: self.categoryButton) - // 위치 필드 추가 (주소, 위도, 경도) let latLabel = self.makePlainLabel("위도") let lonLabel = self.makePlainLabel("경도") @@ -402,11 +397,12 @@ private extension PopUpRegisterView { // 작성자 및 작성시간 let currentTime = self.getCurrentFormattedTime() - let writerLbl = self.makeSimpleLabel("") - let timeLbl = self.makeSimpleLabel(currentTime) + + self.writerLabel = self.makeSimpleLabel("") + let timeLabel = self.makeSimpleLabel(currentTime) - self.addFormRow(leftTitle: "작성자", rightView: writerLbl) - self.addFormRow(leftTitle: "작성시간", rightView: timeLbl) + self.addFormRow(leftTitle: "작성자", rightView: writerLabel) + self.addFormRow(leftTitle: "작성시간", rightView: timeLabel) // 상태값 let statusLbl = self.makeSimpleLabel("진행") @@ -416,7 +412,6 @@ private extension PopUpRegisterView { self.addFormRow(leftTitle: "설명", rightView: self.descriptionTextView, rowHeight: nil, totalHeight: 120) } - // 폼 행 추가 헬퍼 메서드 func addFormRow(leftTitle: String, rightView: UIView, rowHeight: CGFloat? = 36, totalHeight: CGFloat? = nil) { let row = UIView() row.backgroundColor = .white diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index ff17f694..34744333 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -35,6 +35,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { self.reactor = reactor self.mainView.accountIdLabel.text = nickname + "님" + self.mainView.writerLabel.text = nickname if editingStore != nil { self.mainView.pageTitleLabel.text = "팝업스토어 수정" @@ -101,7 +102,7 @@ private extension PopUpStoreRegisterViewController { (self?.reactor as? PopUpStoreRegisterReactor)?.action.onNext(action) }) .disposed(by: self.disposeBag) - + self.mainView.categoryButton.rx.tap .bind { [weak self] in self?.showCategoryPicker() diff --git a/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift b/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift index b1de4138..9fb27041 100644 --- a/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift +++ b/Poppool/Poppool/Presentation/Admin/Common/DateTimePickerManager.swift @@ -77,6 +77,7 @@ final class DateTimePickerManager { let alert1 = UIAlertController(title: "시작시간 선택", message: nil, preferredStyle: .actionSheet) let dp1 = UIDatePicker() dp1.datePickerMode = .time + dp1.minuteInterval = 15 dp1.preferredDatePickerStyle = .wheels alert1.view.addSubview(dp1) @@ -107,6 +108,7 @@ final class DateTimePickerManager { let alert2 = UIAlertController(title: "종료시간 선택", message: nil, preferredStyle: .actionSheet) let dp2 = UIDatePicker() dp2.datePickerMode = .time + dp2.minuteInterval = 15 dp2.preferredDatePickerStyle = .wheels alert2.view.addSubview(dp2) From b8bc440f9e0b07b03ebfd681e127301d2381f759 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 13 Apr 2025 05:40:19 +0000 Subject: [PATCH 100/393] style/#102: Apply SwiftLint autocorrect --- .../AdminRegister/PopUpStoreRegisterReactor.swift | 12 +----------- .../Admin/AdminRegister/PopUpStoreRegisterView.swift | 3 +-- .../PopUpStoreRegisterViewController.swift | 7 +++---- 3 files changed, 5 insertions(+), 17 deletions(-) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 124408ac..e2c6cb67 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -6,7 +6,6 @@ import ReactorKit import RxCocoa import RxSwift - final class PopUpStoreRegisterReactor: Reactor { // MARK: - Properties @@ -17,7 +16,6 @@ final class PopUpStoreRegisterReactor: Reactor { private var disposeBag = DisposeBag() - init(adminUseCase: AdminUseCase, presignedService: PreSignedService, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { self.adminUseCase = adminUseCase self.presignedService = presignedService @@ -145,8 +143,6 @@ final class PopUpStoreRegisterReactor: Reactor { case let .updateName(name): return .just(.setName(name)) - - case let .updateAddress(address): return .just(.setAddress(address)) @@ -208,8 +204,6 @@ final class PopUpStoreRegisterReactor: Reactor { ]) } - - // 스토어 상세 정보 로드 (수정 모드) case let .loadStoreDetail(storeId): return Observable.concat([ @@ -486,7 +480,6 @@ final class PopUpStoreRegisterReactor: Reactor { return true } - // 주소 지오코딩 private func geocodeAddress(address: String) -> Observable { Logger.log(message: "지오코딩 함수 호출: \(address)", category: .debug) @@ -652,7 +645,7 @@ final class PopUpStoreRegisterReactor: Reactor { dispatchGroup.enter() if let imageURL = self.presignedService.fullImageURL(from: imageData.imageUrl) { - URLSession.shared.dataTask(with: imageURL) { data, response, error in + URLSession.shared.dataTask(with: imageURL) { data, _, error in defer { dispatchGroup.leave() } if let error = error { @@ -732,7 +725,6 @@ final class PopUpStoreRegisterReactor: Reactor { .map { _ in updatedImages.map { $0.filePath } } } - // 신규 스토어 등록 private func createStore() -> Observable { return uploadImages() @@ -813,7 +805,6 @@ final class PopUpStoreRegisterReactor: Reactor { .map { _ in updatedImages.map { $0.filePath } } } - // 스토어 정보 업데이트 private func updateStoreInfo(_ newImagePaths: [String]?) -> Observable { guard let storeId = editingStoreId else { @@ -905,4 +896,3 @@ final class PopUpStoreRegisterReactor: Reactor { } } - diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift index eeb03b80..f071b3f5 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterView.swift @@ -88,7 +88,6 @@ final class PopUpRegisterView: UIView { $0.spacing = 0 } - let nameField = UITextField().then { $0.placeholder = "팝업스토어 이름을 입력해 주세요." $0.font = UIFont.systemFont(ofSize: 14) @@ -397,7 +396,7 @@ private extension PopUpRegisterView { // 작성자 및 작성시간 let currentTime = self.getCurrentFormattedTime() - + self.writerLabel = self.makeSimpleLabel("") let timeLabel = self.makeSimpleLabel(currentTime) diff --git a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 34744333..15e6855b 100644 --- a/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/Presentation/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -1,11 +1,11 @@ -import UIKit import CoreLocation import PhotosUI +import UIKit -import SnapKit +import ReactorKit import RxCocoa import RxSwift -import ReactorKit +import SnapKit final class PopUpStoreRegisterViewController: BaseViewController { @@ -320,7 +320,6 @@ extension PopUpStoreRegisterViewController { extension PopUpStoreRegisterViewController: View { typealias Reactor = PopUpStoreRegisterReactor - func bind(reactor: Reactor) { // MARK: - Input (View -> Reactor) // 텍스트 필드 바인딩 From e3db1cb9d5e3973a45ca5cdf4d9511d168824265 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 13 Apr 2025 22:07:48 +0900 Subject: [PATCH 101/393] =?UTF-8?q?refactor/#107:=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Check/modal/infobox/ico/line.png | Bin 516 -> 0 bytes Map/Tmap/modal/ico/logo/logo/square.png | Bin 3943 -> 0 bytes Map/Tmap/naver/modal/ico/logo/logo/square.png | Bin 9022 -> 0 bytes .../logo/logo/modal/ico/logo/logo/square.png | Bin 6222 -> 0 bytes Map/modal/ico/logo/logo/square.png | Bin 1267 -> 0 bytes Map/modal/ico/search/ico/ico/line.png | Bin 300 -> 0 bytes Map/modal/ico/search/ico/ico/line@2x.png | Bin 554 -> 0 bytes Map/modal/ico/search/ico/ico/line@3x.png | Bin 737 -> 0 bytes Marker/UnTapMarker/search/ico/solid.png | Bin 1976 -> 0 bytes Marker/search/ico/solid.png | Bin 2698 -> 0 bytes Poppool/Poppool.xcodeproj/project.pbxproj | 162 +++++++----------- .../Application/TestViewController.swift | 154 ----------------- .../Network/AdminAPI}/AdminAPIEndpoint.swift | 0 .../ResponseDTO}/AdminResponseDTO.swift | 0 .../GetAdminPopUpStoreListResponseDTO.swift | 0 .../MapAPI}/FindDirectionEndPoint.swift | 0 .../Network/MapAPI}/MapAPIEndpoint.swift | 0 .../GetPopUpDirectionResponseDTO.swift | 0 .../ResponseDTO}/MapPopUpStoreDTO.swift | 0 .../Data/Repository/AdminRepository.swift | 0 .../Repository/MapRepository.swift | 0 .../Entities}/MapPopUpStore.swift | 0 .../Repository}/MapDirectionRepository.swift | 0 .../UseCase}/AdminUseCase.swift | 0 .../UseCase}/MapDirectionUseCase.swift | 0 .../UseCase/MapUseCase.swift | 0 .../AdminBottomSheetReactor.swift | 0 .../AdminBottomSheetView.swift | 0 .../AdminBottomSheetViewController.swift | 0 .../{ => Scene}/Admin/AdminReactor.swift | 0 .../PopUpImagesCollectionView.swift | 0 .../PopUpStoreRegisterReactor.swift | 0 .../PopUpStoreRegisterView.swift | 10 +- .../PopUpStoreRegisterViewController.swift | 0 .../{ => Scene}/Admin/AdminStoreCell.swift | 0 .../{ => Scene}/Admin/AdminView.swift | 0 .../Admin/AdminViewController.swift | 0 .../{ => Scene}/Admin/ImageCell.swift | 0 .../Admin/PopUpStoreRegisterReactor.swift | 0 .../Admin/PopUpStoreRegisterView.swift | 0 .../Map/CustomClusterRenderer.swift | 0 .../BalloonBackgroundView.swift | 0 .../FillterSheetView/BalloonChipCell.swift | 0 .../FillterSheetView/CategoryFilterView.swift | 0 .../FilterBottomSheetReactor.swift | 0 .../FilterBottomSheetView.swift | 0 .../FilterBottomSheetViewController.swift | 0 .../Map/FillterSheetView/FilterCell.swift | 0 .../Map/FillterSheetView/FilterChip.swift | 0 .../FillterSheetView/FilterChipsView.swift | 0 .../Map/FillterSheetView/FilterTabsView.swift | 0 .../FillterSheetView/LocationFilterView.swift | 0 .../Map/FindMap/GuideMapViewController.swift | 0 .../FullScreenMapViewController.swift | 0 .../MapGuideView/MapGuideAppService.swift | 0 .../MapGuideView/MapGuideReactor.swift | 0 .../MapGuideView/MapGuideViewController.swift | 0 .../MapPopupCarouselView.swift | 0 .../Map}/MapPopupCardView/PopupCardCell.swift | 0 .../{ => Scene}/Map/MapStoreCard.swift | 0 .../{ => Scene}/Map/MapView/MapMarker.swift | 0 .../{ => Scene}/Map/MapView/MapReactor.swift | 0 .../Map/MapView/MapSearchInput.swift | 0 .../{ => Scene}/Map/MapView/MapView.swift | 0 .../Map/MapView/MapViewController.swift | 0 .../Map/MapView/MarkerTooltipView.swift | 0 .../Map/MicroClusterMarkerView.swift | 0 .../Map/StoreListPanelLayout.swift | 0 .../Map/StoreListView/StoreListCell.swift | 0 .../StoreListView/StoreListHeaderView.swift | 0 .../StoreListView/StoreListPanelLayout.swift | 0 .../Map/StoreListView/StoreListReactor.swift | 0 .../Map/StoreListView/StoreListSection.swift | 0 .../Map/StoreListView/StoreListView.swift | 0 .../StoreListViewController.swift | 0 .../{ => Scene}/Map/StoreLocation.swift | 0 .../{ => Scene}/Map/TestViewController.swift | 0 .../Common/ClusteringManager.swift | 0 .../Common/ClusteringModels.swift | 0 .../Common/DateTimePickerManager.swift | 0 .../Common/ExtendedImage.swift | 0 .../{Map => Utills}/Common/FilterType.swift | 0 .../LocationPermissionBottomSheet.swift | 0 .../Common/MapFilterChips.swift | 0 .../{Map => Utills}/Common/MapUtilities.swift | 0 .../Common/NMFMapViewDelegateProxy.swift | 0 .../Common/RegionDefinitions.swift | 0 .../Common/ViewportBounds.swift | 0 back/search/ico/ico/line.png | Bin 918 -> 0 bytes back/search/ico/ico/search/ico/ico/line.png | Bin 300 -> 0 bytes .../search/ico/ico/search/ico/ico/line@2x.png | Bin 554 -> 0 bytes .../search/ico/ico/search/ico/ico/line@3x.png | Bin 737 -> 0 bytes 92 files changed, 66 insertions(+), 260 deletions(-) delete mode 100644 Check/modal/infobox/ico/line.png delete mode 100644 Map/Tmap/modal/ico/logo/logo/square.png delete mode 100644 Map/Tmap/naver/modal/ico/logo/logo/square.png delete mode 100644 Map/modal/ico/logo/logo/modal/ico/logo/logo/square.png delete mode 100644 Map/modal/ico/logo/logo/square.png delete mode 100644 Map/modal/ico/search/ico/ico/line.png delete mode 100644 Map/modal/ico/search/ico/ico/line@2x.png delete mode 100644 Map/modal/ico/search/ico/ico/line@3x.png delete mode 100644 Marker/UnTapMarker/search/ico/solid.png delete mode 100644 Marker/search/ico/solid.png delete mode 100644 Poppool/Poppool/Application/TestViewController.swift rename Poppool/Poppool/{Presentation/Admin/Data/Remote => Data/Network/AdminAPI}/AdminAPIEndpoint.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/DTO => Data/Network/AdminAPI/ResponseDTO}/AdminResponseDTO.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/DTO => Data/Network/AdminAPI/ResponseDTO}/GetAdminPopUpStoreListResponseDTO.swift (100%) rename Poppool/Poppool/{Presentation/Map/FindMap/MapGuideView => Data/Network/MapAPI}/FindDirectionEndPoint.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/MapDomain => Data/Network/MapAPI}/MapAPIEndpoint.swift (100%) rename Poppool/Poppool/{Presentation/Map/FindMap/MapGuideView => Data/Network/MapAPI/ResponseDTO}/GetPopUpDirectionResponseDTO.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/MapDomain => Data/Network/MapAPI/ResponseDTO}/MapPopUpStoreDTO.swift (100%) rename Poppool/Poppool/{Presentation/Admin => }/Data/Repository/AdminRepository.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/MapDomain => Data}/Repository/MapRepository.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/MapDomain => Domain/Entities}/MapPopUpStore.swift (100%) rename Poppool/Poppool/{Presentation/Map/FindMap/MapGuideView => Domain/Repository}/MapDirectionRepository.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Domain => Domain/UseCase}/AdminUseCase.swift (100%) rename Poppool/Poppool/{Presentation/Map/FindMap/MapGuideView => Domain/UseCase}/MapDirectionUseCase.swift (100%) rename Poppool/Poppool/{Presentation/Admin/Data/MapDomain => Domain}/UseCase/MapUseCase.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminBottomSheet/AdminBottomSheetView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminRegister/PopUpImagesCollectionView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminRegister/PopUpStoreRegisterReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminRegister/PopUpStoreRegisterView.swift (99%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminRegister/PopUpStoreRegisterViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminStoreCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/AdminViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/ImageCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/PopUpStoreRegisterReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Admin/PopUpStoreRegisterView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/CustomClusterRenderer.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/BalloonBackgroundView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/BalloonChipCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/CategoryFilterView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterBottomSheetReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterBottomSheetView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterBottomSheetViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterChip.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterChipsView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/FilterTabsView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FillterSheetView/LocationFilterView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FindMap/GuideMapViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FindMap/MapGuideView/FullScreenMapViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FindMap/MapGuideView/MapGuideAppService.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FindMap/MapGuideView/MapGuideReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/FindMap/MapGuideView/MapGuideViewController.swift (100%) rename Poppool/Poppool/Presentation/{Map/Common => Scene/Map}/MapPopupCardView/MapPopupCarouselView.swift (100%) rename Poppool/Poppool/Presentation/{Map/Common => Scene/Map}/MapPopupCardView/PopupCardCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapStoreCard.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MapMarker.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MapReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MapSearchInput.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MapView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MapViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MapView/MarkerTooltipView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/MicroClusterMarkerView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListPanelLayout.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListCell.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListHeaderView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListPanelLayout.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListReactor.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListSection.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListView.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreListView/StoreListViewController.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/StoreLocation.swift (100%) rename Poppool/Poppool/Presentation/{ => Scene}/Map/TestViewController.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/ClusteringManager.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/ClusteringModels.swift (100%) rename Poppool/Poppool/Presentation/{Admin => Utills}/Common/DateTimePickerManager.swift (100%) rename Poppool/Poppool/Presentation/{Admin => Utills}/Common/ExtendedImage.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/FilterType.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/LocationPermissionBottomSheet.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/MapFilterChips.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/MapUtilities.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/NMFMapViewDelegateProxy.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/RegionDefinitions.swift (100%) rename Poppool/Poppool/Presentation/{Map => Utills}/Common/ViewportBounds.swift (100%) delete mode 100644 back/search/ico/ico/line.png delete mode 100644 back/search/ico/ico/search/ico/ico/line.png delete mode 100644 back/search/ico/ico/search/ico/ico/line@2x.png delete mode 100644 back/search/ico/ico/search/ico/ico/line@3x.png diff --git a/Check/modal/infobox/ico/line.png b/Check/modal/infobox/ico/line.png deleted file mode 100644 index 421badb4c1a84c7b79473869b27070030709afa7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 516 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 diff --git a/Map/Tmap/naver/modal/ico/logo/logo/square.png b/Map/Tmap/naver/modal/ico/logo/logo/square.png deleted file mode 100644 index 7bfb56580a831167adf777a3925ce701b5ef1227..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9022 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 Kw;FMyp#KA=*GDA) diff --git a/Map/modal/ico/logo/logo/modal/ico/logo/logo/square.png b/Map/modal/ico/logo/logo/modal/ico/logo/logo/square.png deleted file mode 100644 index ed2697379008b234d528a73e23b865978337883d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6222 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 C#;TJ5 diff --git a/Map/modal/ico/logo/logo/square.png b/Map/modal/ico/logo/logo/square.png deleted file mode 100644 index 5eb0bfb01aac25e59943c3cbabf035280a80c8ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1267 zcmVVx2eiVcV@RD)<=T7sZWqm|gkq}gpY`!joIuIFwV+a|M3=gw@Gz0*z$CrD-2ONoyY*|xtn!tAzxW~`91}clBJoZdB5pvynD-e&*jODN!il}N zqcHB@8`=bv@=ik(vA5IUOC0jV)#n3~3&?XsxSPWw63hn)tt_ORU%yyrDX*Z&vnCtY zb&bMo^cO~21XzgU;oc)KT7+liXP`)ohnn!=&wC-Iem2(%p~-L(%{}LUMP-E2l1i$e z6`DsFiILq5#T2yQnL1|-FCF>_ywN>wO>U!pDvN`UwqtPTI=sK}6(BVo_ikVb2Zw&! zRtifUc=mu90~&ks$V4J@{xj$>w#qXY?p%h#7!@=U?a*h0&I#)z730!X(YBT!@!b>m zL)!2R&mLg(M5YjN{m-7{_W@u%*H)89R+gk&rKDgVDpy{S9m`IynJILgjrUi5TUZfxuM@Ic_v^ovNCr+ zz$_{;F#I5F$C#}lI=}yb+6;FsS0+!vv7iOd1VonMkrjvC`xukq-o77j5_f>d7z}cV z(FhzI-hutUJ_|P&P~(cu4evo%*C-3AEHWmSqILh@kaaE!sg-xIB%H#{&BGWRS&eL@ z4h}D4s|k|XMRgzzEqHFHmF2ImJA>O|r!i!=&Z)%88nTY#EShk3q6w(Kf-FkAFK+r2 zBF&(C2(&7(@`Amn4$-%=KoVMD6gzT)%(0KGKLKs%^+IZVnY3`=$&ONctOI!-IQ{Z+ zizQ6zb!76&vw^DAYmGl}Z1)=2nPA3z7R2qe zO>=QJnn3fuG3xZnY(z_{*13n#6vhIXhZO?37gD`WLoS6kH+NHrxQ7xi2n;;G8To(B zz?4^DLB1adQA!QJVWSwo+%9TxUmqUCK*ya_CS%5I6zV-?ZP|3p+h6I&Ngp8ar7L>nl{`c3NZ@?pyv-DSX~-dyT$V?h>3;;w_~r zZdsPVU417o^ujU}r%*AlO1)-P6*&}kXi)KYbvPd?=35m$b`cd-zO)Y(IBEo{jT(V! dqeh_G_z%G_rWV(A5=;O9002ovPDHLkV1n{UIrsno diff --git a/Map/modal/ico/search/ico/ico/line.png b/Map/modal/ico/search/ico/ico/line.png deleted file mode 100644 index 10972352c5b3d0bfe9f4da91953032db5162ded9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1|*Ak?@s|zoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{y|R{$B+ufw^Q!&9#Rl#_1ERMne{ookNgHyUp3muDIq5^kr3r5)r7-Vvs424iCg${F*0P;jb9cLYX65hP z^LB#D?)Y!pPVM+Clco7oRwm$-q3z3#nNL(&1d_a?eXbu@XJc1+wR~eodclGzRvT+? s2p9i8^XkLY)Nh=69%X6X5kIBnc#`eb@87$a5$G8PPgg&ebxsLQ0MZ+6)&Kwi diff --git a/Map/modal/ico/search/ico/ico/line@2x.png b/Map/modal/ico/search/ico/ico/line@2x.png deleted file mode 100644 index 4d6322002590b91960c959a0c424b854c0df8c45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 554 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 AMF0Q* diff --git a/Marker/search/ico/solid.png b/Marker/search/ico/solid.png deleted file mode 100644 index 39f13b3126d68a1cbc184d26c7b7cac06527e45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2698 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 diff --git a/back/search/ico/ico/search/ico/ico/line.png b/back/search/ico/ico/search/ico/ico/line.png deleted file mode 100644 index 10972352c5b3d0bfe9f4da91953032db5162ded9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 300 zcmeAS@N?(olHy`uVBq!ia0vp^G9b*s1|*Ak?@s|zoCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eB{y|R{$B+ufw^Q!&9#Rl#_1ERMne{ookNgHyUp3muDIq5^kr3r5)r7-Vvs424iCg${F*0P;jb9cLYX65hP z^LB#D?)Y!pPVM+Clco7oRwm$-q3z3#nNL(&1d_a?eXbu@XJc1+wR~eodclGzRvT+? s2p9i8^XkLY)Nh=69%X6X5kIBnc#`eb@87$a5$G8PPgg&ebxsLQ0MZ+6)&Kwi diff --git a/back/search/ico/ico/search/ico/ico/line@2x.png b/back/search/ico/ico/search/ico/ico/line@2x.png deleted file mode 100644 index 4d6322002590b91960c959a0c424b854c0df8c45..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 554 zcmV+_0@eMAP)!k2f&j?_p10*atzpRbvMg&5An6v3-eH(8T8mrsbULj-h-4j% z-r=&PH4Ilq|3Hue5)r+_CD0lMe3Uv|{z%fnOWq;R$K&w>gb{a8!#j#-2erJTh&rg{ z9Yw5zn%+@F9MtxX!rnm(-ci^cwBQ|uwS!i?qp&$>$vX;52d#NWVR6u!ca(p35WQQ2 zG4CiMAA0QGH5h^AkijnQa9Iz^w^dc`K^c|~qJx`ws0_;6^WmckOTZezPsGEZJjLK| zpiB--G)+^EMx!5myd&i>9*?K^?h}+?|JkKSu8zfcQq)Hy)N)aJfs2D^qyTM|RyCM;G zXNLcS!^O24cE4G7XO9Q~0000000000AOPKNcQP0Z5(1ag>-FwkKy&wexo1p*sFE`L zs5~BzkFI%rdPc2QtJClI-?J>+QDiElh$Klmo6Y7U&-3KGuTrU`F0jKiO*4u}jTEt7 zuP5#!A;G8_q28NrWX?*uvs$e-Tr3tTMMi}~&%9a0!*aQNCIMWLaJ)AUhr_FDc8dC) zJLjv_YDPj3A5ibjq9o)wC88+_LbOA*c#|X6p_;tO5jo|xd6OeDpxV625$8~i-sFgK zs8(-s#7ue3-sFf0sAg|+L^xEtH#yNAYQdYFs8imGH#t!QYQ>wJXb!dHO->YtTJt6+ z_>{NkO-}HDTJ$C-s6(xKlM~FLmc7XdGUcs%lM^JM*1gFI;!t0MM8%`igf}_jPd&8} zDxmX{?|URliEUnJ- zzXV%{x=f!no6T?69F_d!rrmBo`QMRZf+i)u)^wxMv`>TYHZ>ZJH_JW^zHGAY(-;irK!i;!{!ep%#29Zi897 zPetAW(d<)^M Date: Sun, 13 Apr 2025 13:38:47 +0000 Subject: [PATCH 102/393] style/#107: Apply SwiftLint autocorrect --- .../Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift index 85c55aac..f33fb43b 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift @@ -19,7 +19,7 @@ final class PopUpRegisterView: UIView { // 네비게이션 영역 let navigationContainer = UIView() var writerLabel: UILabel! - + let logoImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "image_login_logo") @@ -89,7 +89,6 @@ final class PopUpRegisterView: UIView { $0.spacing = 0 } - let nameField = UITextField().then { $0.placeholder = "팝업스토어 이름을 입력해 주세요." $0.font = UIFont.systemFont(ofSize: 14) @@ -400,7 +399,7 @@ private extension PopUpRegisterView { // 작성자 및 작성시간 let currentTime = self.getCurrentFormattedTime() - + self.writerLabel = self.makeSimpleLabel("") let timeLabel = self.makeSimpleLabel(currentTime) From d7acea3d17990b71b92e6d9485892f07f59c3fe4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 20:51:16 +0900 Subject: [PATCH 103/393] =?UTF-8?q?refactor/#112:=20Convert=20to=20Folder?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 프로젝트가 추적하지 않는 파일들 삭제 --- Poppool/Poppool.xcodeproj/project.pbxproj | 3411 +---------------- .../GetBlockUserListResponse.swift | 0 .../{ => UserAPI}/GetMyCommentResponse.swift | 0 .../{ => UserAPI}/GetMyPageResponse.swift | 0 .../{ => UserAPI}/GetMyProfileResponse.swift | 0 .../GetNoticeDetailResponse.swift | 0 .../{ => UserAPI}/GetNoticeListResponse.swift | 0 ...tOtherUserCommentedPopUpListResponse.swift | 0 .../GetRecentPopUpResponse.swift | 0 .../GetWithdrawlListResponse.swift | 0 .../Admin/PopUpStoreRegisterReactor.swift | 306 -- .../Scene/Admin/PopUpStoreRegisterView.swift | 424 -- .../Scene/Map/CustomClusterRenderer.swift | 15 - .../FillterSheetView/CategoryFilterView.swift | 35 - .../Map/FillterSheetView/FilterTabsView.swift | 39 - .../FillterSheetView/LocationFilterView.swift | 40 - .../Map/FindMap/GuideMapViewController.swift | 1 - .../MapGuideView/MapGuideAppService.swift | 71 - .../Presentation/Scene/Map/MapStoreCard.swift | 116 - .../Scene/Map/MicroClusterMarkerView.swift | 2 - .../Scene/Map/StoreListPanelLayout.swift | 30 - .../StoreListView/StoreListHeaderView.swift | 54 - .../StoreListView/StoreListPanelLayout.swift | 39 - .../Scene/Map/StoreLocation.swift | 8 - .../Scene/Map/TestViewController.swift | 153 - 25 files changed, 60 insertions(+), 4684 deletions(-) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetBlockUserListResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetMyCommentResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetMyPageResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetMyProfileResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetNoticeDetailResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetNoticeListResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetOtherUserCommentedPopUpListResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetRecentPopUpResponse.swift (100%) rename Poppool/Poppool/Domain/Entities/{ => UserAPI}/GetWithdrawlListResponse.swift (100%) delete mode 100644 Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterReactor.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/CustomClusterRenderer.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/CategoryFilterView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterTabsView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/LocationFilterView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/FindMap/GuideMapViewController.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideAppService.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/MapStoreCard.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/MicroClusterMarkerView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/StoreListPanelLayout.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListHeaderView.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListPanelLayout.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/StoreLocation.swift delete mode 100644 Poppool/Poppool/Presentation/Scene/Map/TestViewController.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index f8e92c85..d87bba1e 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -3,466 +3,20 @@ archiveVersion = 1; classes = { }; - objectVersion = 56; + objectVersion = 76; objects = { /* Begin PBXBuildFile section */ - 0818988E2D295DC30067BF01 /* MyPageLogoutSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */; }; - 081898902D295DC80067BF01 /* MyPageLogoutSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818988F2D295DC80067BF01 /* MyPageLogoutSectionCell.swift */; }; - 081898942D2965C20067BF01 /* ProfileEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898932D2965C20067BF01 /* ProfileEditController.swift */; }; - 081898962D2965C90067BF01 /* ProfileEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898952D2965C90067BF01 /* ProfileEditView.swift */; }; - 081898982D2965D20067BF01 /* ProfileEditReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898972D2965D20067BF01 /* ProfileEditReactor.swift */; }; - 0818989C2D2BAA570067BF01 /* WithdrawlCheckModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818989B2D2BAA570067BF01 /* WithdrawlCheckModalView.swift */; }; - 0818989E2D2BAA610067BF01 /* WithdrawlCheckModalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818989D2D2BAA610067BF01 /* WithdrawlCheckModalController.swift */; }; - 081898A02D2BAA670067BF01 /* WithdrawlCheckModalReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818989F2D2BAA670067BF01 /* WithdrawlCheckModalReactor.swift */; }; - 081898A32D2CC0110067BF01 /* WithdrawlReasonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898A22D2CC0110067BF01 /* WithdrawlReasonView.swift */; }; - 081898A52D2CC0180067BF01 /* WithdrawlReasonController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898A42D2CC0180067BF01 /* WithdrawlReasonController.swift */; }; - 081898A72D2CC01D0067BF01 /* WithdrawlReasonReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898A62D2CC01D0067BF01 /* WithdrawlReasonReactor.swift */; }; - 081898AA2D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898A92D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift */; }; - 081898AC2D2CEA940067BF01 /* WithdrawlCheckSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898AB2D2CEA940067BF01 /* WithdrawlCheckSection.swift */; }; - 081898AE2D2CFC230067BF01 /* GetWithdrawlListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898AD2D2CFC230067BF01 /* GetWithdrawlListResponseDTO.swift */; }; - 081898B02D2CFCA40067BF01 /* GetWithdrawlListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898AF2D2CFCA40067BF01 /* GetWithdrawlListResponse.swift */; }; - 081898B32D2D20D70067BF01 /* WithdrawlCompleteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898B22D2D20D70067BF01 /* WithdrawlCompleteView.swift */; }; - 081898B52D2D20E30067BF01 /* WithdrawlCompleteController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898B42D2D20E30067BF01 /* WithdrawlCompleteController.swift */; }; - 081898B72D2D23A90067BF01 /* UINavigationController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898B62D2D23A90067BF01 /* UINavigationController+.swift */; }; - 081898BA2D2E5F4C0067BF01 /* MyCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898B92D2E5F4C0067BF01 /* MyCommentView.swift */; }; - 081898BC2D2E5F510067BF01 /* MyCommentReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898BB2D2E5F510067BF01 /* MyCommentReactor.swift */; }; - 081898BE2D2E5F590067BF01 /* MyCommentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898BD2D2E5F590067BF01 /* MyCommentController.swift */; }; - 081898C32D30AE2C0067BF01 /* GetMyProfileResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898C22D30AE2C0067BF01 /* GetMyProfileResponseDTO.swift */; }; - 081898C52D30AEF40067BF01 /* GetMyProfileResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898C42D30AEF40067BF01 /* GetMyProfileResponse.swift */; }; - 081898CA2D30D5BA0067BF01 /* InfoEditModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898C92D30D5BA0067BF01 /* InfoEditModalView.swift */; }; - 081898CC2D30D5BF0067BF01 /* InfoEditModalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898CB2D30D5BF0067BF01 /* InfoEditModalController.swift */; }; - 081898CE2D30D5C60067BF01 /* InfoEditModalReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898CD2D30D5C60067BF01 /* InfoEditModalReactor.swift */; }; - 081898D02D30EA900067BF01 /* PutUserTailoredInfoRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898CF2D30EA900067BF01 /* PutUserTailoredInfoRequestDTO.swift */; }; - 081898D22D30F57D0067BF01 /* CategoryEditModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898D12D30F57D0067BF01 /* CategoryEditModalView.swift */; }; - 081898D42D30F5840067BF01 /* CategoryEditModalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898D32D30F5840067BF01 /* CategoryEditModalController.swift */; }; - 081898D62D30F58A0067BF01 /* CategoryEditModalReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898D52D30F58A0067BF01 /* CategoryEditModalReactor.swift */; }; - 081898D82D310C160067BF01 /* PutUserCategoryRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898D72D310C160067BF01 /* PutUserCategoryRequestDTO.swift */; }; - 081898DA2D32559B0067BF01 /* PutUserProfileRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898D92D32559B0067BF01 /* PutUserProfileRequestDTO.swift */; }; - 081898DC2D326DC10067BF01 /* IntroState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898DB2D326DC10067BF01 /* IntroState.swift */; }; - 081898E02D338F9C0067BF01 /* ListCountButtonSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898DF2D338F9C0067BF01 /* ListCountButtonSection.swift */; }; - 081898E22D338FA40067BF01 /* ListCountButtonSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898E12D338FA40067BF01 /* ListCountButtonSectionCell.swift */; }; - 081898E42D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898E32D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift */; }; - 081898E62D3391CB0067BF01 /* GetMyCommentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898E52D3391CB0067BF01 /* GetMyCommentResponse.swift */; }; - 081898E82D3392480067BF01 /* GetMyCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898E72D3392480067BF01 /* GetMyCommentRequestDTO.swift */; }; - 081898EC2D33A3960067BF01 /* MyCommentSortedModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898EB2D33A3960067BF01 /* MyCommentSortedModalView.swift */; }; - 081898EE2D33A39D0067BF01 /* MyCommentSortedModalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898ED2D33A39D0067BF01 /* MyCommentSortedModalController.swift */; }; - 081898F02D33A3A30067BF01 /* MyCommentSortedModalReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898EF2D33A3A30067BF01 /* MyCommentSortedModalReactor.swift */; }; - 081898F32D33D6AC0067BF01 /* BlockUserManageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898F22D33D6AC0067BF01 /* BlockUserManageView.swift */; }; - 081898F52D33D6B10067BF01 /* BlockUserManageController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898F42D33D6B10067BF01 /* BlockUserManageController.swift */; }; - 081898F72D33D6B70067BF01 /* BlockUserManageReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898F62D33D6B70067BF01 /* BlockUserManageReactor.swift */; }; - 081898FB2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898FA2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift */; }; - 081898FD2D33D9ED0067BF01 /* GetBlockUserListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898FC2D33D9ED0067BF01 /* GetBlockUserListResponse.swift */; }; - 081898FF2D33DA440067BF01 /* GetBlockUserListRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898FE2D33DA440067BF01 /* GetBlockUserListRequestDTO.swift */; }; - 081899022D3407F50067BF01 /* BlockUserListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899012D3407F50067BF01 /* BlockUserListSection.swift */; }; - 081899052D34080B0067BF01 /* BlockUserListSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899042D34080B0067BF01 /* BlockUserListSectionCell.swift */; }; - 081899082D34B35A0067BF01 /* MyPageNoticeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899072D34B35A0067BF01 /* MyPageNoticeView.swift */; }; - 0818990A2D34B3620067BF01 /* MyPageNoticeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899092D34B3620067BF01 /* MyPageNoticeController.swift */; }; - 0818990C2D34B3670067BF01 /* MyPageNoticeReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818990B2D34B3670067BF01 /* MyPageNoticeReactor.swift */; }; - 0818990E2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818990D2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift */; }; - 081899102D34B7240067BF01 /* GetNoticeListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818990F2D34B7240067BF01 /* GetNoticeListResponse.swift */; }; - 081899122D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899112D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift */; }; - 081899142D34CAEA0067BF01 /* GetNoticeDetailResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899132D34CAEA0067BF01 /* GetNoticeDetailResponse.swift */; }; - 081899182D34D63E0067BF01 /* NoticeListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899172D34D63E0067BF01 /* NoticeListSection.swift */; }; - 0818991A2D34D6430067BF01 /* NoticeListSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899192D34D6430067BF01 /* NoticeListSectionCell.swift */; }; - 0818991E2D34DF7D0067BF01 /* MyPageNoticeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818991D2D34DF7D0067BF01 /* MyPageNoticeDetailView.swift */; }; - 081899202D34DF880067BF01 /* MyPageNoticeDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818991F2D34DF880067BF01 /* MyPageNoticeDetailController.swift */; }; - 081899222D34DF8E0067BF01 /* MyPageNoticeDetailReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899212D34DF8E0067BF01 /* MyPageNoticeDetailReactor.swift */; }; - 081899252D3500B80067BF01 /* FAQView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899242D3500B80067BF01 /* FAQView.swift */; }; - 081899272D3500BF0067BF01 /* FAQController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899262D3500BF0067BF01 /* FAQController.swift */; }; - 081899292D3500C50067BF01 /* FAQReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899282D3500C50067BF01 /* FAQReactor.swift */; }; - 0818992D2D3506240067BF01 /* FAQDropdownSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818992C2D3506240067BF01 /* FAQDropdownSectionCell.swift */; }; - 0818992F2D3506290067BF01 /* FAQDropdownSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818992E2D3506290067BF01 /* FAQDropdownSection.swift */; }; - 081899332D35F1090067BF01 /* MyPageBookmarkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899322D35F1090067BF01 /* MyPageBookmarkView.swift */; }; - 081899352D35F10F0067BF01 /* MyPageBookmarkController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899342D35F10F0067BF01 /* MyPageBookmarkController.swift */; }; - 081899372D35F1140067BF01 /* MyPageBookmarkReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899362D35F1140067BF01 /* MyPageBookmarkReactor.swift */; }; - 081899392D35F11F0067BF01 /* MyPageRecentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899382D35F11F0067BF01 /* MyPageRecentView.swift */; }; - 0818993B2D35F1250067BF01 /* MyPageRecentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818993A2D35F1250067BF01 /* MyPageRecentController.swift */; }; - 0818993D2D35F12A0067BF01 /* MyPageRecentReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818993C2D35F12A0067BF01 /* MyPageRecentReactor.swift */; }; - 0818993F2D35FBE00067BF01 /* GetRecentPopUpResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818993E2D35FBE00067BF01 /* GetRecentPopUpResponseDTO.swift */; }; - 081899412D35FDA10067BF01 /* GetRecentPopUpResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899402D35FDA10067BF01 /* GetRecentPopUpResponse.swift */; }; - 081899452D35FEA10067BF01 /* RecentPopUpSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899442D35FEA10067BF01 /* RecentPopUpSection.swift */; }; - 0818994A2D36322B0067BF01 /* PopUpCardSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899492D36322B0067BF01 /* PopUpCardSection.swift */; }; - 0818994C2D3632320067BF01 /* PopUpCardSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818994B2D3632320067BF01 /* PopUpCardSectionCell.swift */; }; - 081899502D363E5C0067BF01 /* BookMarkPopUpViewTypeModalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0818994F2D363E5C0067BF01 /* BookMarkPopUpViewTypeModalView.swift */; }; - 081899522D363E640067BF01 /* BookMarkPopUpViewTypeModalController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899512D363E640067BF01 /* BookMarkPopUpViewTypeModalController.swift */; }; - 081899542D363E6A0067BF01 /* BookMarkPopUpViewTypeModalReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081899532D363E6A0067BF01 /* BookMarkPopUpViewTypeModalReactor.swift */; }; 082197A12D426DCB0054094A /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 082197A02D426DCB0054094A /* Then */; }; - 082197A72D4E3EE00054094A /* CommentMyMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197A62D4E3EE00054094A /* CommentMyMenuView.swift */; }; - 082197A92D4E3EE90054094A /* CommentMyMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197A82D4E3EE90054094A /* CommentMyMenuController.swift */; }; - 082197AB2D4E3EEF0054094A /* CommentMyMenuReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197AA2D4E3EEF0054094A /* CommentMyMenuReactor.swift */; }; - 082197AD2D4E49370054094A /* DeleteCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197AC2D4E49370054094A /* DeleteCommentRequestDTO.swift */; }; - 082197B02D4E4E190054094A /* NormalCommentEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197AF2D4E4E190054094A /* NormalCommentEditView.swift */; }; - 082197B22D4E4E200054094A /* NormalCommentEditController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197B12D4E4E200054094A /* NormalCommentEditController.swift */; }; - 082197B42D4E4E280054094A /* NormalCommentEditReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 082197B32D4E4E280054094A /* NormalCommentEditReactor.swift */; }; - 083A25822CF361EF0099B58E /* BaseTabmanController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A257F2CF361EF0099B58E /* BaseTabmanController.swift */; }; - 083A25832CF361EF0099B58E /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25802CF361EF0099B58E /* BaseViewController.swift */; }; - 083A25992CF362090099B58E /* Sectionable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25932CF362090099B58E /* Sectionable.swift */; }; - 083A259A2CF362090099B58E /* SectionDecorationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25942CF362090099B58E /* SectionDecorationItem.swift */; }; - 083A259B2CF362090099B58E /* SectionSupplementaryItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25952CF362090099B58E /* SectionSupplementaryItem.swift */; }; - 083A259C2CF362090099B58E /* InOutputable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25972CF362090099B58E /* InOutputable.swift */; }; - 083A25B22CF362670099B58E /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A12CF362670099B58E /* NetworkError.swift */; }; - 083A25B32CF362670099B58E /* Requestable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A22CF362670099B58E /* Requestable.swift */; }; - 083A25B42CF362670099B58E /* Responsable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A32CF362670099B58E /* Responsable.swift */; }; - 083A25B52CF362670099B58E /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A52CF362670099B58E /* Endpoint.swift */; }; - 083A25B62CF362670099B58E /* MultipartEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A62CF362670099B58E /* MultipartEndPoint.swift */; }; - 083A25B72CF362670099B58E /* RequestEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A72CF362670099B58E /* RequestEndpoint.swift */; }; - 083A25B82CF362670099B58E /* IndicatorMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25A92CF362670099B58E /* IndicatorMaker.swift */; }; - 083A25B92CF362670099B58E /* FormDataInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25AB2CF362670099B58E /* FormDataInterceptor.swift */; }; - 083A25BA2CF362670099B58E /* TokenInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25AC2CF362670099B58E /* TokenInterceptor.swift */; }; - 083A25BB2CF362670099B58E /* Provider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25AE2CF362670099B58E /* Provider.swift */; }; - 083A25BC2CF362670099B58E /* ProviderImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25AF2CF362670099B58E /* ProviderImpl.swift */; }; - 083A25BF2CF362770099B58E /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25BD2CF362770099B58E /* Logger.swift */; }; - 083A25C82CF363C00099B58E /* LoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25C72CF363C00099B58E /* LoginController.swift */; }; - 083A25CA2CF363C60099B58E /* LoginReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25C92CF363C60099B58E /* LoginReactor.swift */; }; - 083A25CC2CF363CB0099B58E /* LoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083A25CB2CF363CB0099B58E /* LoginView.swift */; }; 083A25D02CF364B70099B58E /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 083A25CF2CF364B70099B58E /* Alamofire */; }; - 083C860B2D073A15003F441C /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C860A2D073A15003F441C /* DetailView.swift */; }; - 083C860D2D073A1C003F441C /* DetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C860C2D073A1C003F441C /* DetailController.swift */; }; - 083C860F2D073A23003F441C /* DetailReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C860E2D073A23003F441C /* DetailReactor.swift */; }; - 083C861C2D087337003F441C /* GetPopUpDetailRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C861B2D087337003F441C /* GetPopUpDetailRequestDTO.swift */; }; - 083C861E2D08737F003F441C /* GetPopUpDetailResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C861D2D08737F003F441C /* GetPopUpDetailResponseDTO.swift */; }; - 083C86202D087445003F441C /* GetPopUpDetailResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C861F2D087445003F441C /* GetPopUpDetailResponse.swift */; }; - 083C86242D087A44003F441C /* DetailTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86232D087A44003F441C /* DetailTitleSection.swift */; }; - 083C86262D087A4E003F441C /* DetailTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86252D087A4E003F441C /* DetailTitleSectionCell.swift */; }; - 083C86292D088080003F441C /* DetailContentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86282D088080003F441C /* DetailContentSection.swift */; }; - 083C862B2D08808C003F441C /* DetailContentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C862A2D08808C003F441C /* DetailContentSectionCell.swift */; }; - 083C86362D0C7EF4003F441C /* CommentSelectedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86352D0C7EF4003F441C /* CommentSelectedController.swift */; }; - 083C86382D0C7EFC003F441C /* CommentSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86372D0C7EFC003F441C /* CommentSelectedView.swift */; }; - 083C863A2D0C7F0A003F441C /* CommentSelectedReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86392D0C7F0A003F441C /* CommentSelectedReactor.swift */; }; - 083C863D2D0C8BC4003F441C /* NormalCommentAddController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C863C2D0C8BC4003F441C /* NormalCommentAddController.swift */; }; - 083C863F2D0C8BCE003F441C /* NormalCommentAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C863E2D0C8BCE003F441C /* NormalCommentAddView.swift */; }; - 083C86412D0C8BD8003F441C /* NormalCommentAddReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86402D0C8BD8003F441C /* NormalCommentAddReactor.swift */; }; - 083C86452D0DCDE9003F441C /* AddCommentTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86442D0DCDE8003F441C /* AddCommentTitleSectionCell.swift */; }; - 083C86472D0DCDFB003F441C /* AddCommentTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86462D0DCDFB003F441C /* AddCommentTitleSection.swift */; }; - 083C864A2D0DCF96003F441C /* AddCommentDescriptionSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86492D0DCF96003F441C /* AddCommentDescriptionSection.swift */; }; - 083C864C2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C864B2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift */; }; - 083C864F2D0DD3A6003F441C /* AddCommentImageSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C864E2D0DD3A6003F441C /* AddCommentImageSection.swift */; }; - 083C86512D0DD3AB003F441C /* AddCommentImageSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86502D0DD3AB003F441C /* AddCommentImageSectionCell.swift */; }; - 083C86542D0DD7E9003F441C /* AddCommentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86532D0DD7E9003F441C /* AddCommentSection.swift */; }; - 083C86562D0DD7EE003F441C /* AddCommentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86552D0DD7EE003F441C /* AddCommentSectionCell.swift */; }; - 083C86592D0DEFC3003F441C /* CommentCheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86582D0DEFC3003F441C /* CommentCheckView.swift */; }; - 083C865B2D0DEFCF003F441C /* CommentCheckController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C865A2D0DEFCF003F441C /* CommentCheckController.swift */; }; - 083C865D2D0DEFD5003F441C /* CommentCheckReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C865C2D0DEFD5003F441C /* CommentCheckReactor.swift */; }; - 083C86602D0EC496003F441C /* InstaCommentAddView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C865F2D0EC496003F441C /* InstaCommentAddView.swift */; }; - 083C86622D0EC49E003F441C /* InstaCommentAddController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86612D0EC49E003F441C /* InstaCommentAddController.swift */; }; - 083C86642D0EC4A5003F441C /* InstaCommentAddReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86632D0EC4A5003F441C /* InstaCommentAddReactor.swift */; }; - 083C86692D0ECB47003F441C /* InstaGuideSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86682D0ECB47003F441C /* InstaGuideSection.swift */; }; - 083C866B2D0ECB4F003F441C /* InstaGuideSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C866A2D0ECB4F003F441C /* InstaGuideSectionCell.swift */; }; - 083C866E2D0ECB87003F441C /* InstaGuideChildSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C866D2D0ECB87003F441C /* InstaGuideChildSection.swift */; }; - 083C86702D0ECB8E003F441C /* InstaGuideChildSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C866F2D0ECB8E003F441C /* InstaGuideChildSectionCell.swift */; }; - 083C86732D0EE2B1003F441C /* CommentAPIEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86722D0EE2B1003F441C /* CommentAPIEndPoint.swift */; }; - 083C86762D0EE2CF003F441C /* PostCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86752D0EE2CF003F441C /* PostCommentRequestDTO.swift */; }; - 083C86782D0EE382003F441C /* CommentAPIRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86772D0EE382003F441C /* CommentAPIRepository.swift */; }; - 083C867A2D0EE3BB003F441C /* CommentAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 083C86792D0EE3BB003F441C /* CommentAPIUseCaseImpl.swift */; }; - 0841BA802CF9F34100049E31 /* ImageBannerChildSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA7F2CF9F34100049E31 /* ImageBannerChildSectionCell.swift */; }; - 0841BA822CF9F5DF00049E31 /* PreSignedService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA812CF9F5DF00049E31 /* PreSignedService.swift */; }; - 0841BA872CF9F62400049E31 /* PreSignedURLDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA842CF9F62300049E31 /* PreSignedURLDTO.swift */; }; - 0841BA882CF9F62400049E31 /* PresignedURLRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA852CF9F62300049E31 /* PresignedURLRequestDTO.swift */; }; - 0841BA892CF9F62400049E31 /* PreSignedURLResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA862CF9F62300049E31 /* PreSignedURLResponseDTO.swift */; }; - 0841BA8C2CF9F67100049E31 /* PreSignedAPIEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA8B2CF9F67100049E31 /* PreSignedAPIEndPoint.swift */; }; - 0841BA8E2CF9F8A100049E31 /* ImageBannerChildSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BA8D2CF9F8A100049E31 /* ImageBannerChildSection.swift */; }; - 0841BAA32CFA31A300049E31 /* SpacingSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAA22CFA31A300049E31 /* SpacingSection.swift */; }; - 0841BAA52CFA31A900049E31 /* SpacingSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAA42CFA31A900049E31 /* SpacingSectionCell.swift */; }; - 0841BAA82CFA354500049E31 /* HomeTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAA72CFA354500049E31 /* HomeTitleSection.swift */; }; - 0841BAAA2CFA354C00049E31 /* HomeTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAA92CFA354C00049E31 /* HomeTitleSectionCell.swift */; }; - 0841BAAC2CFA35F300049E31 /* UILabel+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAAB2CFA35F300049E31 /* UILabel+.swift */; }; - 0841BAAF2CFA38EA00049E31 /* HomeCardSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAAE2CFA38EA00049E31 /* HomeCardSection.swift */; }; - 0841BAB12CFA38F500049E31 /* HomeCardSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAB02CFA38F500049E31 /* HomeCardSectionCell.swift */; }; - 0841BAB42CFABED700049E31 /* HomePopularCardSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAB32CFABED700049E31 /* HomePopularCardSection.swift */; }; - 0841BAB62CFABEDC00049E31 /* HomePopularCardSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAB52CFABEDC00049E31 /* HomePopularCardSectionCell.swift */; }; - 0841BAB82CFAC41300049E31 /* SectionBackGroundDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAB72CFAC41300049E31 /* SectionBackGroundDecorationView.swift */; }; - 0841BABA2CFAE5BE00049E31 /* HomeHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAB92CFAE5BE00049E31 /* HomeHeaderView.swift */; }; - 0841BABC2CFB59E200049E31 /* String?+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BABB2CFB59E200049E31 /* String?+.swift */; }; - 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BABD2CFB5AA600049E31 /* Date?+.swift */; }; - 0841BAC32CFB600800049E31 /* TabbarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0841BAC22CFB600800049E31 /* TabbarController.swift */; }; - 086DD8C82CFDEA9200B97D3B /* UIView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8C72CFDEA9200B97D3B /* UIView+.swift */; }; - 086DD8CC2CFDFEA800B97D3B /* HomeListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8CB2CFDFEA800B97D3B /* HomeListController.swift */; }; - 086DD8CE2CFDFEB000B97D3B /* HomeListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8CD2CFDFEB000B97D3B /* HomeListView.swift */; }; - 086DD8D02CFDFEB900B97D3B /* HomeListReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8CF2CFDFEB900B97D3B /* HomeListReactor.swift */; }; - 086DD8D32CFDFF1500B97D3B /* HomePopUpType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8D22CFDFF1500B97D3B /* HomePopUpType.swift */; }; - 086DD8D62CFF182100B97D3B /* UserAPIEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8D52CFF182100B97D3B /* UserAPIEndPoint.swift */; }; - 086DD8D82CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8D72CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift */; }; - 086DD8DA2CFF194700B97D3B /* UserAPIRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8D92CFF194700B97D3B /* UserAPIRepositoryImpl.swift */; }; - 086DD8DE2CFF19C400B97D3B /* UserAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8DD2CFF19C400B97D3B /* UserAPIUseCaseImpl.swift */; }; - 086DD8E02CFF2C3700B97D3B /* UIImageView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8DF2CFF2C3700B97D3B /* UIImageView+.swift */; }; - 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD8E22CFF356300B97D3B /* HomeCardGridSection.swift */; }; - 086DD92A2D0086AA00B97D3B /* SearchController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD9292D0086AA00B97D3B /* SearchController.swift */; }; - 086DD92C2D0086B100B97D3B /* SearchReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD92B2D0086B100B97D3B /* SearchReactor.swift */; }; - 086DD92E2D0086B900B97D3B /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD92D2D0086B900B97D3B /* SearchView.swift */; }; - 086DD9302D0090E900B97D3B /* UITextField+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD92F2D0090E900B97D3B /* UITextField+.swift */; }; - 086DD9342D00962500B97D3B /* SearchTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD9332D00962500B97D3B /* SearchTitleSectionCell.swift */; }; - 086DD9362D00963900B97D3B /* SearchTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD9352D00963900B97D3B /* SearchTitleSection.swift */; }; - 086DD93B2D009A1C00B97D3B /* CancelableTagSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD93A2D009A1C00B97D3B /* CancelableTagSection.swift */; }; - 086DD93D2D009A2600B97D3B /* CancelableTagSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD93C2D009A2600B97D3B /* CancelableTagSectionCell.swift */; }; - 086DD9402D01EEEB00B97D3B /* SearchCountTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD93F2D01EEEB00B97D3B /* SearchCountTitleSection.swift */; }; - 086DD9422D01EEF700B97D3B /* SearchCountTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086DD9412D01EEF700B97D3B /* SearchCountTitleSectionCell.swift */; }; - 086F89C32D1E347700CA4FC9 /* CommentUserInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89C22D1E347700CA4FC9 /* CommentUserInfoView.swift */; }; - 086F89C52D1E347E00CA4FC9 /* CommentUserInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89C42D1E347E00CA4FC9 /* CommentUserInfoController.swift */; }; - 086F89C72D1E348400CA4FC9 /* CommentUserInfoReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89C62D1E348400CA4FC9 /* CommentUserInfoReactor.swift */; }; - 086F89CA2D1E42A700CA4FC9 /* CommentUserBlockView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89C92D1E42A700CA4FC9 /* CommentUserBlockView.swift */; }; - 086F89CC2D1E42B000CA4FC9 /* CommentUserBlockController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89CB2D1E42B000CA4FC9 /* CommentUserBlockController.swift */; }; - 086F89CE2D1E42B500CA4FC9 /* CommentUserBlockReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89CD2D1E42B500CA4FC9 /* CommentUserBlockReactor.swift */; }; - 086F89D02D1E60A100CA4FC9 /* PostUserBlockRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89CF2D1E60A100CA4FC9 /* PostUserBlockRequestDTO.swift */; }; - 086F89D32D1E6DA600CA4FC9 /* OtherUserCommentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89D22D1E6DA600CA4FC9 /* OtherUserCommentView.swift */; }; - 086F89D52D1E6DB100CA4FC9 /* OtherUserCommentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89D42D1E6DB100CA4FC9 /* OtherUserCommentController.swift */; }; - 086F89D72D1E6DB700CA4FC9 /* OtherUserCommentReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89D62D1E6DB700CA4FC9 /* OtherUserCommentReactor.swift */; }; - 086F89D92D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89D82D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift */; }; - 086F89DB2D1E7A6C00CA4FC9 /* GetOtherUserCommentedPopUpListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89DA2D1E7A6C00CA4FC9 /* GetOtherUserCommentedPopUpListResponseDTO.swift */; }; - 086F89E02D1E7CC700CA4FC9 /* GetOtherUserCommentedPopUpListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89DF2D1E7CC700CA4FC9 /* GetOtherUserCommentedPopUpListResponse.swift */; }; - 086F89E42D1FE91300CA4FC9 /* OtherUserCommentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89E32D1FE91300CA4FC9 /* OtherUserCommentSection.swift */; }; - 086F89E62D1FE91800CA4FC9 /* OtherUserCommentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89E52D1FE91800CA4FC9 /* OtherUserCommentSectionCell.swift */; }; - 086F89EA2D2009E300CA4FC9 /* SubLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89E92D2009E300CA4FC9 /* SubLoginView.swift */; }; - 086F89EC2D2009EB00CA4FC9 /* SubLoginController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89EB2D2009EB00CA4FC9 /* SubLoginController.swift */; }; - 086F89EE2D2009F100CA4FC9 /* SubLoginReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89ED2D2009F100CA4FC9 /* SubLoginReactor.swift */; }; - 086F89F12D2269D800CA4FC9 /* MyPageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89F02D2269D800CA4FC9 /* MyPageView.swift */; }; - 086F89F32D2269DE00CA4FC9 /* MyPageController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89F22D2269DE00CA4FC9 /* MyPageController.swift */; }; - 086F89F52D2269E300CA4FC9 /* MyPageReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89F42D2269E300CA4FC9 /* MyPageReactor.swift */; }; - 086F89F72D226DF600CA4FC9 /* GetMyPageResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89F62D226DF600CA4FC9 /* GetMyPageResponseDTO.swift */; }; - 086F89F92D226EEB00CA4FC9 /* GetMyPageResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F89F82D226EEB00CA4FC9 /* GetMyPageResponse.swift */; }; - 086F8A052D23CB3300CA4FC9 /* MyPageProfileSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A042D23CB3300CA4FC9 /* MyPageProfileSection.swift */; }; - 086F8A072D23CB3800CA4FC9 /* MyPageProfileSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A062D23CB3800CA4FC9 /* MyPageProfileSectionCell.swift */; }; - 086F8A0A2D2621EE00CA4FC9 /* MyPageMyCommentTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A092D2621EE00CA4FC9 /* MyPageMyCommentTitleSection.swift */; }; - 086F8A0C2D2621F400CA4FC9 /* MyPageMyCommentTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A0B2D2621F400CA4FC9 /* MyPageMyCommentTitleSectionCell.swift */; }; - 086F8A0F2D26297900CA4FC9 /* MyPageCommentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A0E2D26297900CA4FC9 /* MyPageCommentSection.swift */; }; - 086F8A112D26297D00CA4FC9 /* MyPageCommentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A102D26297D00CA4FC9 /* MyPageCommentSectionCell.swift */; }; - 086F8A182D265C5F00CA4FC9 /* MyPageListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A172D265C5F00CA4FC9 /* MyPageListSection.swift */; }; - 086F8A1A2D265C6300CA4FC9 /* MyPageListSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 086F8A192D265C6300CA4FC9 /* MyPageListSectionCell.swift */; }; 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 088DE2432D104EE70030FA9E /* SwiftSoup */; }; - 088DE24A2D12F3360030FA9E /* DetailInfoSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2492D12F3360030FA9E /* DetailInfoSection.swift */; }; - 088DE24C2D12F33B0030FA9E /* DetailInfoSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE24B2D12F33B0030FA9E /* DetailInfoSectionCell.swift */; }; - 088DE24F2D13019A0030FA9E /* DetailCommentTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE24E2D13019A0030FA9E /* DetailCommentTitleSection.swift */; }; - 088DE2512D13019E0030FA9E /* DetailCommentTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2502D13019E0030FA9E /* DetailCommentTitleSectionCell.swift */; }; - 088DE2542D144A7E0030FA9E /* DetailCommentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2532D144A7E0030FA9E /* DetailCommentSection.swift */; }; - 088DE2562D144A830030FA9E /* DetailCommentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2552D144A830030FA9E /* DetailCommentSectionCell.swift */; }; - 088DE2582D144B0F0030FA9E /* DetailCommentProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2572D144B0F0030FA9E /* DetailCommentProfileView.swift */; }; - 088DE25A2D1458620030FA9E /* DetailCommentImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE2592D1458620030FA9E /* DetailCommentImageCell.swift */; }; - 088DE25D2D145E3A0030FA9E /* DetailSimilarSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE25C2D145E3A0030FA9E /* DetailSimilarSection.swift */; }; - 088DE25F2D145E3F0030FA9E /* DetailSimilarSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 088DE25E2D145E3F0030FA9E /* DetailSimilarSectionCell.swift */; }; - 089952422D031E650022AEF9 /* SearchSortedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952412D031E650022AEF9 /* SearchSortedController.swift */; }; - 089952442D031E6D0022AEF9 /* SearchSortedReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952432D031E6D0022AEF9 /* SearchSortedReactor.swift */; }; - 089952462D031E740022AEF9 /* SearchSortedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952452D031E740022AEF9 /* SearchSortedView.swift */; }; - 089952492D033A1C0022AEF9 /* PopUpAPIEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952482D033A1C0022AEF9 /* PopUpAPIEndPoint.swift */; }; - 0899524B2D033A9C0022AEF9 /* GetClosePopUpListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899524A2D033A9C0022AEF9 /* GetClosePopUpListResponseDTO.swift */; }; - 0899524D2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899524C2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift */; }; - 0899524F2D033B5A0022AEF9 /* GetSearchPopUpListRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899524E2D033B5A0022AEF9 /* GetSearchPopUpListRequestDTO.swift */; }; - 089952512D033C410022AEF9 /* GetSearchBottomPopUpListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952502D033C410022AEF9 /* GetSearchBottomPopUpListResponse.swift */; }; - 089952532D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952522D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift */; }; - 089952552D033D480022AEF9 /* PopUpAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952542D033D480022AEF9 /* PopUpAPIUseCaseImpl.swift */; }; - 089952582D0347AC0022AEF9 /* SearchCategoryController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952572D0347AC0022AEF9 /* SearchCategoryController.swift */; }; - 0899525A2D0347B40022AEF9 /* SearchCategoryReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952592D0347B40022AEF9 /* SearchCategoryReactor.swift */; }; - 0899525C2D0347BD0022AEF9 /* SearchCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899525B2D0347BD0022AEF9 /* SearchCategoryView.swift */; }; - 089952602D0366C40022AEF9 /* SearchMainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899525F2D0366C40022AEF9 /* SearchMainController.swift */; }; - 089952622D0366D30022AEF9 /* SearchMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952612D0366D30022AEF9 /* SearchMainView.swift */; }; - 089952642D0366DA0022AEF9 /* SearchMainReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952632D0366DA0022AEF9 /* SearchMainReactor.swift */; }; - 089952662D046CCD0022AEF9 /* SearchResultController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952652D046CCD0022AEF9 /* SearchResultController.swift */; }; - 089952682D046CD80022AEF9 /* SearchResultReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952672D046CD80022AEF9 /* SearchResultReactor.swift */; }; - 0899526A2D046CDE0022AEF9 /* SearchResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952692D046CDE0022AEF9 /* SearchResultView.swift */; }; - 0899526C2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899526B2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift */; }; - 0899526E2D0474340022AEF9 /* GetSearchPopUpListResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0899526D2D0474340022AEF9 /* GetSearchPopUpListResponse.swift */; }; - 089952732D0475E90022AEF9 /* SearchResultCountSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */; }; - 089952752D0475F20022AEF9 /* SearchResultCountSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */; }; - 089B4FD82D9A57AE00FC0CC3 /* ImageLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */; }; - 089B4FDF2D9A8F9A00FC0CC3 /* MemoryStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */; }; - 08A2E46C2D15BC5000102313 /* CommentLikeRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */; }; - 08A2E4792D1B06A300102313 /* ImageDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4782D1B06A300102313 /* ImageDetailView.swift */; }; - 08A2E47B2D1B06AA00102313 /* ImageDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */; }; - 08A2E47D2D1B06B000102313 /* ImageDetailReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E47C2D1B06B000102313 /* ImageDetailReactor.swift */; }; - 08A2E4802D1BCDE300102313 /* CommentDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E47F2D1BCDE300102313 /* CommentDetailView.swift */; }; - 08A2E4822D1BCDEA00102313 /* CommentDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4812D1BCDEA00102313 /* CommentDetailController.swift */; }; - 08A2E4842D1BCDEF00102313 /* CommentDetailReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4832D1BCDEF00102313 /* CommentDetailReactor.swift */; }; - 08A2E4862D1BD85C00102313 /* CommentDetailImageSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4852D1BD85C00102313 /* CommentDetailImageSection.swift */; }; - 08A2E48A2D1BDA8400102313 /* CommentDetailContentSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4892D1BDA8400102313 /* CommentDetailContentSection.swift */; }; - 08A2E48C2D1BDA8A00102313 /* CommentDetailContentSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E48B2D1BDA8A00102313 /* CommentDetailContentSectionCell.swift */; }; - 08A2E48F2D1BF6E500102313 /* CommentListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E48E2D1BF6E500102313 /* CommentListView.swift */; }; - 08A2E4912D1BF6EA00102313 /* CommentListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4902D1BF6EA00102313 /* CommentListController.swift */; }; - 08A2E4932D1BF6EF00102313 /* CommentListReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4922D1BF6EF00102313 /* CommentListReactor.swift */; }; - 08A2E4952D1C078300102313 /* GetPopUpCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4942D1C078300102313 /* GetPopUpCommentRequestDTO.swift */; }; - 08A2E4972D1C07F500102313 /* GetPopUpCommentResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4962D1C07F500102313 /* GetPopUpCommentResponseDTO.swift */; }; - 08A2E4992D1C08D600102313 /* GetPopUpCommentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E4982D1C08D600102313 /* GetPopUpCommentResponse.swift */; }; - 08A2E49D2D1C416800102313 /* CommentListTitleSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E49C2D1C416800102313 /* CommentListTitleSection.swift */; }; - 08A2E49F2D1C417000102313 /* CommentListTitleSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08A2E49E2D1C417000102313 /* CommentListTitleSectionCell.swift */; }; - 08B191372CF366680057BC04 /* UICollectionViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191342CF366670057BC04 /* UICollectionViewCell+.swift */; }; - 08B191382CF366680057BC04 /* UICollectionReusableView+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191352CF366670057BC04 /* UICollectionReusableView+.swift */; }; - 08B191392CF366680057BC04 /* UITableViewCell+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191362CF366670057BC04 /* UITableViewCell+.swift */; }; - 08B1913B2CF366A00057BC04 /* UIApplication+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1913A2CF366A00057BC04 /* UIApplication+.swift */; }; - 08B1913F2CF367FA0057BC04 /* UIColor+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1913E2CF367FA0057BC04 /* UIColor+.swift */; }; - 08B191412CF367FF0057BC04 /* UIFont+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191402CF367FF0057BC04 /* UIFont+.swift */; }; - 08B1914B2CF41D690057BC04 /* GothicA1-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191422CF41D680057BC04 /* GothicA1-Bold.ttf */; }; - 08B1914C2CF41D690057BC04 /* GothicA1-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191432CF41D680057BC04 /* GothicA1-Light.ttf */; }; - 08B1914D2CF41D690057BC04 /* GothicA1-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191442CF41D680057BC04 /* GothicA1-Medium.ttf */; }; - 08B1914E2CF41D690057BC04 /* GothicA1-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191452CF41D680057BC04 /* GothicA1-Regular.ttf */; }; - 08B1914F2CF41D690057BC04 /* Poppins-Bold.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191462CF41D680057BC04 /* Poppins-Bold.ttf */; }; - 08B191502CF41D690057BC04 /* Poppins-Light.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191472CF41D680057BC04 /* Poppins-Light.ttf */; }; - 08B191512CF41D690057BC04 /* Poppins-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191482CF41D680057BC04 /* Poppins-Medium.ttf */; }; - 08B191522CF41D690057BC04 /* Poppins-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 08B191492CF41D680057BC04 /* Poppins-Regular.ttf */; }; - 08B191552CF41D6F0057BC04 /* PP_loading.json in Resources */ = {isa = PBXBuildFile; fileRef = 08B191532CF41D6F0057BC04 /* PP_loading.json */; }; - 08B191562CF41D6F0057BC04 /* PP_splash.json in Resources */ = {isa = PBXBuildFile; fileRef = 08B191542CF41D6F0057BC04 /* PP_splash.json */; }; - 08B191592CF41E610057BC04 /* SignUpMainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191582CF41E610057BC04 /* SignUpMainController.swift */; }; - 08B1915B2CF41E690057BC04 /* SignUpMainReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1915A2CF41E690057BC04 /* SignUpMainReactor.swift */; }; - 08B1915D2CF41E6F0057BC04 /* SignUpMainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1915C2CF41E6F0057BC04 /* SignUpMainView.swift */; }; - 08B191612CF430E70057BC04 /* PPProgressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191602CF430E70057BC04 /* PPProgressView.swift */; }; - 08B191632CF430F30057BC04 /* PPProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191622CF430F30057BC04 /* PPProgressIndicator.swift */; }; - 08B191672CF432220057BC04 /* PPCancelHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191662CF432220057BC04 /* PPCancelHeaderView.swift */; }; - 08B1916A2CF434B80057BC04 /* SignUpStep1Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191692CF434B80057BC04 /* SignUpStep1Controller.swift */; }; - 08B1916C2CF434C30057BC04 /* SignUpStep1View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1916B2CF434C30057BC04 /* SignUpStep1View.swift */; }; - 08B1916E2CF434CF0057BC04 /* SignUpStep1Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1916D2CF434CF0057BC04 /* SignUpStep1Reactor.swift */; }; - 08B191712CF4398D0057BC04 /* PPLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191702CF4398D0057BC04 /* PPLabel.swift */; }; - 08B191742CF43DF40057BC04 /* SignUpCheckBoxButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191732CF43DF40057BC04 /* SignUpCheckBoxButton.swift */; }; - 08B191762CF440C40057BC04 /* PPButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191752CF440C40057BC04 /* PPButton.swift */; }; - 08B191782CF442230057BC04 /* UIImage+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191772CF442230057BC04 /* UIImage+.swift */; }; - 08B1917A2CF452B30057BC04 /* SignUpTermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191792CF452B30057BC04 /* SignUpTermsView.swift */; }; - 08B1917D2CF46DE30057BC04 /* TermsDetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1917C2CF46DE30057BC04 /* TermsDetailController.swift */; }; - 08B1917F2CF46DF20057BC04 /* TermsDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1917E2CF46DF20057BC04 /* TermsDetailView.swift */; }; - 08B191822CF48A7B0057BC04 /* SignUpStep2Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191812CF48A7B0057BC04 /* SignUpStep2Controller.swift */; }; - 08B191842CF48A820057BC04 /* SignUpStep2View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191832CF48A820057BC04 /* SignUpStep2View.swift */; }; - 08B191862CF48A8B0057BC04 /* SignUpStep2Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191852CF48A8B0057BC04 /* SignUpStep2Reactor.swift */; }; - 08B191882CF48FAE0057BC04 /* NickNameState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191872CF48FAE0057BC04 /* NickNameState.swift */; }; - 08B1918D2CF49FF70057BC04 /* SignUpStep3View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1918C2CF49FF70057BC04 /* SignUpStep3View.swift */; }; - 08B1918F2CF4A0020057BC04 /* SignUpStep3Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1918E2CF4A0020057BC04 /* SignUpStep3Controller.swift */; }; - 08B191912CF4A00E0057BC04 /* SignUpStep3Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191902CF4A00E0057BC04 /* SignUpStep3Reactor.swift */; }; - 08B191942CF4A0F00057BC04 /* SignUpStep4View.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191932CF4A0F00057BC04 /* SignUpStep4View.swift */; }; - 08B191962CF4A0FA0057BC04 /* SignUpStep4Controller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191952CF4A0FA0057BC04 /* SignUpStep4Controller.swift */; }; - 08B191982CF4A1010057BC04 /* SignUpStep4Reactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191972CF4A1010057BC04 /* SignUpStep4Reactor.swift */; }; - 08B1919C2CF4A77C0057BC04 /* TagSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1919B2CF4A77C0057BC04 /* TagSection.swift */; }; - 08B1919E2CF4A7830057BC04 /* TagSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1919D2CF4A7830057BC04 /* TagSectionCell.swift */; }; - 08B191A02CF4AA0E0057BC04 /* Reactive+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B1919F2CF4AA0E0057BC04 /* Reactive+.swift */; }; - 08B191A22CF4AE890057BC04 /* ToastMaker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191A12CF4AE890057BC04 /* ToastMaker.swift */; }; - 08B191A42CF5A7030057BC04 /* PPSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191A32CF5A7030057BC04 /* PPSegmentedControl.swift */; }; - 08B191A72CF5A9430057BC04 /* AgeSelectedButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191A62CF5A9430057BC04 /* AgeSelectedButton.swift */; }; - 08B191AC2CF5BF9D0057BC04 /* AgeSelectedController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191AB2CF5BF9D0057BC04 /* AgeSelectedController.swift */; }; - 08B191AE2CF5BFA60057BC04 /* AgeSelectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191AD2CF5BFA60057BC04 /* AgeSelectedView.swift */; }; - 08B191B02CF5BFAE0057BC04 /* AgeSelectedReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191AF2CF5BFAE0057BC04 /* AgeSelectedReactor.swift */; }; - 08B191B22CF5C0A60057BC04 /* PPPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B12CF5C0A60057BC04 /* PPPicker.swift */; }; - 08B191B42CF609260057BC04 /* KakaoLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B32CF609260057BC04 /* KakaoLoginService.swift */; }; - 08B191B62CF6092B0057BC04 /* AppleLoginService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */; }; - 08B191B82CF6092F0057BC04 /* AuthServiceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */; }; - 08B191C22CF615CA0057BC04 /* KeyPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08B191C12CF615CA0057BC04 /* KeyPath.swift */; }; - 08CBEA032D38989E00248007 /* PostTokenReissueResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */; }; - 08CBEA062D38991600248007 /* PostTokenReissueResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */; }; - 08CBEA0B2D38DBD600248007 /* LastLoginView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA0A2D38DBD600248007 /* LastLoginView.swift */; }; - 08CBEA0D2D38ED0D00248007 /* CountButtonView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA0C2D38ED0D00248007 /* CountButtonView.swift */; }; - 08CBEA3A2D3FABE100248007 /* ToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA392D3FABE100248007 /* ToastView.swift */; }; - 08CBEA3C2D3FABED00248007 /* BookMarkToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA3B2D3FABED00248007 /* BookMarkToastView.swift */; }; - 08CBEA3E2D3FF6A100248007 /* PopUpCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CBEA3D2D3FF6A100248007 /* PopUpCardView.swift */; }; - 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */; }; - 08DC61F32CF75037002A2F44 /* KeyChainService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F22CF75037002A2F44 /* KeyChainService.swift */; }; - 08DC61F52CF765B5002A2F44 /* UserDefaultService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */; }; - 08DC61F82CF76843002A2F44 /* SignUpCompleteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F72CF76843002A2F44 /* SignUpCompleteView.swift */; }; - 08DC61FA2CF7684F002A2F44 /* SignUpCompleteController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61F92CF7684F002A2F44 /* SignUpCompleteController.swift */; }; - 08DC61FC2CF76862002A2F44 /* SignUpCompleteReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC61FB2CF76862002A2F44 /* SignUpCompleteReactor.swift */; }; - 08DC62032CF8AC06002A2F44 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62022CF8AC06002A2F44 /* HomeView.swift */; }; - 08DC62052CF8AC0E002A2F44 /* HomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62042CF8AC0E002A2F44 /* HomeController.swift */; }; - 08DC62072CF8AC14002A2F44 /* HomeReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62062CF8AC14002A2F44 /* HomeReactor.swift */; }; - 08DC620B2CF8AE0F002A2F44 /* ImageBannerSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC620A2CF8AE0F002A2F44 /* ImageBannerSection.swift */; }; - 08DC620D2CF8AE16002A2F44 /* ImageBannerSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC620C2CF8AE16002A2F44 /* ImageBannerSectionCell.swift */; }; - 08DC62112CF8B446002A2F44 /* SortedRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DC62102CF8B446002A2F44 /* SortedRequestDTO.swift */; }; - 08DE8A0D2D5236840049BCAC /* PutCommentRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A0C2D5236840049BCAC /* PutCommentRequestDTO.swift */; }; - 08DE8A102D5255110049BCAC /* DetailEmptyCommetSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A0F2D5255110049BCAC /* DetailEmptyCommetSection.swift */; }; - 08DE8A122D5255180049BCAC /* DetailEmptyCommetSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A112D5255180049BCAC /* DetailEmptyCommetSectionCell.swift */; }; - 08DE8A182D525BA20049BCAC /* Terms.plist in Resources */ = {isa = PBXBuildFile; fileRef = 08DE8A172D525BA20049BCAC /* Terms.plist */; }; - 08DE8A1B2D5261DE0049BCAC /* MyPageTermsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A1A2D5261DE0049BCAC /* MyPageTermsController.swift */; }; - 08DE8A1D2D5261E70049BCAC /* MyPageTermsReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A1C2D5261E70049BCAC /* MyPageTermsReactor.swift */; }; - 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A3E2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift */; }; - 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142B2D994A3A00DFD08F /* KakaoSDK */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */; }; 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; - 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */; }; - 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */; }; - 4E685ECE2D12CEB6001EF91C /* BalloonBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAA2D12CEB6001EF91C /* BalloonBackgroundView.swift */; }; - 4E685ECF2D12CEB6001EF91C /* BalloonChipCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAB2D12CEB6001EF91C /* BalloonChipCell.swift */; }; - 4E685ED12D12CEB6001EF91C /* FilterBottomSheetReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAD2D12CEB6001EF91C /* FilterBottomSheetReactor.swift */; }; - 4E685ED22D12CEB6001EF91C /* FilterBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAE2D12CEB6001EF91C /* FilterBottomSheetView.swift */; }; - 4E685ED32D12CEB6001EF91C /* FilterBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EAF2D12CEB6001EF91C /* FilterBottomSheetViewController.swift */; }; - 4E685ED42D12CEB6001EF91C /* FilterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EB02D12CEB6001EF91C /* FilterCell.swift */; }; - 4E685ED52D12CEB6001EF91C /* FilterChip.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EB12D12CEB6001EF91C /* FilterChip.swift */; }; - 4E685ED62D12CEB6001EF91C /* FilterChipsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EB22D12CEB6001EF91C /* FilterChipsView.swift */; }; - 4E685ED92D12CEB6001EF91C /* MapRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EB62D12CEB6001EF91C /* MapRepository.swift */; }; - 4E685EDA2D12CEB6001EF91C /* MapUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EB82D12CEB6001EF91C /* MapUseCase.swift */; }; - 4E685EDB2D12CEB6001EF91C /* MapAPIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBA2D12CEB6001EF91C /* MapAPIEndpoint.swift */; }; - 4E685EDD2D12CEB6001EF91C /* MapPopUpStoreDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBC2D12CEB6001EF91C /* MapPopUpStoreDTO.swift */; }; - 4E685EDE2D12CEB6001EF91C /* MapPopupCarouselView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBE2D12CEB6001EF91C /* MapPopupCarouselView.swift */; }; - 4E685EDF2D12CEB6001EF91C /* PopupCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBF2D12CEB6001EF91C /* PopupCardCell.swift */; }; - 4E685EE02D12CEB6001EF91C /* StoreListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC12D12CEB6001EF91C /* StoreListCell.swift */; }; - 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC22D12CEB6001EF91C /* StoreListReactor.swift */; }; - 4E685EE22D12CEB6001EF91C /* StoreListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC32D12CEB6001EF91C /* StoreListView.swift */; }; - 4E685EE32D12CEB6001EF91C /* StoreListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC42D12CEB6001EF91C /* StoreListViewController.swift */; }; - 4E685EE42D12CEB6001EF91C /* MapFilterChips.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC62D12CEB6001EF91C /* MapFilterChips.swift */; }; - 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC72D12CEB6001EF91C /* MapMarker.swift */; }; - 4E685EE62D12CEB6001EF91C /* MapReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC82D12CEB6001EF91C /* MapReactor.swift */; }; - 4E685EE72D12CEB6001EF91C /* MapSearchInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EC92D12CEB6001EF91C /* MapSearchInput.swift */; }; - 4E685EE92D12CEB6001EF91C /* MapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685ECB2D12CEB6001EF91C /* MapView.swift */; }; - 4E685EEA2D12CEB6001EF91C /* MapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */; }; - 4E6A06702D42A96100B2A658 /* FullScreenMapViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6A066F2D42A96100B2A658 /* FullScreenMapViewController.swift */; }; - 4E6C07062D4B6E56008A962A /* RegionDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6C07052D4B6E56008A962A /* RegionDefinitions.swift */; }; - 4E6C07082D4B6E74008A962A /* ClusteringModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6C07072D4B6E74008A962A /* ClusteringModels.swift */; }; - 4E6C070A2D4B6E81008A962A /* ClusteringManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6C07092D4B6E81008A962A /* ClusteringManager.swift */; }; - 4E6CA4852D34D6ED0034D09A /* AdminBottomSheetReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E6CA4842D34D6ED0034D09A /* AdminBottomSheetReactor.swift */; }; - 4E755B1D2D2B9AD300ADFB21 /* GetAdminPopUpStoreListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B1C2D2B9AD300ADFB21 /* GetAdminPopUpStoreListResponseDTO.swift */; }; - 4E755B1F2D2B9AE500ADFB21 /* AdminAPIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B1E2D2B9AE500ADFB21 /* AdminAPIEndpoint.swift */; }; - 4E755B212D2B9BAB00ADFB21 /* AdminResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B202D2B9BAB00ADFB21 /* AdminResponseDTO.swift */; }; - 4E755B232D2B9C5D00ADFB21 /* AdminViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B222D2B9C5D00ADFB21 /* AdminViewController.swift */; }; - 4E755B252D2B9C6C00ADFB21 /* AdminView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B242D2B9C6C00ADFB21 /* AdminView.swift */; }; - 4E755B292D2BA65A00ADFB21 /* AdminReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B282D2BA65A00ADFB21 /* AdminReactor.swift */; }; - 4E755B2B2D2BA76E00ADFB21 /* AdminUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B2A2D2BA76E00ADFB21 /* AdminUseCase.swift */; }; - 4E7823A82D2E84E800AC5110 /* AdminRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B2E2D2BA7FB00ADFB21 /* AdminRepository.swift */; }; - 4E7823A92D2E84FB00AC5110 /* AdminStoreCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E755B262D2B9C7C00ADFB21 /* AdminStoreCell.swift */; }; - 4E78706E2D37CB1900465FC9 /* ProfileEditListButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 081898BF2D2FBD130067BF01 /* ProfileEditListButton.swift */; }; - 4E78706F2D37CB2200465FC9 /* PopUpStoreRegisterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12802D2BE0A6006744D6 /* PopUpStoreRegisterViewController.swift */; }; - 4E8AA29D2D59A2340029DF75 /* MarkerTooltipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E8AA29C2D59A2340029DF75 /* MarkerTooltipView.swift */; }; - 4E9790C52D40E13500210499 /* MapGuideViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9790C42D40E13500210499 /* MapGuideViewController.swift */; }; - 4E9A46602D55D1270010578A /* MapUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9A465F2D55D1270010578A /* MapUtilities.swift */; }; - 4E9C12782D2BC7A0006744D6 /* AdminBottomSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12772D2BC7A0006744D6 /* AdminBottomSheetView.swift */; }; - 4E9C127A2D2BC811006744D6 /* AdminBottomSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E9C12792D2BC811006744D6 /* AdminBottomSheetViewController.swift */; }; - 4EA2C93D2D424D3300F4D97C /* MapDirectionRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA2C93C2D424D3300F4D97C /* MapDirectionRepository.swift */; }; - 4EA2C93F2D424D7400F4D97C /* MapDirectionUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA2C93E2D424D7400F4D97C /* MapDirectionUseCase.swift */; }; - 4EA2C9412D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA2C9402D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift */; }; - 4EA2C9432D424DF900F4D97C /* FindDirectionEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA2C9422D424DF900F4D97C /* FindDirectionEndPoint.swift */; }; - 4EA9989A2D21C2FC009DC30B /* StoreListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EA998992D21C2FC009DC30B /* StoreListSection.swift */; }; 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA9989C2D21C404009DC30B /* RxDataSources */; }; - 4EAB809D2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */; }; - 4EAB809F2D3F8EF50041AF30 /* ViewportBounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */; }; - 4EDDEFB42D2D285900CFAFA5 /* DateTimePickerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */; }; - 4EDE57032D5E70650014D924 /* LocationPermissionBottomSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; - 4EE5A3D32D40E4A600A2469A /* MapGuideReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */; }; - 4EEA13072DA7CDDA00775256 /* PopUpImagesCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */; }; - 4EEA1D8F2D352012003E7DE9 /* ImageCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */; }; - 4EEA1D912D352027003E7DE9 /* ExtendedImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */; }; - 4EECA3942D56770B00A07CCA /* MapPopUpStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E685EBB2D12CEB6001EF91C /* MapPopUpStore.swift */; }; - 4EED9BAC2D22730400B288E7 /* FilterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4EED9BAB2D22730400B288E7 /* FilterType.swift */; }; - BD226D512CF6DB290038C984 /* PPReturnHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD226D502CF6DB290038C984 /* PPReturnHeaderView.swift */; }; - BD9103612CF6149D00BBCCAE /* AuthAPIEndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91034E2CF6149D00BBCCAE /* AuthAPIEndPoint.swift */; }; - BD9103622CF6149D00BBCCAE /* LoginResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91034F2CF6149D00BBCCAE /* LoginResponseDTO.swift */; }; - BD9103632CF6149D00BBCCAE /* BannerPopUpStoreDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103512CF6149D00BBCCAE /* BannerPopUpStoreDTO.swift */; }; - BD9103642CF6149D00BBCCAE /* GetHomeInfoResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103522CF6149D00BBCCAE /* GetHomeInfoResponseDTO.swift */; }; - BD9103652CF6149D00BBCCAE /* HomeAPIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103532CF6149D00BBCCAE /* HomeAPIEndpoint.swift */; }; - BD9103662CF6149D00BBCCAE /* PopUpStoreResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103542CF6149D00BBCCAE /* PopUpStoreResponseDTO.swift */; }; - BD9103682CF6149D00BBCCAE /* AuthAPIRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103572CF6149D00BBCCAE /* AuthAPIRepositoryImpl.swift */; }; - BD9103692CF6149D00BBCCAE /* HomeAPIRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103582CF6149D00BBCCAE /* HomeAPIRepository.swift */; }; - BD91036A2CF6149D00BBCCAE /* SignUpRepositoryImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103592CF6149D00BBCCAE /* SignUpRepositoryImpl.swift */; }; - BD91036B2CF6149D00BBCCAE /* CheckNickNameRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91035B2CF6149D00BBCCAE /* CheckNickNameRequestDTO.swift */; }; - BD91036C2CF6149D00BBCCAE /* GetCategoryListResponseDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91035C2CF6149D00BBCCAE /* GetCategoryListResponseDTO.swift */; }; - BD91036D2CF6149D00BBCCAE /* SignUpAPIEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91035D2CF6149D00BBCCAE /* SignUpAPIEndpoint.swift */; }; - BD91036E2CF6149D00BBCCAE /* SignUpRequestDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91035E2CF6149D00BBCCAE /* SignUpRequestDTO.swift */; }; - BD9103812CF614A900BBCCAE /* HomeAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103702CF614A900BBCCAE /* HomeAPIUseCaseImpl.swift */; }; - BD9103832CF614A900BBCCAE /* SignUpAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103722CF614A900BBCCAE /* SignUpAPIUseCaseImpl.swift */; }; - BD9103852CF614A900BBCCAE /* AuthAPIUseCaseImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103742CF614A900BBCCAE /* AuthAPIUseCaseImpl.swift */; }; - BD9103862CF614A900BBCCAE /* BannerPopUpStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103762CF614A900BBCCAE /* BannerPopUpStore.swift */; }; - BD9103872CF614A900BBCCAE /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103772CF614A900BBCCAE /* Category.swift */; }; - BD9103882CF614A900BBCCAE /* GetHomeInfoResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103782CF614A900BBCCAE /* GetHomeInfoResponse.swift */; }; - BD9103892CF614A900BBCCAE /* PopUpStoreResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103792CF614A900BBCCAE /* PopUpStoreResponse.swift */; }; - BD91038A2CF614A900BBCCAE /* LoginResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91037A2CF614A900BBCCAE /* LoginResponse.swift */; }; - BD91038B2CF614A900BBCCAE /* AuthRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91037C2CF614A900BBCCAE /* AuthRepository.swift */; }; - BD9103922CF6166800BBCCAE /* SplashView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD91038E2CF6166800BBCCAE /* SplashView.swift */; }; - BD9103932CF6166800BBCCAE /* SplashController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD9103902CF6166800BBCCAE /* SplashController.swift */; }; - BDCA41C12CF35AC0005EECF6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41C02CF35AC0005EECF6 /* AppDelegate.swift */; }; - BDCA41C32CF35AC0005EECF6 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDCA41C22CF35AC0005EECF6 /* SceneDelegate.swift */; }; - BDCA41CA2CF35AC1005EECF6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BDCA41C92CF35AC1005EECF6 /* Assets.xcassets */; }; - BDCA41CD2CF35AC1005EECF6 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = BDCA41CC2CF35AC1005EECF6 /* Base */; }; BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F12CF35D0D005EECF6 /* SnapKit */; }; BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F72CF35D9A005EECF6 /* RxSwift */; }; BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41FD2CF35EE7005EECF6 /* ReactorKit */; }; @@ -476,2448 +30,70 @@ /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; - 057151572D9D2E0800260615 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSection.swift; sourceTree = ""; }; - 0818988F2D295DC80067BF01 /* MyPageLogoutSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageLogoutSectionCell.swift; sourceTree = ""; }; - 081898932D2965C20067BF01 /* ProfileEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditController.swift; sourceTree = ""; }; - 081898952D2965C90067BF01 /* ProfileEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditView.swift; sourceTree = ""; }; - 081898972D2965D20067BF01 /* ProfileEditReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditReactor.swift; sourceTree = ""; }; - 0818989B2D2BAA570067BF01 /* WithdrawlCheckModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCheckModalView.swift; sourceTree = ""; }; - 0818989D2D2BAA610067BF01 /* WithdrawlCheckModalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCheckModalController.swift; sourceTree = ""; }; - 0818989F2D2BAA670067BF01 /* WithdrawlCheckModalReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCheckModalReactor.swift; sourceTree = ""; }; - 081898A22D2CC0110067BF01 /* WithdrawlReasonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlReasonView.swift; sourceTree = ""; }; - 081898A42D2CC0180067BF01 /* WithdrawlReasonController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlReasonController.swift; sourceTree = ""; }; - 081898A62D2CC01D0067BF01 /* WithdrawlReasonReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlReasonReactor.swift; sourceTree = ""; }; - 081898A92D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCheckSectionCell.swift; sourceTree = ""; }; - 081898AB2D2CEA940067BF01 /* WithdrawlCheckSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCheckSection.swift; sourceTree = ""; }; - 081898AD2D2CFC230067BF01 /* GetWithdrawlListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetWithdrawlListResponseDTO.swift; sourceTree = ""; }; - 081898AF2D2CFCA40067BF01 /* GetWithdrawlListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetWithdrawlListResponse.swift; sourceTree = ""; }; - 081898B22D2D20D70067BF01 /* WithdrawlCompleteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCompleteView.swift; sourceTree = ""; }; - 081898B42D2D20E30067BF01 /* WithdrawlCompleteController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WithdrawlCompleteController.swift; sourceTree = ""; }; - 081898B62D2D23A90067BF01 /* UINavigationController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController+.swift"; sourceTree = ""; }; - 081898B92D2E5F4C0067BF01 /* MyCommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentView.swift; sourceTree = ""; }; - 081898BB2D2E5F510067BF01 /* MyCommentReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentReactor.swift; sourceTree = ""; }; - 081898BD2D2E5F590067BF01 /* MyCommentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentController.swift; sourceTree = ""; }; - 081898BF2D2FBD130067BF01 /* ProfileEditListButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditListButton.swift; sourceTree = ""; }; - 081898C22D30AE2C0067BF01 /* GetMyProfileResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyProfileResponseDTO.swift; sourceTree = ""; }; - 081898C42D30AEF40067BF01 /* GetMyProfileResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyProfileResponse.swift; sourceTree = ""; }; - 081898C92D30D5BA0067BF01 /* InfoEditModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoEditModalView.swift; sourceTree = ""; }; - 081898CB2D30D5BF0067BF01 /* InfoEditModalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoEditModalController.swift; sourceTree = ""; }; - 081898CD2D30D5C60067BF01 /* InfoEditModalReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoEditModalReactor.swift; sourceTree = ""; }; - 081898CF2D30EA900067BF01 /* PutUserTailoredInfoRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutUserTailoredInfoRequestDTO.swift; sourceTree = ""; }; - 081898D12D30F57D0067BF01 /* CategoryEditModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryEditModalView.swift; sourceTree = ""; }; - 081898D32D30F5840067BF01 /* CategoryEditModalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryEditModalController.swift; sourceTree = ""; }; - 081898D52D30F58A0067BF01 /* CategoryEditModalReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoryEditModalReactor.swift; sourceTree = ""; }; - 081898D72D310C160067BF01 /* PutUserCategoryRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutUserCategoryRequestDTO.swift; sourceTree = ""; }; - 081898D92D32559B0067BF01 /* PutUserProfileRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutUserProfileRequestDTO.swift; sourceTree = ""; }; - 081898DB2D326DC10067BF01 /* IntroState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntroState.swift; sourceTree = ""; }; - 081898DF2D338F9C0067BF01 /* ListCountButtonSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCountButtonSection.swift; sourceTree = ""; }; - 081898E12D338FA40067BF01 /* ListCountButtonSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCountButtonSectionCell.swift; sourceTree = ""; }; - 081898E32D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyCommentedPopUpResponseDTO.swift; sourceTree = ""; }; - 081898E52D3391CB0067BF01 /* GetMyCommentResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyCommentResponse.swift; sourceTree = ""; }; - 081898E72D3392480067BF01 /* GetMyCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyCommentRequestDTO.swift; sourceTree = ""; }; - 081898EB2D33A3960067BF01 /* MyCommentSortedModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentSortedModalView.swift; sourceTree = ""; }; - 081898ED2D33A39D0067BF01 /* MyCommentSortedModalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentSortedModalController.swift; sourceTree = ""; }; - 081898EF2D33A3A30067BF01 /* MyCommentSortedModalReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentSortedModalReactor.swift; sourceTree = ""; }; - 081898F22D33D6AC0067BF01 /* BlockUserManageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockUserManageView.swift; sourceTree = ""; }; - 081898F42D33D6B10067BF01 /* BlockUserManageController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockUserManageController.swift; sourceTree = ""; }; - 081898F62D33D6B70067BF01 /* BlockUserManageReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockUserManageReactor.swift; sourceTree = ""; }; - 081898FA2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetBlockUserListResponseDTO.swift; sourceTree = ""; }; - 081898FC2D33D9ED0067BF01 /* GetBlockUserListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetBlockUserListResponse.swift; sourceTree = ""; }; - 081898FE2D33DA440067BF01 /* GetBlockUserListRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetBlockUserListRequestDTO.swift; sourceTree = ""; }; - 081899012D3407F50067BF01 /* BlockUserListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockUserListSection.swift; sourceTree = ""; }; - 081899042D34080B0067BF01 /* BlockUserListSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockUserListSectionCell.swift; sourceTree = ""; }; - 081899072D34B35A0067BF01 /* MyPageNoticeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeView.swift; sourceTree = ""; }; - 081899092D34B3620067BF01 /* MyPageNoticeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeController.swift; sourceTree = ""; }; - 0818990B2D34B3670067BF01 /* MyPageNoticeReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeReactor.swift; sourceTree = ""; }; - 0818990D2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetNoticeListResponseDTO.swift; sourceTree = ""; }; - 0818990F2D34B7240067BF01 /* GetNoticeListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetNoticeListResponse.swift; sourceTree = ""; }; - 081899112D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetNoticeDetailResponseDTO.swift; sourceTree = ""; }; - 081899132D34CAEA0067BF01 /* GetNoticeDetailResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetNoticeDetailResponse.swift; sourceTree = ""; }; - 081899172D34D63E0067BF01 /* NoticeListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeListSection.swift; sourceTree = ""; }; - 081899192D34D6430067BF01 /* NoticeListSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeListSectionCell.swift; sourceTree = ""; }; - 0818991D2D34DF7D0067BF01 /* MyPageNoticeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeDetailView.swift; sourceTree = ""; }; - 0818991F2D34DF880067BF01 /* MyPageNoticeDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeDetailController.swift; sourceTree = ""; }; - 081899212D34DF8E0067BF01 /* MyPageNoticeDetailReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageNoticeDetailReactor.swift; sourceTree = ""; }; - 081899242D3500B80067BF01 /* FAQView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQView.swift; sourceTree = ""; }; - 081899262D3500BF0067BF01 /* FAQController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQController.swift; sourceTree = ""; }; - 081899282D3500C50067BF01 /* FAQReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQReactor.swift; sourceTree = ""; }; - 0818992C2D3506240067BF01 /* FAQDropdownSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQDropdownSectionCell.swift; sourceTree = ""; }; - 0818992E2D3506290067BF01 /* FAQDropdownSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FAQDropdownSection.swift; sourceTree = ""; }; - 081899322D35F1090067BF01 /* MyPageBookmarkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageBookmarkView.swift; sourceTree = ""; }; - 081899342D35F10F0067BF01 /* MyPageBookmarkController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageBookmarkController.swift; sourceTree = ""; }; - 081899362D35F1140067BF01 /* MyPageBookmarkReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageBookmarkReactor.swift; sourceTree = ""; }; - 081899382D35F11F0067BF01 /* MyPageRecentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageRecentView.swift; sourceTree = ""; }; - 0818993A2D35F1250067BF01 /* MyPageRecentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageRecentController.swift; sourceTree = ""; }; - 0818993C2D35F12A0067BF01 /* MyPageRecentReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageRecentReactor.swift; sourceTree = ""; }; - 0818993E2D35FBE00067BF01 /* GetRecentPopUpResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetRecentPopUpResponseDTO.swift; sourceTree = ""; }; - 081899402D35FDA10067BF01 /* GetRecentPopUpResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetRecentPopUpResponse.swift; sourceTree = ""; }; - 081899442D35FEA10067BF01 /* RecentPopUpSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentPopUpSection.swift; sourceTree = ""; }; - 081899492D36322B0067BF01 /* PopUpCardSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpCardSection.swift; sourceTree = ""; }; - 0818994B2D3632320067BF01 /* PopUpCardSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpCardSectionCell.swift; sourceTree = ""; }; - 0818994F2D363E5C0067BF01 /* BookMarkPopUpViewTypeModalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkPopUpViewTypeModalView.swift; sourceTree = ""; }; - 081899512D363E640067BF01 /* BookMarkPopUpViewTypeModalController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkPopUpViewTypeModalController.swift; sourceTree = ""; }; - 081899532D363E6A0067BF01 /* BookMarkPopUpViewTypeModalReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkPopUpViewTypeModalReactor.swift; sourceTree = ""; }; - 082197A62D4E3EE00054094A /* CommentMyMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentMyMenuView.swift; sourceTree = ""; }; - 082197A82D4E3EE90054094A /* CommentMyMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentMyMenuController.swift; sourceTree = ""; }; - 082197AA2D4E3EEF0054094A /* CommentMyMenuReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentMyMenuReactor.swift; sourceTree = ""; }; - 082197AC2D4E49370054094A /* DeleteCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteCommentRequestDTO.swift; sourceTree = ""; }; - 082197AF2D4E4E190054094A /* NormalCommentEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentEditView.swift; sourceTree = ""; }; - 082197B12D4E4E200054094A /* NormalCommentEditController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentEditController.swift; sourceTree = ""; }; - 082197B32D4E4E280054094A /* NormalCommentEditReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentEditReactor.swift; sourceTree = ""; }; - 083A257F2CF361EF0099B58E /* BaseTabmanController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTabmanController.swift; sourceTree = ""; }; - 083A25802CF361EF0099B58E /* BaseViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; - 083A25932CF362090099B58E /* Sectionable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Sectionable.swift; sourceTree = ""; }; - 083A25942CF362090099B58E /* SectionDecorationItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionDecorationItem.swift; sourceTree = ""; }; - 083A25952CF362090099B58E /* SectionSupplementaryItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionSupplementaryItem.swift; sourceTree = ""; }; - 083A25972CF362090099B58E /* InOutputable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InOutputable.swift; sourceTree = ""; }; - 083A25A12CF362670099B58E /* NetworkError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; - 083A25A22CF362670099B58E /* Requestable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Requestable.swift; sourceTree = ""; }; - 083A25A32CF362670099B58E /* Responsable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Responsable.swift; sourceTree = ""; }; - 083A25A52CF362670099B58E /* Endpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; - 083A25A62CF362670099B58E /* MultipartEndPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipartEndPoint.swift; sourceTree = ""; }; - 083A25A72CF362670099B58E /* RequestEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RequestEndpoint.swift; sourceTree = ""; }; - 083A25A92CF362670099B58E /* IndicatorMaker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = IndicatorMaker.swift; sourceTree = ""; }; - 083A25AB2CF362670099B58E /* FormDataInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FormDataInterceptor.swift; sourceTree = ""; }; - 083A25AC2CF362670099B58E /* TokenInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenInterceptor.swift; sourceTree = ""; }; - 083A25AE2CF362670099B58E /* Provider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Provider.swift; sourceTree = ""; }; - 083A25AF2CF362670099B58E /* ProviderImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProviderImpl.swift; sourceTree = ""; }; - 083A25BD2CF362770099B58E /* Logger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; - 083A25C72CF363C00099B58E /* LoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginController.swift; sourceTree = ""; }; - 083A25C92CF363C60099B58E /* LoginReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginReactor.swift; sourceTree = ""; }; - 083A25CB2CF363CB0099B58E /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = ""; }; - 083C860A2D073A15003F441C /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = ""; }; - 083C860C2D073A1C003F441C /* DetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailController.swift; sourceTree = ""; }; - 083C860E2D073A23003F441C /* DetailReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailReactor.swift; sourceTree = ""; }; - 083C861B2D087337003F441C /* GetPopUpDetailRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpDetailRequestDTO.swift; sourceTree = ""; }; - 083C861D2D08737F003F441C /* GetPopUpDetailResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpDetailResponseDTO.swift; sourceTree = ""; }; - 083C861F2D087445003F441C /* GetPopUpDetailResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpDetailResponse.swift; sourceTree = ""; }; - 083C86232D087A44003F441C /* DetailTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTitleSection.swift; sourceTree = ""; }; - 083C86252D087A4E003F441C /* DetailTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTitleSectionCell.swift; sourceTree = ""; }; - 083C86282D088080003F441C /* DetailContentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailContentSection.swift; sourceTree = ""; }; - 083C862A2D08808C003F441C /* DetailContentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailContentSectionCell.swift; sourceTree = ""; }; - 083C86352D0C7EF4003F441C /* CommentSelectedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentSelectedController.swift; sourceTree = ""; }; - 083C86372D0C7EFC003F441C /* CommentSelectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentSelectedView.swift; sourceTree = ""; }; - 083C86392D0C7F0A003F441C /* CommentSelectedReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentSelectedReactor.swift; sourceTree = ""; }; - 083C863C2D0C8BC4003F441C /* NormalCommentAddController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentAddController.swift; sourceTree = ""; }; - 083C863E2D0C8BCE003F441C /* NormalCommentAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentAddView.swift; sourceTree = ""; }; - 083C86402D0C8BD8003F441C /* NormalCommentAddReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NormalCommentAddReactor.swift; sourceTree = ""; }; - 083C86442D0DCDE8003F441C /* AddCommentTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentTitleSectionCell.swift; sourceTree = ""; }; - 083C86462D0DCDFB003F441C /* AddCommentTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentTitleSection.swift; sourceTree = ""; }; - 083C86492D0DCF96003F441C /* AddCommentDescriptionSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentDescriptionSection.swift; sourceTree = ""; }; - 083C864B2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentDescriptionSectionCell.swift; sourceTree = ""; }; - 083C864E2D0DD3A6003F441C /* AddCommentImageSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentImageSection.swift; sourceTree = ""; }; - 083C86502D0DD3AB003F441C /* AddCommentImageSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentImageSectionCell.swift; sourceTree = ""; }; - 083C86532D0DD7E9003F441C /* AddCommentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentSection.swift; sourceTree = ""; }; - 083C86552D0DD7EE003F441C /* AddCommentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddCommentSectionCell.swift; sourceTree = ""; }; - 083C86582D0DEFC3003F441C /* CommentCheckView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentCheckView.swift; sourceTree = ""; }; - 083C865A2D0DEFCF003F441C /* CommentCheckController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentCheckController.swift; sourceTree = ""; }; - 083C865C2D0DEFD5003F441C /* CommentCheckReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentCheckReactor.swift; sourceTree = ""; }; - 083C865F2D0EC496003F441C /* InstaCommentAddView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaCommentAddView.swift; sourceTree = ""; }; - 083C86612D0EC49E003F441C /* InstaCommentAddController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaCommentAddController.swift; sourceTree = ""; }; - 083C86632D0EC4A5003F441C /* InstaCommentAddReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaCommentAddReactor.swift; sourceTree = ""; }; - 083C86682D0ECB47003F441C /* InstaGuideSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaGuideSection.swift; sourceTree = ""; }; - 083C866A2D0ECB4F003F441C /* InstaGuideSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaGuideSectionCell.swift; sourceTree = ""; }; - 083C866D2D0ECB87003F441C /* InstaGuideChildSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaGuideChildSection.swift; sourceTree = ""; }; - 083C866F2D0ECB8E003F441C /* InstaGuideChildSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstaGuideChildSectionCell.swift; sourceTree = ""; }; - 083C86722D0EE2B1003F441C /* CommentAPIEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentAPIEndPoint.swift; sourceTree = ""; }; - 083C86752D0EE2CF003F441C /* PostCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostCommentRequestDTO.swift; sourceTree = ""; }; - 083C86772D0EE382003F441C /* CommentAPIRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentAPIRepository.swift; sourceTree = ""; }; - 083C86792D0EE3BB003F441C /* CommentAPIUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentAPIUseCaseImpl.swift; sourceTree = ""; }; - 0841BA7F2CF9F34100049E31 /* ImageBannerChildSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerChildSectionCell.swift; sourceTree = ""; }; - 0841BA812CF9F5DF00049E31 /* PreSignedService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreSignedService.swift; sourceTree = ""; }; - 0841BA842CF9F62300049E31 /* PreSignedURLDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreSignedURLDTO.swift; sourceTree = ""; }; - 0841BA852CF9F62300049E31 /* PresignedURLRequestDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PresignedURLRequestDTO.swift; sourceTree = ""; }; - 0841BA862CF9F62300049E31 /* PreSignedURLResponseDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreSignedURLResponseDTO.swift; sourceTree = ""; }; - 0841BA8B2CF9F67100049E31 /* PreSignedAPIEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreSignedAPIEndPoint.swift; sourceTree = ""; }; - 0841BA8D2CF9F8A100049E31 /* ImageBannerChildSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerChildSection.swift; sourceTree = ""; }; - 0841BAA22CFA31A300049E31 /* SpacingSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacingSection.swift; sourceTree = ""; }; - 0841BAA42CFA31A900049E31 /* SpacingSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpacingSectionCell.swift; sourceTree = ""; }; - 0841BAA72CFA354500049E31 /* HomeTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTitleSection.swift; sourceTree = ""; }; - 0841BAA92CFA354C00049E31 /* HomeTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTitleSectionCell.swift; sourceTree = ""; }; - 0841BAAB2CFA35F300049E31 /* UILabel+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+.swift"; sourceTree = ""; }; - 0841BAAE2CFA38EA00049E31 /* HomeCardSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCardSection.swift; sourceTree = ""; }; - 0841BAB02CFA38F500049E31 /* HomeCardSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCardSectionCell.swift; sourceTree = ""; }; - 0841BAB32CFABED700049E31 /* HomePopularCardSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePopularCardSection.swift; sourceTree = ""; }; - 0841BAB52CFABEDC00049E31 /* HomePopularCardSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePopularCardSectionCell.swift; sourceTree = ""; }; - 0841BAB72CFAC41300049E31 /* SectionBackGroundDecorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionBackGroundDecorationView.swift; sourceTree = ""; }; - 0841BAB92CFAE5BE00049E31 /* HomeHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeHeaderView.swift; sourceTree = ""; }; - 0841BABB2CFB59E200049E31 /* String?+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String?+.swift"; sourceTree = ""; }; - 0841BABD2CFB5AA600049E31 /* Date?+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date?+.swift"; sourceTree = ""; }; - 0841BAC22CFB600800049E31 /* TabbarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabbarController.swift; sourceTree = ""; }; - 086DD8C72CFDEA9200B97D3B /* UIView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+.swift"; sourceTree = ""; }; - 086DD8CB2CFDFEA800B97D3B /* HomeListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListController.swift; sourceTree = ""; }; - 086DD8CD2CFDFEB000B97D3B /* HomeListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListView.swift; sourceTree = ""; }; - 086DD8CF2CFDFEB900B97D3B /* HomeListReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeListReactor.swift; sourceTree = ""; }; - 086DD8D22CFDFF1500B97D3B /* HomePopUpType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePopUpType.swift; sourceTree = ""; }; - 086DD8D52CFF182100B97D3B /* UserAPIEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAPIEndPoint.swift; sourceTree = ""; }; - 086DD8D72CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostBookmarkPopUpRequestDTO.swift; sourceTree = ""; }; - 086DD8D92CFF194700B97D3B /* UserAPIRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAPIRepositoryImpl.swift; sourceTree = ""; }; - 086DD8DD2CFF19C400B97D3B /* UserAPIUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAPIUseCaseImpl.swift; sourceTree = ""; }; - 086DD8DF2CFF2C3700B97D3B /* UIImageView+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+.swift"; sourceTree = ""; }; - 086DD8E22CFF356300B97D3B /* HomeCardGridSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCardGridSection.swift; sourceTree = ""; }; - 086DD9292D0086AA00B97D3B /* SearchController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchController.swift; sourceTree = ""; }; - 086DD92B2D0086B100B97D3B /* SearchReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchReactor.swift; sourceTree = ""; }; - 086DD92D2D0086B900B97D3B /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = ""; }; - 086DD92F2D0090E900B97D3B /* UITextField+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+.swift"; sourceTree = ""; }; - 086DD9332D00962500B97D3B /* SearchTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTitleSectionCell.swift; sourceTree = ""; }; - 086DD9352D00963900B97D3B /* SearchTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTitleSection.swift; sourceTree = ""; }; - 086DD93A2D009A1C00B97D3B /* CancelableTagSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelableTagSection.swift; sourceTree = ""; }; - 086DD93C2D009A2600B97D3B /* CancelableTagSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelableTagSectionCell.swift; sourceTree = ""; }; - 086DD93F2D01EEEB00B97D3B /* SearchCountTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchCountTitleSection.swift; sourceTree = ""; }; - 086DD9412D01EEF700B97D3B /* SearchCountTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchCountTitleSectionCell.swift; sourceTree = ""; }; - 086F89C22D1E347700CA4FC9 /* CommentUserInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserInfoView.swift; sourceTree = ""; }; - 086F89C42D1E347E00CA4FC9 /* CommentUserInfoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserInfoController.swift; sourceTree = ""; }; - 086F89C62D1E348400CA4FC9 /* CommentUserInfoReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserInfoReactor.swift; sourceTree = ""; }; - 086F89C92D1E42A700CA4FC9 /* CommentUserBlockView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserBlockView.swift; sourceTree = ""; }; - 086F89CB2D1E42B000CA4FC9 /* CommentUserBlockController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserBlockController.swift; sourceTree = ""; }; - 086F89CD2D1E42B500CA4FC9 /* CommentUserBlockReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentUserBlockReactor.swift; sourceTree = ""; }; - 086F89CF2D1E60A100CA4FC9 /* PostUserBlockRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostUserBlockRequestDTO.swift; sourceTree = ""; }; - 086F89D22D1E6DA600CA4FC9 /* OtherUserCommentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherUserCommentView.swift; sourceTree = ""; }; - 086F89D42D1E6DB100CA4FC9 /* OtherUserCommentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherUserCommentController.swift; sourceTree = ""; }; - 086F89D62D1E6DB700CA4FC9 /* OtherUserCommentReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherUserCommentReactor.swift; sourceTree = ""; }; - 086F89D82D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetOtherUserCommentListRequestDTO.swift; sourceTree = ""; }; - 086F89DA2D1E7A6C00CA4FC9 /* GetOtherUserCommentedPopUpListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetOtherUserCommentedPopUpListResponseDTO.swift; sourceTree = ""; }; - 086F89DF2D1E7CC700CA4FC9 /* GetOtherUserCommentedPopUpListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetOtherUserCommentedPopUpListResponse.swift; sourceTree = ""; }; - 086F89E32D1FE91300CA4FC9 /* OtherUserCommentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherUserCommentSection.swift; sourceTree = ""; }; - 086F89E52D1FE91800CA4FC9 /* OtherUserCommentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OtherUserCommentSectionCell.swift; sourceTree = ""; }; - 086F89E92D2009E300CA4FC9 /* SubLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubLoginView.swift; sourceTree = ""; }; - 086F89EB2D2009EB00CA4FC9 /* SubLoginController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubLoginController.swift; sourceTree = ""; }; - 086F89ED2D2009F100CA4FC9 /* SubLoginReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SubLoginReactor.swift; sourceTree = ""; }; - 086F89F02D2269D800CA4FC9 /* MyPageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageView.swift; sourceTree = ""; }; - 086F89F22D2269DE00CA4FC9 /* MyPageController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageController.swift; sourceTree = ""; }; - 086F89F42D2269E300CA4FC9 /* MyPageReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageReactor.swift; sourceTree = ""; }; - 086F89F62D226DF600CA4FC9 /* GetMyPageResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyPageResponseDTO.swift; sourceTree = ""; }; - 086F89F82D226EEB00CA4FC9 /* GetMyPageResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetMyPageResponse.swift; sourceTree = ""; }; - 086F8A042D23CB3300CA4FC9 /* MyPageProfileSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageProfileSection.swift; sourceTree = ""; }; - 086F8A062D23CB3800CA4FC9 /* MyPageProfileSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageProfileSectionCell.swift; sourceTree = ""; }; - 086F8A092D2621EE00CA4FC9 /* MyPageMyCommentTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageMyCommentTitleSection.swift; sourceTree = ""; }; - 086F8A0B2D2621F400CA4FC9 /* MyPageMyCommentTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageMyCommentTitleSectionCell.swift; sourceTree = ""; }; - 086F8A0E2D26297900CA4FC9 /* MyPageCommentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCommentSection.swift; sourceTree = ""; }; - 086F8A102D26297D00CA4FC9 /* MyPageCommentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageCommentSectionCell.swift; sourceTree = ""; }; - 086F8A172D265C5F00CA4FC9 /* MyPageListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageListSection.swift; sourceTree = ""; }; - 086F8A192D265C6300CA4FC9 /* MyPageListSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageListSectionCell.swift; sourceTree = ""; }; - 088DE2492D12F3360030FA9E /* DetailInfoSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailInfoSection.swift; sourceTree = ""; }; - 088DE24B2D12F33B0030FA9E /* DetailInfoSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailInfoSectionCell.swift; sourceTree = ""; }; - 088DE24E2D13019A0030FA9E /* DetailCommentTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentTitleSection.swift; sourceTree = ""; }; - 088DE2502D13019E0030FA9E /* DetailCommentTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentTitleSectionCell.swift; sourceTree = ""; }; - 088DE2532D144A7E0030FA9E /* DetailCommentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentSection.swift; sourceTree = ""; }; - 088DE2552D144A830030FA9E /* DetailCommentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentSectionCell.swift; sourceTree = ""; }; - 088DE2572D144B0F0030FA9E /* DetailCommentProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentProfileView.swift; sourceTree = ""; }; - 088DE2592D1458620030FA9E /* DetailCommentImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailCommentImageCell.swift; sourceTree = ""; }; - 088DE25C2D145E3A0030FA9E /* DetailSimilarSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailSimilarSection.swift; sourceTree = ""; }; - 088DE25E2D145E3F0030FA9E /* DetailSimilarSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailSimilarSectionCell.swift; sourceTree = ""; }; - 089952412D031E650022AEF9 /* SearchSortedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSortedController.swift; sourceTree = ""; }; - 089952432D031E6D0022AEF9 /* SearchSortedReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSortedReactor.swift; sourceTree = ""; }; - 089952452D031E740022AEF9 /* SearchSortedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchSortedView.swift; sourceTree = ""; }; - 089952482D033A1C0022AEF9 /* PopUpAPIEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpAPIEndPoint.swift; sourceTree = ""; }; - 0899524A2D033A9C0022AEF9 /* GetClosePopUpListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetClosePopUpListResponseDTO.swift; sourceTree = ""; }; - 0899524C2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetOpenPopUpListResponseDTO.swift; sourceTree = ""; }; - 0899524E2D033B5A0022AEF9 /* GetSearchPopUpListRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSearchPopUpListRequestDTO.swift; sourceTree = ""; }; - 089952502D033C410022AEF9 /* GetSearchBottomPopUpListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSearchBottomPopUpListResponse.swift; sourceTree = ""; }; - 089952522D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpAPIRepositoryImpl.swift; sourceTree = ""; }; - 089952542D033D480022AEF9 /* PopUpAPIUseCaseImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpAPIUseCaseImpl.swift; sourceTree = ""; }; - 089952572D0347AC0022AEF9 /* SearchCategoryController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchCategoryController.swift; sourceTree = ""; }; - 089952592D0347B40022AEF9 /* SearchCategoryReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchCategoryReactor.swift; sourceTree = ""; }; - 0899525B2D0347BD0022AEF9 /* SearchCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchCategoryView.swift; sourceTree = ""; }; - 0899525F2D0366C40022AEF9 /* SearchMainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchMainController.swift; sourceTree = ""; }; - 089952612D0366D30022AEF9 /* SearchMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchMainView.swift; sourceTree = ""; }; - 089952632D0366DA0022AEF9 /* SearchMainReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchMainReactor.swift; sourceTree = ""; }; - 089952652D046CCD0022AEF9 /* SearchResultController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultController.swift; sourceTree = ""; }; - 089952672D046CD80022AEF9 /* SearchResultReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultReactor.swift; sourceTree = ""; }; - 089952692D046CDE0022AEF9 /* SearchResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultView.swift; sourceTree = ""; }; - 0899526B2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSearchPopUpListResponseDTO.swift; sourceTree = ""; }; - 0899526D2D0474340022AEF9 /* GetSearchPopUpListResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetSearchPopUpListResponse.swift; sourceTree = ""; }; - 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSection.swift; sourceTree = ""; }; - 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultCountSectionCell.swift; sourceTree = ""; }; - 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLoader.swift; sourceTree = ""; }; - 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MemoryStorage.swift; sourceTree = ""; }; - 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentLikeRequestDTO.swift; sourceTree = ""; }; - 08A2E4782D1B06A300102313 /* ImageDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailView.swift; sourceTree = ""; }; - 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailController.swift; sourceTree = ""; }; - 08A2E47C2D1B06B000102313 /* ImageDetailReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageDetailReactor.swift; sourceTree = ""; }; - 08A2E47F2D1BCDE300102313 /* CommentDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailView.swift; sourceTree = ""; }; - 08A2E4812D1BCDEA00102313 /* CommentDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailController.swift; sourceTree = ""; }; - 08A2E4832D1BCDEF00102313 /* CommentDetailReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailReactor.swift; sourceTree = ""; }; - 08A2E4852D1BD85C00102313 /* CommentDetailImageSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailImageSection.swift; sourceTree = ""; }; - 08A2E4892D1BDA8400102313 /* CommentDetailContentSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailContentSection.swift; sourceTree = ""; }; - 08A2E48B2D1BDA8A00102313 /* CommentDetailContentSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentDetailContentSectionCell.swift; sourceTree = ""; }; - 08A2E48E2D1BF6E500102313 /* CommentListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListView.swift; sourceTree = ""; }; - 08A2E4902D1BF6EA00102313 /* CommentListController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListController.swift; sourceTree = ""; }; - 08A2E4922D1BF6EF00102313 /* CommentListReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListReactor.swift; sourceTree = ""; }; - 08A2E4942D1C078300102313 /* GetPopUpCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpCommentRequestDTO.swift; sourceTree = ""; }; - 08A2E4962D1C07F500102313 /* GetPopUpCommentResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpCommentResponseDTO.swift; sourceTree = ""; }; - 08A2E4982D1C08D600102313 /* GetPopUpCommentResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpCommentResponse.swift; sourceTree = ""; }; - 08A2E49C2D1C416800102313 /* CommentListTitleSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListTitleSection.swift; sourceTree = ""; }; - 08A2E49E2D1C417000102313 /* CommentListTitleSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentListTitleSectionCell.swift; sourceTree = ""; }; - 08B191342CF366670057BC04 /* UICollectionViewCell+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+.swift"; sourceTree = ""; }; - 08B191352CF366670057BC04 /* UICollectionReusableView+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionReusableView+.swift"; sourceTree = ""; }; - 08B191362CF366670057BC04 /* UITableViewCell+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+.swift"; sourceTree = ""; }; - 08B1913A2CF366A00057BC04 /* UIApplication+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+.swift"; sourceTree = ""; }; - 08B1913E2CF367FA0057BC04 /* UIColor+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+.swift"; sourceTree = ""; }; - 08B191402CF367FF0057BC04 /* UIFont+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIFont+.swift"; sourceTree = ""; }; - 08B191422CF41D680057BC04 /* GothicA1-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GothicA1-Bold.ttf"; sourceTree = ""; }; - 08B191432CF41D680057BC04 /* GothicA1-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GothicA1-Light.ttf"; sourceTree = ""; }; - 08B191442CF41D680057BC04 /* GothicA1-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GothicA1-Medium.ttf"; sourceTree = ""; }; - 08B191452CF41D680057BC04 /* GothicA1-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "GothicA1-Regular.ttf"; sourceTree = ""; }; - 08B191462CF41D680057BC04 /* Poppins-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Bold.ttf"; sourceTree = ""; }; - 08B191472CF41D680057BC04 /* Poppins-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Light.ttf"; sourceTree = ""; }; - 08B191482CF41D680057BC04 /* Poppins-Medium.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Medium.ttf"; sourceTree = ""; }; - 08B191492CF41D680057BC04 /* Poppins-Regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Poppins-Regular.ttf"; sourceTree = ""; }; - 08B191532CF41D6F0057BC04 /* PP_loading.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = PP_loading.json; sourceTree = ""; }; - 08B191542CF41D6F0057BC04 /* PP_splash.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = PP_splash.json; sourceTree = ""; }; - 08B191582CF41E610057BC04 /* SignUpMainController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpMainController.swift; sourceTree = ""; }; - 08B1915A2CF41E690057BC04 /* SignUpMainReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpMainReactor.swift; sourceTree = ""; }; - 08B1915C2CF41E6F0057BC04 /* SignUpMainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpMainView.swift; sourceTree = ""; }; - 08B191602CF430E70057BC04 /* PPProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPProgressView.swift; sourceTree = ""; }; - 08B191622CF430F30057BC04 /* PPProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPProgressIndicator.swift; sourceTree = ""; }; - 08B191662CF432220057BC04 /* PPCancelHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPCancelHeaderView.swift; sourceTree = ""; }; - 08B191692CF434B80057BC04 /* SignUpStep1Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep1Controller.swift; sourceTree = ""; }; - 08B1916B2CF434C30057BC04 /* SignUpStep1View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep1View.swift; sourceTree = ""; }; - 08B1916D2CF434CF0057BC04 /* SignUpStep1Reactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep1Reactor.swift; sourceTree = ""; }; - 08B191702CF4398D0057BC04 /* PPLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPLabel.swift; sourceTree = ""; }; - 08B191732CF43DF40057BC04 /* SignUpCheckBoxButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCheckBoxButton.swift; sourceTree = ""; }; - 08B191752CF440C40057BC04 /* PPButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPButton.swift; sourceTree = ""; }; - 08B191772CF442230057BC04 /* UIImage+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+.swift"; sourceTree = ""; }; - 08B191792CF452B30057BC04 /* SignUpTermsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpTermsView.swift; sourceTree = ""; }; - 08B1917C2CF46DE30057BC04 /* TermsDetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsDetailController.swift; sourceTree = ""; }; - 08B1917E2CF46DF20057BC04 /* TermsDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TermsDetailView.swift; sourceTree = ""; }; - 08B191812CF48A7B0057BC04 /* SignUpStep2Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep2Controller.swift; sourceTree = ""; }; - 08B191832CF48A820057BC04 /* SignUpStep2View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep2View.swift; sourceTree = ""; }; - 08B191852CF48A8B0057BC04 /* SignUpStep2Reactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep2Reactor.swift; sourceTree = ""; }; - 08B191872CF48FAE0057BC04 /* NickNameState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NickNameState.swift; sourceTree = ""; }; - 08B1918C2CF49FF70057BC04 /* SignUpStep3View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep3View.swift; sourceTree = ""; }; - 08B1918E2CF4A0020057BC04 /* SignUpStep3Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep3Controller.swift; sourceTree = ""; }; - 08B191902CF4A00E0057BC04 /* SignUpStep3Reactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep3Reactor.swift; sourceTree = ""; }; - 08B191932CF4A0F00057BC04 /* SignUpStep4View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep4View.swift; sourceTree = ""; }; - 08B191952CF4A0FA0057BC04 /* SignUpStep4Controller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep4Controller.swift; sourceTree = ""; }; - 08B191972CF4A1010057BC04 /* SignUpStep4Reactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpStep4Reactor.swift; sourceTree = ""; }; - 08B1919B2CF4A77C0057BC04 /* TagSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSection.swift; sourceTree = ""; }; - 08B1919D2CF4A7830057BC04 /* TagSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagSectionCell.swift; sourceTree = ""; }; - 08B1919F2CF4AA0E0057BC04 /* Reactive+.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Reactive+.swift"; sourceTree = ""; }; - 08B191A12CF4AE890057BC04 /* ToastMaker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastMaker.swift; sourceTree = ""; }; - 08B191A32CF5A7030057BC04 /* PPSegmentedControl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PPSegmentedControl.swift; sourceTree = ""; }; - 08B191A62CF5A9430057BC04 /* AgeSelectedButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeSelectedButton.swift; sourceTree = ""; }; - 08B191AB2CF5BF9D0057BC04 /* AgeSelectedController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeSelectedController.swift; sourceTree = ""; }; - 08B191AD2CF5BFA60057BC04 /* AgeSelectedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeSelectedView.swift; sourceTree = ""; }; - 08B191AF2CF5BFAE0057BC04 /* AgeSelectedReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AgeSelectedReactor.swift; sourceTree = ""; }; - 08B191B12CF5C0A60057BC04 /* PPPicker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PPPicker.swift; sourceTree = ""; }; - 08B191B32CF609260057BC04 /* KakaoLoginService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KakaoLoginService.swift; sourceTree = ""; }; - 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppleLoginService.swift; sourceTree = ""; }; - 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthServiceable.swift; sourceTree = ""; }; - 08B191C12CF615CA0057BC04 /* KeyPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyPath.swift; sourceTree = ""; }; - 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTokenReissueResponseDTO.swift; sourceTree = ""; }; - 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostTokenReissueResponse.swift; sourceTree = ""; }; - 08CBEA0A2D38DBD600248007 /* LastLoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LastLoginView.swift; sourceTree = ""; }; - 08CBEA0C2D38ED0D00248007 /* CountButtonView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CountButtonView.swift; sourceTree = ""; }; - 08CBEA392D3FABE100248007 /* ToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastView.swift; sourceTree = ""; }; - 08CBEA3B2D3FABED00248007 /* BookMarkToastView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookMarkToastView.swift; sourceTree = ""; }; - 08CBEA3D2D3FF6A100248007 /* PopUpCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpCardView.swift; sourceTree = ""; }; - 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiskStorage.swift; sourceTree = ""; }; - 08DC61F22CF75037002A2F44 /* KeyChainService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyChainService.swift; sourceTree = ""; }; - 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultService.swift; sourceTree = ""; }; - 08DC61F72CF76843002A2F44 /* SignUpCompleteView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCompleteView.swift; sourceTree = ""; }; - 08DC61F92CF7684F002A2F44 /* SignUpCompleteController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCompleteController.swift; sourceTree = ""; }; - 08DC61FB2CF76862002A2F44 /* SignUpCompleteReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpCompleteReactor.swift; sourceTree = ""; }; - 08DC62022CF8AC06002A2F44 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; - 08DC62042CF8AC0E002A2F44 /* HomeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeController.swift; sourceTree = ""; }; - 08DC62062CF8AC14002A2F44 /* HomeReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeReactor.swift; sourceTree = ""; }; - 08DC620A2CF8AE0F002A2F44 /* ImageBannerSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerSection.swift; sourceTree = ""; }; - 08DC620C2CF8AE16002A2F44 /* ImageBannerSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageBannerSectionCell.swift; sourceTree = ""; }; - 08DC62102CF8B446002A2F44 /* SortedRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SortedRequestDTO.swift; sourceTree = ""; }; - 08DE8A0C2D5236840049BCAC /* PutCommentRequestDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PutCommentRequestDTO.swift; sourceTree = ""; }; - 08DE8A0F2D5255110049BCAC /* DetailEmptyCommetSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEmptyCommetSection.swift; sourceTree = ""; }; - 08DE8A112D5255180049BCAC /* DetailEmptyCommetSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailEmptyCommetSectionCell.swift; sourceTree = ""; }; - 08DE8A162D525A9B0049BCAC /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/LaunchScreen.strings; sourceTree = ""; }; - 08DE8A172D525BA20049BCAC /* Terms.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Terms.plist; sourceTree = ""; }; - 08DE8A1A2D5261DE0049BCAC /* MyPageTermsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageTermsController.swift; sourceTree = ""; }; - 08DE8A1C2D5261E70049BCAC /* MyPageTermsReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageTermsReactor.swift; sourceTree = ""; }; - 08DE8A3E2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentedPopUpGridSection.swift; sourceTree = ""; }; - 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyCommentedPopUpGridSectionCell.swift; sourceTree = ""; }; - 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpStoreRegisterReactor.swift; sourceTree = ""; }; - 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpStoreRegisterView.swift; sourceTree = ""; }; - 4E685EAA2D12CEB6001EF91C /* BalloonBackgroundView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BalloonBackgroundView.swift; sourceTree = ""; }; - 4E685EAB2D12CEB6001EF91C /* BalloonChipCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BalloonChipCell.swift; sourceTree = ""; }; - 4E685EAD2D12CEB6001EF91C /* FilterBottomSheetReactor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterBottomSheetReactor.swift; sourceTree = ""; }; - 4E685EAE2D12CEB6001EF91C /* FilterBottomSheetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterBottomSheetView.swift; sourceTree = ""; }; - 4E685EAF2D12CEB6001EF91C /* FilterBottomSheetViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterBottomSheetViewController.swift; sourceTree = ""; }; - 4E685EB02D12CEB6001EF91C /* FilterCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterCell.swift; sourceTree = ""; }; - 4E685EB12D12CEB6001EF91C /* FilterChip.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterChip.swift; sourceTree = ""; }; - 4E685EB22D12CEB6001EF91C /* FilterChipsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterChipsView.swift; sourceTree = ""; }; - 4E685EB62D12CEB6001EF91C /* MapRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapRepository.swift; sourceTree = ""; }; - 4E685EB82D12CEB6001EF91C /* MapUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapUseCase.swift; sourceTree = ""; }; - 4E685EBA2D12CEB6001EF91C /* MapAPIEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapAPIEndpoint.swift; sourceTree = ""; }; - 4E685EBB2D12CEB6001EF91C /* MapPopUpStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapPopUpStore.swift; sourceTree = ""; }; - 4E685EBC2D12CEB6001EF91C /* MapPopUpStoreDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapPopUpStoreDTO.swift; sourceTree = ""; }; - 4E685EBE2D12CEB6001EF91C /* MapPopupCarouselView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapPopupCarouselView.swift; sourceTree = ""; }; - 4E685EBF2D12CEB6001EF91C /* PopupCardCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopupCardCell.swift; sourceTree = ""; }; - 4E685EC12D12CEB6001EF91C /* StoreListCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreListCell.swift; sourceTree = ""; }; - 4E685EC22D12CEB6001EF91C /* StoreListReactor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreListReactor.swift; sourceTree = ""; }; - 4E685EC32D12CEB6001EF91C /* StoreListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreListView.swift; sourceTree = ""; }; - 4E685EC42D12CEB6001EF91C /* StoreListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StoreListViewController.swift; sourceTree = ""; }; - 4E685EC62D12CEB6001EF91C /* MapFilterChips.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapFilterChips.swift; sourceTree = ""; }; - 4E685EC72D12CEB6001EF91C /* MapMarker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapMarker.swift; sourceTree = ""; }; - 4E685EC82D12CEB6001EF91C /* MapReactor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapReactor.swift; sourceTree = ""; }; - 4E685EC92D12CEB6001EF91C /* MapSearchInput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapSearchInput.swift; sourceTree = ""; }; - 4E685ECB2D12CEB6001EF91C /* MapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapView.swift; sourceTree = ""; }; - 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapViewController.swift; sourceTree = ""; }; - 4E6A066F2D42A96100B2A658 /* FullScreenMapViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMapViewController.swift; sourceTree = ""; }; - 4E6C07052D4B6E56008A962A /* RegionDefinitions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionDefinitions.swift; sourceTree = ""; }; - 4E6C07072D4B6E74008A962A /* ClusteringModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClusteringModels.swift; sourceTree = ""; }; - 4E6C07092D4B6E81008A962A /* ClusteringManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClusteringManager.swift; sourceTree = ""; }; - 4E6CA4842D34D6ED0034D09A /* AdminBottomSheetReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminBottomSheetReactor.swift; sourceTree = ""; }; - 4E755B1C2D2B9AD300ADFB21 /* GetAdminPopUpStoreListResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetAdminPopUpStoreListResponseDTO.swift; sourceTree = ""; }; - 4E755B1E2D2B9AE500ADFB21 /* AdminAPIEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminAPIEndpoint.swift; sourceTree = ""; }; - 4E755B202D2B9BAB00ADFB21 /* AdminResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminResponseDTO.swift; sourceTree = ""; }; - 4E755B222D2B9C5D00ADFB21 /* AdminViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminViewController.swift; sourceTree = ""; }; - 4E755B242D2B9C6C00ADFB21 /* AdminView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminView.swift; sourceTree = ""; }; - 4E755B262D2B9C7C00ADFB21 /* AdminStoreCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminStoreCell.swift; sourceTree = ""; }; - 4E755B282D2BA65A00ADFB21 /* AdminReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminReactor.swift; sourceTree = ""; }; - 4E755B2A2D2BA76E00ADFB21 /* AdminUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminUseCase.swift; sourceTree = ""; }; - 4E755B2E2D2BA7FB00ADFB21 /* AdminRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminRepository.swift; sourceTree = ""; }; - 4E8AA29C2D59A2340029DF75 /* MarkerTooltipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkerTooltipView.swift; sourceTree = ""; }; - 4E9790C42D40E13500210499 /* MapGuideViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapGuideViewController.swift; sourceTree = ""; }; - 4E9A465F2D55D1270010578A /* MapUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUtilities.swift; sourceTree = ""; }; - 4E9C12772D2BC7A0006744D6 /* AdminBottomSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminBottomSheetView.swift; sourceTree = ""; }; - 4E9C12792D2BC811006744D6 /* AdminBottomSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdminBottomSheetViewController.swift; sourceTree = ""; }; - 4E9C12802D2BE0A6006744D6 /* PopUpStoreRegisterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpStoreRegisterViewController.swift; sourceTree = ""; }; - 4EA2C93C2D424D3300F4D97C /* MapDirectionRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDirectionRepository.swift; sourceTree = ""; }; - 4EA2C93E2D424D7400F4D97C /* MapDirectionUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDirectionUseCase.swift; sourceTree = ""; }; - 4EA2C9402D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GetPopUpDirectionResponseDTO.swift; sourceTree = ""; }; - 4EA2C9422D424DF900F4D97C /* FindDirectionEndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FindDirectionEndPoint.swift; sourceTree = ""; }; - 4EA998992D21C2FC009DC30B /* StoreListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StoreListSection.swift; sourceTree = ""; }; - 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NMFMapViewDelegateProxy.swift; sourceTree = ""; }; - 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewportBounds.swift; sourceTree = ""; }; - 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimePickerManager.swift; sourceTree = ""; }; - 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationPermissionBottomSheet.swift; sourceTree = ""; }; - 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapGuideReactor.swift; sourceTree = ""; }; - 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopUpImagesCollectionView.swift; sourceTree = ""; }; - 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCell.swift; sourceTree = ""; }; - 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedImage.swift; sourceTree = ""; }; - 4EED9BAB2D22730400B288E7 /* FilterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterType.swift; sourceTree = ""; }; - BD226D502CF6DB290038C984 /* PPReturnHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PPReturnHeaderView.swift; sourceTree = ""; }; - BD91034E2CF6149D00BBCCAE /* AuthAPIEndPoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthAPIEndPoint.swift; sourceTree = ""; }; - BD91034F2CF6149D00BBCCAE /* LoginResponseDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginResponseDTO.swift; sourceTree = ""; }; - BD9103512CF6149D00BBCCAE /* BannerPopUpStoreDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerPopUpStoreDTO.swift; sourceTree = ""; }; - BD9103522CF6149D00BBCCAE /* GetHomeInfoResponseDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetHomeInfoResponseDTO.swift; sourceTree = ""; }; - BD9103532CF6149D00BBCCAE /* HomeAPIEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeAPIEndpoint.swift; sourceTree = ""; }; - BD9103542CF6149D00BBCCAE /* PopUpStoreResponseDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopUpStoreResponseDTO.swift; sourceTree = ""; }; - BD9103572CF6149D00BBCCAE /* AuthAPIRepositoryImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthAPIRepositoryImpl.swift; sourceTree = ""; }; - BD9103582CF6149D00BBCCAE /* HomeAPIRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeAPIRepository.swift; sourceTree = ""; }; - BD9103592CF6149D00BBCCAE /* SignUpRepositoryImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignUpRepositoryImpl.swift; sourceTree = ""; }; - BD91035B2CF6149D00BBCCAE /* CheckNickNameRequestDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckNickNameRequestDTO.swift; sourceTree = ""; }; - BD91035C2CF6149D00BBCCAE /* GetCategoryListResponseDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetCategoryListResponseDTO.swift; sourceTree = ""; }; - BD91035D2CF6149D00BBCCAE /* SignUpAPIEndpoint.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignUpAPIEndpoint.swift; sourceTree = ""; }; - BD91035E2CF6149D00BBCCAE /* SignUpRequestDTO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignUpRequestDTO.swift; sourceTree = ""; }; - BD9103702CF614A900BBCCAE /* HomeAPIUseCaseImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeAPIUseCaseImpl.swift; sourceTree = ""; }; - BD9103722CF614A900BBCCAE /* SignUpAPIUseCaseImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignUpAPIUseCaseImpl.swift; sourceTree = ""; }; - BD9103742CF614A900BBCCAE /* AuthAPIUseCaseImpl.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthAPIUseCaseImpl.swift; sourceTree = ""; }; - BD9103762CF614A900BBCCAE /* BannerPopUpStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BannerPopUpStore.swift; sourceTree = ""; }; - BD9103772CF614A900BBCCAE /* Category.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = ""; }; - BD9103782CF614A900BBCCAE /* GetHomeInfoResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetHomeInfoResponse.swift; sourceTree = ""; }; - BD9103792CF614A900BBCCAE /* PopUpStoreResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PopUpStoreResponse.swift; sourceTree = ""; }; - BD91037A2CF614A900BBCCAE /* LoginResponse.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginResponse.swift; sourceTree = ""; }; - BD91037C2CF614A900BBCCAE /* AuthRepository.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthRepository.swift; sourceTree = ""; }; - BD91038E2CF6166800BBCCAE /* SplashView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashView.swift; sourceTree = ""; }; - BD9103902CF6166800BBCCAE /* SplashController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashController.swift; sourceTree = ""; }; BDCA41BD2CF35AC0005EECF6 /* Poppool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Poppool.app; sourceTree = BUILT_PRODUCTS_DIR; }; - BDCA41C02CF35AC0005EECF6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - BDCA41C22CF35AC0005EECF6 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - BDCA41C92CF35AC1005EECF6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - BDCA41CC2CF35AC1005EECF6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - BDCA41CE2CF35AC1005EECF6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - BDE30CE02CF87A9700C21E08 /* Poppool.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Poppool.entitlements; sourceTree = ""; }; /* End PBXFileReference section */ -/* Begin PBXFrameworksBuildPhase section */ - BDCA41BA2CF35AC0005EECF6 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */, - BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, - BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, - BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, - 082197A12D426DCB0054094A /* Then in Frameworks */, - 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, - 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, - 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, - BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, - BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, - BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, - BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, - 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, - 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, - 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, - BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, - 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, - 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, - 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 05FBCABE2DABE79A00215BE6 /* AdminAPI */ = { - isa = PBXGroup; - children = ( - 05FBCABF2DABE7AC00215BE6 /* ResponseDTO */, - 4E755B1E2D2B9AE500ADFB21 /* AdminAPIEndpoint.swift */, - ); - path = AdminAPI; - sourceTree = ""; - }; - 05FBCABF2DABE7AC00215BE6 /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - 4E755B1C2D2B9AD300ADFB21 /* GetAdminPopUpStoreListResponseDTO.swift */, - 4E755B202D2B9BAB00ADFB21 /* AdminResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 05FBCAC02DABEB1100215BE6 /* MapAPI */ = { - isa = PBXGroup; - children = ( - 4EA2C9422D424DF900F4D97C /* FindDirectionEndPoint.swift */, - 4E685EBA2D12CEB6001EF91C /* MapAPIEndpoint.swift */, - 05FBCAC12DABEB3B00215BE6 /* ResponseDTO */, - ); - path = MapAPI; - sourceTree = ""; - }; - 05FBCAC12DABEB3B00215BE6 /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - 4E685EBC2D12CEB6001EF91C /* MapPopUpStoreDTO.swift */, - 4EA2C9402D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 0818988C2D295DAD0067BF01 /* MyPageLogoutSection */ = { - isa = PBXGroup; - children = ( - 0818988D2D295DC30067BF01 /* MyPageLogoutSection.swift */, - 0818988F2D295DC80067BF01 /* MyPageLogoutSectionCell.swift */, - ); - path = MyPageLogoutSection; - sourceTree = ""; - }; - 081898912D2962E30067BF01 /* Main */ = { - isa = PBXGroup; - children = ( - 086F89FA2D23B84900CA4FC9 /* View */, - 086F89F22D2269DE00CA4FC9 /* MyPageController.swift */, - 086F89F42D2269E300CA4FC9 /* MyPageReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 081898922D2965B10067BF01 /* ProfileEdit */ = { - isa = PBXGroup; - children = ( - 081898C82D30D5AC0067BF01 /* CategoryEditModal */, - 081898C72D30D5A10067BF01 /* InfoEditModal */, - 081898C62D30D5940067BF01 /* Main */, - ); - path = ProfileEdit; - sourceTree = ""; - }; - 081898992D2BAA200067BF01 /* Withdrawl */ = { - isa = PBXGroup; - children = ( - 081898B12D2D20C50067BF01 /* Complete */, - 081898A12D2CBFF30067BF01 /* SelectedReason */, - 0818989A2D2BAA400067BF01 /* CheckModal */, - ); - path = Withdrawl; - sourceTree = ""; - }; - 0818989A2D2BAA400067BF01 /* CheckModal */ = { - isa = PBXGroup; - children = ( - 0818989B2D2BAA570067BF01 /* WithdrawlCheckModalView.swift */, - 0818989D2D2BAA610067BF01 /* WithdrawlCheckModalController.swift */, - 0818989F2D2BAA670067BF01 /* WithdrawlCheckModalReactor.swift */, - ); - path = CheckModal; - sourceTree = ""; - }; - 081898A12D2CBFF30067BF01 /* SelectedReason */ = { - isa = PBXGroup; - children = ( - 081898A82D2CEA210067BF01 /* View */, - 081898A42D2CC0180067BF01 /* WithdrawlReasonController.swift */, - 081898A62D2CC01D0067BF01 /* WithdrawlReasonReactor.swift */, - ); - path = SelectedReason; - sourceTree = ""; - }; - 081898A82D2CEA210067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081898A22D2CC0110067BF01 /* WithdrawlReasonView.swift */, - 081898A92D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift */, - 081898AB2D2CEA940067BF01 /* WithdrawlCheckSection.swift */, - ); - path = View; - sourceTree = ""; - }; - 081898B12D2D20C50067BF01 /* Complete */ = { - isa = PBXGroup; - children = ( - 081898B22D2D20D70067BF01 /* WithdrawlCompleteView.swift */, - 081898B42D2D20E30067BF01 /* WithdrawlCompleteController.swift */, - ); - path = Complete; - sourceTree = ""; - }; - 081898B82D2E5F420067BF01 /* MyComment */ = { - isa = PBXGroup; - children = ( - 081898EA2D33A3870067BF01 /* SortedModal */, - 081898E92D33A3800067BF01 /* Main */, - ); - path = MyComment; - sourceTree = ""; - }; - 081898C12D2FBD170067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081898952D2965C90067BF01 /* ProfileEditView.swift */, - 081898BF2D2FBD130067BF01 /* ProfileEditListButton.swift */, - ); - path = View; - sourceTree = ""; - }; - 081898C62D30D5940067BF01 /* Main */ = { - isa = PBXGroup; - children = ( - 081898C12D2FBD170067BF01 /* View */, - 081898932D2965C20067BF01 /* ProfileEditController.swift */, - 081898972D2965D20067BF01 /* ProfileEditReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 081898C72D30D5A10067BF01 /* InfoEditModal */ = { - isa = PBXGroup; - children = ( - 081898C92D30D5BA0067BF01 /* InfoEditModalView.swift */, - 081898CB2D30D5BF0067BF01 /* InfoEditModalController.swift */, - 081898CD2D30D5C60067BF01 /* InfoEditModalReactor.swift */, - ); - path = InfoEditModal; - sourceTree = ""; - }; - 081898C82D30D5AC0067BF01 /* CategoryEditModal */ = { - isa = PBXGroup; - children = ( - 081898D12D30F57D0067BF01 /* CategoryEditModalView.swift */, - 081898D32D30F5840067BF01 /* CategoryEditModalController.swift */, - 081898D52D30F58A0067BF01 /* CategoryEditModalReactor.swift */, - ); - path = CategoryEditModal; - sourceTree = ""; - }; - 081898DD2D338E2B0067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 08DE8A3D2D54DCAF0049BCAC /* MyCommentedPopUpGridSection */, - 081898DE2D338F790067BF01 /* ListCountButtonSection */, - 081898B92D2E5F4C0067BF01 /* MyCommentView.swift */, - ); - path = View; - sourceTree = ""; - }; - 081898DE2D338F790067BF01 /* ListCountButtonSection */ = { - isa = PBXGroup; - children = ( - 081898DF2D338F9C0067BF01 /* ListCountButtonSection.swift */, - 081898E12D338FA40067BF01 /* ListCountButtonSectionCell.swift */, - ); - path = ListCountButtonSection; - sourceTree = ""; - }; - 081898E92D33A3800067BF01 /* Main */ = { - isa = PBXGroup; - children = ( - 081898DD2D338E2B0067BF01 /* View */, - 081898BD2D2E5F590067BF01 /* MyCommentController.swift */, - 081898BB2D2E5F510067BF01 /* MyCommentReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 081898EA2D33A3870067BF01 /* SortedModal */ = { - isa = PBXGroup; - children = ( - 081898EB2D33A3960067BF01 /* MyCommentSortedModalView.swift */, - 081898ED2D33A39D0067BF01 /* MyCommentSortedModalController.swift */, - 081898EF2D33A3A30067BF01 /* MyCommentSortedModalReactor.swift */, - ); - path = SortedModal; - sourceTree = ""; - }; - 081898F12D33D6930067BF01 /* Block */ = { - isa = PBXGroup; - children = ( - 081899002D3407DC0067BF01 /* View */, - 081898F42D33D6B10067BF01 /* BlockUserManageController.swift */, - 081898F62D33D6B70067BF01 /* BlockUserManageReactor.swift */, - ); - path = Block; - sourceTree = ""; - }; - 081899002D3407DC0067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081899032D3407F80067BF01 /* BlockUserListSection */, - 081898F22D33D6AC0067BF01 /* BlockUserManageView.swift */, - ); - path = View; - sourceTree = ""; - }; - 081899032D3407F80067BF01 /* BlockUserListSection */ = { - isa = PBXGroup; - children = ( - 081899012D3407F50067BF01 /* BlockUserListSection.swift */, - 081899042D34080B0067BF01 /* BlockUserListSectionCell.swift */, - ); - path = BlockUserListSection; - sourceTree = ""; - }; - 081899062D34B3470067BF01 /* Notice */ = { - isa = PBXGroup; - children = ( - 0818991C2D34DF5B0067BF01 /* Detail */, - 0818991B2D34DF530067BF01 /* List */, - ); - path = Notice; - sourceTree = ""; - }; - 081899152D34D6060067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081899162D34D62F0067BF01 /* NoticeListSection */, - 081899072D34B35A0067BF01 /* MyPageNoticeView.swift */, - ); - path = View; - sourceTree = ""; - }; - 081899162D34D62F0067BF01 /* NoticeListSection */ = { - isa = PBXGroup; - children = ( - 081899172D34D63E0067BF01 /* NoticeListSection.swift */, - 081899192D34D6430067BF01 /* NoticeListSectionCell.swift */, - ); - path = NoticeListSection; - sourceTree = ""; - }; - 0818991B2D34DF530067BF01 /* List */ = { - isa = PBXGroup; - children = ( - 081899152D34D6060067BF01 /* View */, - 081899092D34B3620067BF01 /* MyPageNoticeController.swift */, - 0818990B2D34B3670067BF01 /* MyPageNoticeReactor.swift */, - ); - path = List; - sourceTree = ""; - }; - 0818991C2D34DF5B0067BF01 /* Detail */ = { - isa = PBXGroup; - children = ( - 0818991D2D34DF7D0067BF01 /* MyPageNoticeDetailView.swift */, - 0818991F2D34DF880067BF01 /* MyPageNoticeDetailController.swift */, - 081899212D34DF8E0067BF01 /* MyPageNoticeDetailReactor.swift */, - ); - path = Detail; - sourceTree = ""; - }; - 081899232D3500A90067BF01 /* FAQ */ = { - isa = PBXGroup; - children = ( - 0818992A2D3506100067BF01 /* View */, - 081899262D3500BF0067BF01 /* FAQController.swift */, - 081899282D3500C50067BF01 /* FAQReactor.swift */, - ); - path = FAQ; - sourceTree = ""; - }; - 0818992A2D3506100067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 0818992B2D3506160067BF01 /* FAQDropdownSection */, - 081899242D3500B80067BF01 /* FAQView.swift */, - ); - path = View; - sourceTree = ""; - }; - 0818992B2D3506160067BF01 /* FAQDropdownSection */ = { - isa = PBXGroup; - children = ( - 0818992C2D3506240067BF01 /* FAQDropdownSectionCell.swift */, - 0818992E2D3506290067BF01 /* FAQDropdownSection.swift */, - ); - path = FAQDropdownSection; - sourceTree = ""; - }; - 081899302D35F0C40067BF01 /* Bookmark */ = { - isa = PBXGroup; - children = ( - 0818994E2D363E3D0067BF01 /* ViewTypeModal */, - 0818994D2D363E340067BF01 /* Main */, - ); - path = Bookmark; - sourceTree = ""; - }; - 081899312D35F0D40067BF01 /* Recent */ = { - isa = PBXGroup; - children = ( - 081899422D35FE870067BF01 /* View */, - 0818993A2D35F1250067BF01 /* MyPageRecentController.swift */, - 0818993C2D35F12A0067BF01 /* MyPageRecentReactor.swift */, - ); - path = Recent; - sourceTree = ""; - }; - 081899422D35FE870067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081899432D35FE8D0067BF01 /* RecentPopUpSection */, - 081899382D35F11F0067BF01 /* MyPageRecentView.swift */, - ); - path = View; - sourceTree = ""; - }; - 081899432D35FE8D0067BF01 /* RecentPopUpSection */ = { - isa = PBXGroup; - children = ( - 081899442D35FEA10067BF01 /* RecentPopUpSection.swift */, - ); - path = RecentPopUpSection; - sourceTree = ""; - }; - 081899462D3632030067BF01 /* View */ = { - isa = PBXGroup; - children = ( - 081899472D3632070067BF01 /* PopUpCardSection */, - 08CBEA0C2D38ED0D00248007 /* CountButtonView.swift */, - ); - path = View; - sourceTree = ""; - }; - 081899472D3632070067BF01 /* PopUpCardSection */ = { - isa = PBXGroup; - children = ( - 081899492D36322B0067BF01 /* PopUpCardSection.swift */, - 0818994B2D3632320067BF01 /* PopUpCardSectionCell.swift */, - 08CBEA3D2D3FF6A100248007 /* PopUpCardView.swift */, - ); - path = PopUpCardSection; - sourceTree = ""; - }; - 0818994D2D363E340067BF01 /* Main */ = { - isa = PBXGroup; - children = ( - 081899462D3632030067BF01 /* View */, - 081899322D35F1090067BF01 /* MyPageBookmarkView.swift */, - 081899342D35F10F0067BF01 /* MyPageBookmarkController.swift */, - 081899362D35F1140067BF01 /* MyPageBookmarkReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 0818994E2D363E3D0067BF01 /* ViewTypeModal */ = { - isa = PBXGroup; - children = ( - 0818994F2D363E5C0067BF01 /* BookMarkPopUpViewTypeModalView.swift */, - 081899512D363E640067BF01 /* BookMarkPopUpViewTypeModalController.swift */, - 081899532D363E6A0067BF01 /* BookMarkPopUpViewTypeModalReactor.swift */, - ); - path = ViewTypeModal; - sourceTree = ""; - }; - 082197A42D4E3E9B0054094A /* CommentMyMenu */ = { - isa = PBXGroup; - children = ( - 082197A62D4E3EE00054094A /* CommentMyMenuView.swift */, - 082197A82D4E3EE90054094A /* CommentMyMenuController.swift */, - 082197AA2D4E3EEF0054094A /* CommentMyMenuReactor.swift */, - ); - path = CommentMyMenu; - sourceTree = ""; - }; - 082197A52D4E3EAC0054094A /* CommentInfoMenu */ = { - isa = PBXGroup; - children = ( - 086F89C12D1E345D00CA4FC9 /* CommentUserInfo */, - 082197A42D4E3E9B0054094A /* CommentMyMenu */, - ); - path = CommentInfoMenu; - sourceTree = ""; - }; - 082197AE2D4E4E040054094A /* NormalCommentEdit */ = { - isa = PBXGroup; - children = ( - 082197AF2D4E4E190054094A /* NormalCommentEditView.swift */, - 082197B12D4E4E200054094A /* NormalCommentEditController.swift */, - 082197B32D4E4E280054094A /* NormalCommentEditReactor.swift */, - ); - path = NormalCommentEdit; - sourceTree = ""; - }; - 083A256B2CF361190099B58E /* Application */ = { - isa = PBXGroup; - children = ( - BDCA41C02CF35AC0005EECF6 /* AppDelegate.swift */, - BDCA41C22CF35AC0005EECF6 /* SceneDelegate.swift */, - ); - path = Application; - sourceTree = ""; - }; - 083A256C2CF361210099B58E /* Resource */ = { - isa = PBXGroup; - children = ( - 08DE8A132D525A4A0049BCAC /* Strings */, - 08B191C12CF615CA0057BC04 /* KeyPath.swift */, - 057151572D9D2E0800260615 /* Debug.xcconfig */, - 08B191532CF41D6F0057BC04 /* PP_loading.json */, - 08B191542CF41D6F0057BC04 /* PP_splash.json */, - 08B1914A2CF41D680057BC04 /* Font */, - BDCA41C92CF35AC1005EECF6 /* Assets.xcassets */, - BDCA41CB2CF35AC1005EECF6 /* LaunchScreen.storyboard */, - BDCA41CE2CF35AC1005EECF6 /* Info.plist */, - ); - path = Resource; - sourceTree = ""; - }; - 083A256D2CF3613C0099B58E /* Presentation */ = { - isa = PBXGroup; - children = ( - 08B1915F2CF430D40057BC04 /* Components */, - 083A25C22CF3635B0099B58E /* Scene */, - 083A259D2CF3620B0099B58E /* Utills */, - 08B191332CF366500057BC04 /* Extension */, - ); - path = Presentation; - sourceTree = ""; - }; - 083A25812CF361EF0099B58E /* Controllers */ = { - isa = PBXGroup; - children = ( - 083A257F2CF361EF0099B58E /* BaseTabmanController.swift */, - 083A25802CF361EF0099B58E /* BaseViewController.swift */, - ); - path = Controllers; - sourceTree = ""; - }; - 083A25962CF362090099B58E /* Sectionable */ = { - isa = PBXGroup; - children = ( - 083A25932CF362090099B58E /* Sectionable.swift */, - 083A25942CF362090099B58E /* SectionDecorationItem.swift */, - 083A25952CF362090099B58E /* SectionSupplementaryItem.swift */, - ); - path = Sectionable; - sourceTree = ""; - }; - 083A25982CF362090099B58E /* Interfaces */ = { - isa = PBXGroup; - children = ( - 083A25962CF362090099B58E /* Sectionable */, - 083A25972CF362090099B58E /* InOutputable.swift */, - ); - path = Interfaces; - sourceTree = ""; - }; - 083A259D2CF3620B0099B58E /* Utills */ = { - isa = PBXGroup; - children = ( - 083A25982CF362090099B58E /* Interfaces */, - 083A25812CF361EF0099B58E /* Controllers */, - 08CBEA382D3FABD300248007 /* ToastMaker */, - 4EDDEFB22D2D284B00CFAFA5 /* Common */, - ); - path = Utills; - sourceTree = ""; - }; - 083A259E2CF362310099B58E /* Domain */ = { - isa = PBXGroup; - children = ( - BD91037B2CF614A900BBCCAE /* Entities */, - BD91037F2CF614A900BBCCAE /* Repository */, - BD9103752CF614A900BBCCAE /* UseCase */, - ); - path = Domain; - sourceTree = ""; - }; - 083A259F2CF362360099B58E /* Data */ = { - isa = PBXGroup; - children = ( - BD91035A2CF6149D00BBCCAE /* Repository */, - BD9103602CF6149D00BBCCAE /* Network */, - ); - path = Data; - sourceTree = ""; - }; - 083A25A02CF3623C0099B58E /* Infrastructure */ = { - isa = PBXGroup; - children = ( - 089B4FD62D9A576F00FC0CC3 /* ImageLoader */, - 0841BA832CF9F61500049E31 /* PreSignedService */, - 083A25B12CF362670099B58E /* NetworkLayer */, - 08DC61F42CF765B5002A2F44 /* UserDefaultService.swift */, - 08DC61F22CF75037002A2F44 /* KeyChainService.swift */, - 08B191B72CF6092F0057BC04 /* AuthServiceable.swift */, - 08B191B52CF6092B0057BC04 /* AppleLoginService.swift */, - 08B191B32CF609260057BC04 /* KakaoLoginService.swift */, - 083A25BE2CF362770099B58E /* Logger */, - ); - path = Infrastructure; - sourceTree = ""; - }; - 083A25A42CF362670099B58E /* Common */ = { - isa = PBXGroup; - children = ( - 083A25A12CF362670099B58E /* NetworkError.swift */, - 083A25A22CF362670099B58E /* Requestable.swift */, - 083A25A32CF362670099B58E /* Responsable.swift */, - ); - path = Common; - sourceTree = ""; - }; - 083A25A82CF362670099B58E /* EndPoint */ = { - isa = PBXGroup; - children = ( - 083A25A52CF362670099B58E /* Endpoint.swift */, - 083A25A62CF362670099B58E /* MultipartEndPoint.swift */, - 083A25A72CF362670099B58E /* RequestEndpoint.swift */, - ); - path = EndPoint; - sourceTree = ""; - }; - 083A25AA2CF362670099B58E /* IndicatorMaker */ = { - isa = PBXGroup; - children = ( - 083A25A92CF362670099B58E /* IndicatorMaker.swift */, - ); - path = IndicatorMaker; - sourceTree = ""; - }; - 083A25AD2CF362670099B58E /* Interceptor */ = { - isa = PBXGroup; - children = ( - 083A25AB2CF362670099B58E /* FormDataInterceptor.swift */, - 083A25AC2CF362670099B58E /* TokenInterceptor.swift */, - ); - path = Interceptor; - sourceTree = ""; - }; - 083A25B02CF362670099B58E /* Provider */ = { - isa = PBXGroup; - children = ( - 083A25AE2CF362670099B58E /* Provider.swift */, - 083A25AF2CF362670099B58E /* ProviderImpl.swift */, - ); - path = Provider; - sourceTree = ""; - }; - 083A25B12CF362670099B58E /* NetworkLayer */ = { - isa = PBXGroup; - children = ( - 083A25A42CF362670099B58E /* Common */, - 083A25A82CF362670099B58E /* EndPoint */, - 083A25AA2CF362670099B58E /* IndicatorMaker */, - 083A25AD2CF362670099B58E /* Interceptor */, - 083A25B02CF362670099B58E /* Provider */, - ); - path = NetworkLayer; - sourceTree = ""; - }; - 083A25BE2CF362770099B58E /* Logger */ = { - isa = PBXGroup; - children = ( - 083A25BD2CF362770099B58E /* Logger.swift */, - ); - path = Logger; - sourceTree = ""; - }; - 083A25C22CF3635B0099B58E /* Scene */ = { - isa = PBXGroup; - children = ( - 086F89EF2D2269CD00CA4FC9 /* MyPage */, - 08A2E4772D1B069300102313 /* ImageDetail */, - 0841BABF2CFB5EC700049E31 /* TabbarController */, - 08DC62012CF8ABDA002A2F44 /* Home */, - 086DD9282D00869D00B97D3B /* Search */, - 083A25C42CF363A10099B58E /* SignUp */, - 083A25C32CF363650099B58E /* Login */, - BD9103912CF6166800BBCCAE /* Splash */, - 083C86092D073A08003F441C /* Detail */, - 083C86332D0C7EDB003F441C /* Comment */, - 4E755B1B2D2B9ABF00ADFB21 /* Admin */, - 4E685ECD2D12CEB6001EF91C /* Map */, - ); - path = Scene; - sourceTree = ""; - }; - 083A25C32CF363650099B58E /* Login */ = { - isa = PBXGroup; - children = ( - 086F89E72D2009CF00CA4FC9 /* Main */, - 086F89E82D2009D700CA4FC9 /* Sub */, - 08CBEA0A2D38DBD600248007 /* LastLoginView.swift */, - ); - path = Login; - sourceTree = ""; - }; - 083A25C42CF363A10099B58E /* SignUp */ = { - isa = PBXGroup; - children = ( - 08B1917B2CF46DD40057BC04 /* TermsDetail */, - 08B191572CF41E550057BC04 /* Main */, - 08B191682CF434A10057BC04 /* Step1 */, - 08B191802CF48A5C0057BC04 /* Step2 */, - 08B191892CF49FDE0057BC04 /* Step3 */, - 08B191922CF4A0E00057BC04 /* Step4 */, - 08DC61F62CF76831002A2F44 /* SignUpComplete */, - ); - path = SignUp; - sourceTree = ""; - }; - 083C86092D073A08003F441C /* Detail */ = { - isa = PBXGroup; - children = ( - 083C86212D087A14003F441C /* View */, - 083C860C2D073A1C003F441C /* DetailController.swift */, - 083C860E2D073A23003F441C /* DetailReactor.swift */, - ); - path = Detail; - sourceTree = ""; - }; - 083C86192D087316003F441C /* RequestDTO */ = { - isa = PBXGroup; - children = ( - 0899524E2D033B5A0022AEF9 /* GetSearchPopUpListRequestDTO.swift */, - 083C861B2D087337003F441C /* GetPopUpDetailRequestDTO.swift */, - 08A2E4942D1C078300102313 /* GetPopUpCommentRequestDTO.swift */, - ); - path = RequestDTO; - sourceTree = ""; - }; - 083C861A2D08731C003F441C /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - 0899526B2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift */, - 0899524A2D033A9C0022AEF9 /* GetClosePopUpListResponseDTO.swift */, - 0899524C2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift */, - 083C861D2D08737F003F441C /* GetPopUpDetailResponseDTO.swift */, - 08A2E4962D1C07F500102313 /* GetPopUpCommentResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 083C86212D087A14003F441C /* View */ = { - isa = PBXGroup; - children = ( - 08DE8A0E2D5255000049BCAC /* DetailEmptyCommetSection */, - 088DE25B2D145E1F0030FA9E /* DetailSimilarSection */, - 088DE2522D144A6E0030FA9E /* DetailCommentSection */, - 088DE24D2D13018C0030FA9E /* DetailCommentTitleSection */, - 088DE2482D12F3250030FA9E /* DetailInfoSection */, - 083C86272D088069003F441C /* DetailContentSection */, - 083C86222D087A37003F441C /* DetailTitleSection */, - 083C860A2D073A15003F441C /* DetailView.swift */, - ); - path = View; - sourceTree = ""; - }; - 083C86222D087A37003F441C /* DetailTitleSection */ = { - isa = PBXGroup; - children = ( - 083C86232D087A44003F441C /* DetailTitleSection.swift */, - 083C86252D087A4E003F441C /* DetailTitleSectionCell.swift */, - ); - path = DetailTitleSection; - sourceTree = ""; - }; - 083C86272D088069003F441C /* DetailContentSection */ = { - isa = PBXGroup; - children = ( - 083C86282D088080003F441C /* DetailContentSection.swift */, - 083C862A2D08808C003F441C /* DetailContentSectionCell.swift */, - ); - path = DetailContentSection; - sourceTree = ""; - }; - 083C86332D0C7EDB003F441C /* Comment */ = { - isa = PBXGroup; - children = ( - 082197AE2D4E4E040054094A /* NormalCommentEdit */, - 082197A52D4E3EAC0054094A /* CommentInfoMenu */, - 086F89D12D1E6D9100CA4FC9 /* OtherUserComment */, - 086F89C82D1E429400CA4FC9 /* CommentUserBlock */, - 08A2E48D2D1BF6D900102313 /* CommentList */, - 08A2E47E2D1BCDB800102313 /* CommentDetail */, - 083C86572D0DEFB1003F441C /* CommentCheck */, - 083C86342D0C7EE3003F441C /* CommentSelected */, - 083C863B2D0C8BAF003F441C /* NormalCommentAdd */, - 083C865E2D0EC47B003F441C /* InstaComment */, - ); - path = Comment; - sourceTree = ""; - }; - 083C86342D0C7EE3003F441C /* CommentSelected */ = { - isa = PBXGroup; - children = ( - 083C86372D0C7EFC003F441C /* CommentSelectedView.swift */, - 083C86352D0C7EF4003F441C /* CommentSelectedController.swift */, - 083C86392D0C7F0A003F441C /* CommentSelectedReactor.swift */, - ); - path = CommentSelected; - sourceTree = ""; - }; - 083C863B2D0C8BAF003F441C /* NormalCommentAdd */ = { - isa = PBXGroup; - children = ( - 083C86422D0DCD9B003F441C /* View */, - 083C863C2D0C8BC4003F441C /* NormalCommentAddController.swift */, - 083C86402D0C8BD8003F441C /* NormalCommentAddReactor.swift */, - ); - path = NormalCommentAdd; - sourceTree = ""; - }; - 083C86422D0DCD9B003F441C /* View */ = { - isa = PBXGroup; - children = ( - 083C864D2D0DD317003F441C /* AddCommentImageSection */, - 083C86432D0DCDD5003F441C /* AddCommentTitleSection */, - 083C86482D0DCF7A003F441C /* AddCommentDescriptionSection */, - 083C86522D0DD7D1003F441C /* AddCommentSection */, - 083C863E2D0C8BCE003F441C /* NormalCommentAddView.swift */, - ); - path = View; - sourceTree = ""; - }; - 083C86432D0DCDD5003F441C /* AddCommentTitleSection */ = { - isa = PBXGroup; - children = ( - 083C86442D0DCDE8003F441C /* AddCommentTitleSectionCell.swift */, - 083C86462D0DCDFB003F441C /* AddCommentTitleSection.swift */, - ); - path = AddCommentTitleSection; - sourceTree = ""; - }; - 083C86482D0DCF7A003F441C /* AddCommentDescriptionSection */ = { - isa = PBXGroup; - children = ( - 083C86492D0DCF96003F441C /* AddCommentDescriptionSection.swift */, - 083C864B2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift */, - ); - path = AddCommentDescriptionSection; - sourceTree = ""; - }; - 083C864D2D0DD317003F441C /* AddCommentImageSection */ = { - isa = PBXGroup; - children = ( - 083C864E2D0DD3A6003F441C /* AddCommentImageSection.swift */, - 083C86502D0DD3AB003F441C /* AddCommentImageSectionCell.swift */, - ); - path = AddCommentImageSection; - sourceTree = ""; - }; - 083C86522D0DD7D1003F441C /* AddCommentSection */ = { - isa = PBXGroup; - children = ( - 083C86532D0DD7E9003F441C /* AddCommentSection.swift */, - 083C86552D0DD7EE003F441C /* AddCommentSectionCell.swift */, - ); - path = AddCommentSection; - sourceTree = ""; - }; - 083C86572D0DEFB1003F441C /* CommentCheck */ = { - isa = PBXGroup; - children = ( - 083C86582D0DEFC3003F441C /* CommentCheckView.swift */, - 083C865A2D0DEFCF003F441C /* CommentCheckController.swift */, - 083C865C2D0DEFD5003F441C /* CommentCheckReactor.swift */, - ); - path = CommentCheck; - sourceTree = ""; - }; - 083C865E2D0EC47B003F441C /* InstaComment */ = { - isa = PBXGroup; - children = ( - 083C86652D0ECB23003F441C /* View */, - 083C86612D0EC49E003F441C /* InstaCommentAddController.swift */, - 083C86632D0EC4A5003F441C /* InstaCommentAddReactor.swift */, - ); - path = InstaComment; - sourceTree = ""; - }; - 083C86652D0ECB23003F441C /* View */ = { - isa = PBXGroup; - children = ( - 083C86662D0ECB2B003F441C /* InstaGuideSection */, - 083C865F2D0EC496003F441C /* InstaCommentAddView.swift */, - ); - path = View; - sourceTree = ""; - }; - 083C86662D0ECB2B003F441C /* InstaGuideSection */ = { - isa = PBXGroup; - children = ( - 083C866C2D0ECB7B003F441C /* InstaGuideChildSection */, - 083C86672D0ECB3C003F441C /* InstaGuideSection */, - ); - path = InstaGuideSection; - sourceTree = ""; - }; - 083C86672D0ECB3C003F441C /* InstaGuideSection */ = { - isa = PBXGroup; - children = ( - 083C86682D0ECB47003F441C /* InstaGuideSection.swift */, - 083C866A2D0ECB4F003F441C /* InstaGuideSectionCell.swift */, - ); - path = InstaGuideSection; - sourceTree = ""; - }; - 083C866C2D0ECB7B003F441C /* InstaGuideChildSection */ = { - isa = PBXGroup; - children = ( - 083C866D2D0ECB87003F441C /* InstaGuideChildSection.swift */, - 083C866F2D0ECB8E003F441C /* InstaGuideChildSectionCell.swift */, - ); - path = InstaGuideChildSection; - sourceTree = ""; - }; - 083C86712D0EE2A3003F441C /* CommentAPI */ = { - isa = PBXGroup; - children = ( - 083C86722D0EE2B1003F441C /* CommentAPIEndPoint.swift */, - 083C86742D0EE2B8003F441C /* RequestDTO */, - ); - path = CommentAPI; - sourceTree = ""; - }; - 083C86742D0EE2B8003F441C /* RequestDTO */ = { - isa = PBXGroup; - children = ( - 083C86752D0EE2CF003F441C /* PostCommentRequestDTO.swift */, - 082197AC2D4E49370054094A /* DeleteCommentRequestDTO.swift */, - 08DE8A0C2D5236840049BCAC /* PutCommentRequestDTO.swift */, - ); - path = RequestDTO; - sourceTree = ""; - }; - 0841BA832CF9F61500049E31 /* PreSignedService */ = { - isa = PBXGroup; - children = ( - 0841BA842CF9F62300049E31 /* PreSignedURLDTO.swift */, - 0841BA852CF9F62300049E31 /* PresignedURLRequestDTO.swift */, - 0841BA862CF9F62300049E31 /* PreSignedURLResponseDTO.swift */, - 0841BA812CF9F5DF00049E31 /* PreSignedService.swift */, - 0841BA8B2CF9F67100049E31 /* PreSignedAPIEndPoint.swift */, - ); - path = PreSignedService; - sourceTree = ""; - }; - 0841BA9D2CFA085700049E31 /* ImageBannerSection */ = { - isa = PBXGroup; - children = ( - 08DC620A2CF8AE0F002A2F44 /* ImageBannerSection.swift */, - 08DC620C2CF8AE16002A2F44 /* ImageBannerSectionCell.swift */, - ); - path = ImageBannerSection; - sourceTree = ""; - }; - 0841BA9E2CFA085F00049E31 /* ImageBannerChildSection */ = { - isa = PBXGroup; - children = ( - 0841BA8D2CF9F8A100049E31 /* ImageBannerChildSection.swift */, - 0841BA7F2CF9F34100049E31 /* ImageBannerChildSectionCell.swift */, - ); - path = ImageBannerChildSection; - sourceTree = ""; - }; - 0841BAA12CFA319400049E31 /* SpacingSection */ = { - isa = PBXGroup; - children = ( - 0841BAA22CFA31A300049E31 /* SpacingSection.swift */, - 0841BAA42CFA31A900049E31 /* SpacingSectionCell.swift */, - ); - path = SpacingSection; - sourceTree = ""; - }; - 0841BAA62CFA353500049E31 /* HomeTitleSection */ = { - isa = PBXGroup; - children = ( - 0841BAA72CFA354500049E31 /* HomeTitleSection.swift */, - 0841BAA92CFA354C00049E31 /* HomeTitleSectionCell.swift */, - ); - path = HomeTitleSection; - sourceTree = ""; - }; - 0841BAAD2CFA38CB00049E31 /* HomeCardSection */ = { - isa = PBXGroup; - children = ( - 0841BAAE2CFA38EA00049E31 /* HomeCardSection.swift */, - 0841BAB02CFA38F500049E31 /* HomeCardSectionCell.swift */, - ); - path = HomeCardSection; - sourceTree = ""; - }; - 0841BAB22CFABEA900049E31 /* HomePopularCardSection */ = { - isa = PBXGroup; - children = ( - 0841BAB32CFABED700049E31 /* HomePopularCardSection.swift */, - 0841BAB52CFABEDC00049E31 /* HomePopularCardSectionCell.swift */, - ); - path = HomePopularCardSection; - sourceTree = ""; - }; - 0841BABF2CFB5EC700049E31 /* TabbarController */ = { - isa = PBXGroup; - children = ( - 0841BAC22CFB600800049E31 /* TabbarController.swift */, - ); - path = TabbarController; - sourceTree = ""; - }; - 086DD8C92CFDFE9100B97D3B /* Main */ = { - isa = PBXGroup; - children = ( - 08DC62082CF8ADB7002A2F44 /* View */, - 08DC62042CF8AC0E002A2F44 /* HomeController.swift */, - 08DC62062CF8AC14002A2F44 /* HomeReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 086DD8CA2CFDFE9900B97D3B /* List */ = { - isa = PBXGroup; - children = ( - 086DD8E12CFF355200B97D3B /* View */, - 086DD8CB2CFDFEA800B97D3B /* HomeListController.swift */, - 086DD8CF2CFDFEB900B97D3B /* HomeListReactor.swift */, - 086DD8D22CFDFF1500B97D3B /* HomePopUpType.swift */, - ); - path = List; - sourceTree = ""; - }; - 086DD8D42CFF181500B97D3B /* UserAPI */ = { - isa = PBXGroup; - children = ( - 086DD8D52CFF182100B97D3B /* UserAPIEndPoint.swift */, - 086F89DD2D1E7A7A00CA4FC9 /* ResponseDTO */, - 086F89DC2D1E7A7100CA4FC9 /* RequesetDTO */, - ); - path = UserAPI; - sourceTree = ""; - }; - 086DD8E12CFF355200B97D3B /* View */ = { - isa = PBXGroup; - children = ( - 086DD8CD2CFDFEB000B97D3B /* HomeListView.swift */, - 086DD8E22CFF356300B97D3B /* HomeCardGridSection.swift */, - ); - path = View; - sourceTree = ""; - }; - 086DD9282D00869D00B97D3B /* Search */ = { - isa = PBXGroup; - children = ( - 0899523F2D031E470022AEF9 /* Main */, - 0899525E2D0366A20022AEF9 /* AfterSearch */, - 0899525D2D0366950022AEF9 /* BeforeSearch */, - 089952562D03478D0022AEF9 /* CategoryController */, - 089952402D031E500022AEF9 /* SortedController */, - ); - path = Search; - sourceTree = ""; - }; - 086DD9312D0095DA00B97D3B /* View */ = { - isa = PBXGroup; - children = ( - 086DD93E2D01EED200B97D3B /* SearchCountTitleSection */, - 086DD9372D0099FB00B97D3B /* CancelableTagSection */, - 086DD9322D00960700B97D3B /* SearchTitleSection */, - 086DD92D2D0086B900B97D3B /* SearchView.swift */, - ); - path = View; - sourceTree = ""; - }; - 086DD9322D00960700B97D3B /* SearchTitleSection */ = { - isa = PBXGroup; - children = ( - 086DD9332D00962500B97D3B /* SearchTitleSectionCell.swift */, - 086DD9352D00963900B97D3B /* SearchTitleSection.swift */, - ); - path = SearchTitleSection; - sourceTree = ""; - }; - 086DD9372D0099FB00B97D3B /* CancelableTagSection */ = { - isa = PBXGroup; - children = ( - 086DD93A2D009A1C00B97D3B /* CancelableTagSection.swift */, - 086DD93C2D009A2600B97D3B /* CancelableTagSectionCell.swift */, - ); - path = CancelableTagSection; - sourceTree = ""; - }; - 086DD93E2D01EED200B97D3B /* SearchCountTitleSection */ = { - isa = PBXGroup; - children = ( - 086DD93F2D01EEEB00B97D3B /* SearchCountTitleSection.swift */, - 086DD9412D01EEF700B97D3B /* SearchCountTitleSectionCell.swift */, - ); - path = SearchCountTitleSection; - sourceTree = ""; - }; - 086F89C12D1E345D00CA4FC9 /* CommentUserInfo */ = { - isa = PBXGroup; - children = ( - 086F89C22D1E347700CA4FC9 /* CommentUserInfoView.swift */, - 086F89C42D1E347E00CA4FC9 /* CommentUserInfoController.swift */, - 086F89C62D1E348400CA4FC9 /* CommentUserInfoReactor.swift */, - ); - path = CommentUserInfo; - sourceTree = ""; - }; - 086F89C82D1E429400CA4FC9 /* CommentUserBlock */ = { - isa = PBXGroup; - children = ( - 086F89C92D1E42A700CA4FC9 /* CommentUserBlockView.swift */, - 086F89CB2D1E42B000CA4FC9 /* CommentUserBlockController.swift */, - 086F89CD2D1E42B500CA4FC9 /* CommentUserBlockReactor.swift */, - ); - path = CommentUserBlock; - sourceTree = ""; - }; - 086F89D12D1E6D9100CA4FC9 /* OtherUserComment */ = { - isa = PBXGroup; - children = ( - 086F89E12D1FE8F200CA4FC9 /* View */, - 086F89D42D1E6DB100CA4FC9 /* OtherUserCommentController.swift */, - 086F89D62D1E6DB700CA4FC9 /* OtherUserCommentReactor.swift */, - ); - path = OtherUserComment; - sourceTree = ""; - }; - 086F89DC2D1E7A7100CA4FC9 /* RequesetDTO */ = { - isa = PBXGroup; - children = ( - 086DD8D72CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift */, - 08A2E46B2D15BC5000102313 /* CommentLikeRequestDTO.swift */, - 086F89CF2D1E60A100CA4FC9 /* PostUserBlockRequestDTO.swift */, - 086F89D82D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift */, - 081898CF2D30EA900067BF01 /* PutUserTailoredInfoRequestDTO.swift */, - 081898D72D310C160067BF01 /* PutUserCategoryRequestDTO.swift */, - 081898D92D32559B0067BF01 /* PutUserProfileRequestDTO.swift */, - 081898E72D3392480067BF01 /* GetMyCommentRequestDTO.swift */, - 081898FE2D33DA440067BF01 /* GetBlockUserListRequestDTO.swift */, - ); - path = RequesetDTO; - sourceTree = ""; - }; - 086F89DD2D1E7A7A00CA4FC9 /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - 086F89DA2D1E7A6C00CA4FC9 /* GetOtherUserCommentedPopUpListResponseDTO.swift */, - 081898C22D30AE2C0067BF01 /* GetMyProfileResponseDTO.swift */, - 086F89F62D226DF600CA4FC9 /* GetMyPageResponseDTO.swift */, - 081898AD2D2CFC230067BF01 /* GetWithdrawlListResponseDTO.swift */, - 081898E32D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift */, - 081898FA2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift */, - 0818990D2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift */, - 081899112D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift */, - 0818993E2D35FBE00067BF01 /* GetRecentPopUpResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 086F89DE2D1E7CBE00CA4FC9 /* UserAPI */ = { - isa = PBXGroup; - children = ( - 086F89DF2D1E7CC700CA4FC9 /* GetOtherUserCommentedPopUpListResponse.swift */, - 086F89F82D226EEB00CA4FC9 /* GetMyPageResponse.swift */, - 081898AF2D2CFCA40067BF01 /* GetWithdrawlListResponse.swift */, - 081898C42D30AEF40067BF01 /* GetMyProfileResponse.swift */, - 081898E52D3391CB0067BF01 /* GetMyCommentResponse.swift */, - 081898FC2D33D9ED0067BF01 /* GetBlockUserListResponse.swift */, - 0818990F2D34B7240067BF01 /* GetNoticeListResponse.swift */, - 081899132D34CAEA0067BF01 /* GetNoticeDetailResponse.swift */, - 081899402D35FDA10067BF01 /* GetRecentPopUpResponse.swift */, - ); - name = UserAPI; - sourceTree = ""; - }; - 086F89E12D1FE8F200CA4FC9 /* View */ = { - isa = PBXGroup; - children = ( - 086F89E22D1FE8F800CA4FC9 /* OtherUserCommentSection */, - 086F89D22D1E6DA600CA4FC9 /* OtherUserCommentView.swift */, - ); - path = View; - sourceTree = ""; - }; - 086F89E22D1FE8F800CA4FC9 /* OtherUserCommentSection */ = { - isa = PBXGroup; - children = ( - 086F89E32D1FE91300CA4FC9 /* OtherUserCommentSection.swift */, - 086F89E52D1FE91800CA4FC9 /* OtherUserCommentSectionCell.swift */, - ); - path = OtherUserCommentSection; - sourceTree = ""; - }; - 086F89E72D2009CF00CA4FC9 /* Main */ = { - isa = PBXGroup; - children = ( - 083A25CB2CF363CB0099B58E /* LoginView.swift */, - 083A25C72CF363C00099B58E /* LoginController.swift */, - 083A25C92CF363C60099B58E /* LoginReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 086F89E82D2009D700CA4FC9 /* Sub */ = { - isa = PBXGroup; - children = ( - 086F89E92D2009E300CA4FC9 /* SubLoginView.swift */, - 086F89EB2D2009EB00CA4FC9 /* SubLoginController.swift */, - 086F89ED2D2009F100CA4FC9 /* SubLoginReactor.swift */, - ); - path = Sub; - sourceTree = ""; - }; - 086F89EF2D2269CD00CA4FC9 /* MyPage */ = { - isa = PBXGroup; - children = ( - 08DE8A192D5261CD0049BCAC /* Terms */, - 081899312D35F0D40067BF01 /* Recent */, - 081899302D35F0C40067BF01 /* Bookmark */, - 081899232D3500A90067BF01 /* FAQ */, - 081899062D34B3470067BF01 /* Notice */, - 081898F12D33D6930067BF01 /* Block */, - 081898B82D2E5F420067BF01 /* MyComment */, - 081898992D2BAA200067BF01 /* Withdrawl */, - 081898922D2965B10067BF01 /* ProfileEdit */, - 081898912D2962E30067BF01 /* Main */, - ); - path = MyPage; - sourceTree = ""; - }; - 086F89FA2D23B84900CA4FC9 /* View */ = { - isa = PBXGroup; - children = ( - 0818988C2D295DAD0067BF01 /* MyPageLogoutSection */, - 086F8A162D265C5200CA4FC9 /* MyPageListSection */, - 086F8A0D2D26296600CA4FC9 /* MyPageCommentSection */, - 086F8A082D2621DB00CA4FC9 /* MyPageMyCommentTitleSection */, - 086F8A032D23CB2400CA4FC9 /* MyPageProfileSection */, - 086F89F02D2269D800CA4FC9 /* MyPageView.swift */, - ); - path = View; - sourceTree = ""; - }; - 086F8A032D23CB2400CA4FC9 /* MyPageProfileSection */ = { - isa = PBXGroup; - children = ( - 086F8A042D23CB3300CA4FC9 /* MyPageProfileSection.swift */, - 086F8A062D23CB3800CA4FC9 /* MyPageProfileSectionCell.swift */, - ); - path = MyPageProfileSection; - sourceTree = ""; - }; - 086F8A082D2621DB00CA4FC9 /* MyPageMyCommentTitleSection */ = { - isa = PBXGroup; - children = ( - 086F8A092D2621EE00CA4FC9 /* MyPageMyCommentTitleSection.swift */, - 086F8A0B2D2621F400CA4FC9 /* MyPageMyCommentTitleSectionCell.swift */, - ); - path = MyPageMyCommentTitleSection; - sourceTree = ""; - }; - 086F8A0D2D26296600CA4FC9 /* MyPageCommentSection */ = { - isa = PBXGroup; - children = ( - 086F8A0E2D26297900CA4FC9 /* MyPageCommentSection.swift */, - 086F8A102D26297D00CA4FC9 /* MyPageCommentSectionCell.swift */, - ); - path = MyPageCommentSection; - sourceTree = ""; - }; - 086F8A162D265C5200CA4FC9 /* MyPageListSection */ = { - isa = PBXGroup; - children = ( - 086F8A172D265C5F00CA4FC9 /* MyPageListSection.swift */, - 086F8A192D265C6300CA4FC9 /* MyPageListSectionCell.swift */, - ); - path = MyPageListSection; - sourceTree = ""; - }; - 088DE2482D12F3250030FA9E /* DetailInfoSection */ = { - isa = PBXGroup; - children = ( - 088DE2492D12F3360030FA9E /* DetailInfoSection.swift */, - 088DE24B2D12F33B0030FA9E /* DetailInfoSectionCell.swift */, - ); - path = DetailInfoSection; - sourceTree = ""; - }; - 088DE24D2D13018C0030FA9E /* DetailCommentTitleSection */ = { - isa = PBXGroup; - children = ( - 088DE24E2D13019A0030FA9E /* DetailCommentTitleSection.swift */, - 088DE2502D13019E0030FA9E /* DetailCommentTitleSectionCell.swift */, - ); - path = DetailCommentTitleSection; - sourceTree = ""; - }; - 088DE2522D144A6E0030FA9E /* DetailCommentSection */ = { - isa = PBXGroup; - children = ( - 088DE2532D144A7E0030FA9E /* DetailCommentSection.swift */, - 088DE2552D144A830030FA9E /* DetailCommentSectionCell.swift */, - 088DE2572D144B0F0030FA9E /* DetailCommentProfileView.swift */, - 088DE2592D1458620030FA9E /* DetailCommentImageCell.swift */, - ); - path = DetailCommentSection; - sourceTree = ""; - }; - 088DE25B2D145E1F0030FA9E /* DetailSimilarSection */ = { - isa = PBXGroup; - children = ( - 088DE25C2D145E3A0030FA9E /* DetailSimilarSection.swift */, - 088DE25E2D145E3F0030FA9E /* DetailSimilarSectionCell.swift */, - ); - path = DetailSimilarSection; - sourceTree = ""; - }; - 0899523F2D031E470022AEF9 /* Main */ = { - isa = PBXGroup; - children = ( - 089952612D0366D30022AEF9 /* SearchMainView.swift */, - 0899525F2D0366C40022AEF9 /* SearchMainController.swift */, - 089952632D0366DA0022AEF9 /* SearchMainReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 089952402D031E500022AEF9 /* SortedController */ = { - isa = PBXGroup; - children = ( - 089952452D031E740022AEF9 /* SearchSortedView.swift */, - 089952412D031E650022AEF9 /* SearchSortedController.swift */, - 089952432D031E6D0022AEF9 /* SearchSortedReactor.swift */, - ); - path = SortedController; - sourceTree = ""; - }; - 089952472D033A0E0022AEF9 /* PopUpAPI */ = { - isa = PBXGroup; - children = ( - 089952482D033A1C0022AEF9 /* PopUpAPIEndPoint.swift */, - 083C861A2D08731C003F441C /* ResponseDTO */, - 083C86192D087316003F441C /* RequestDTO */, - ); - path = PopUpAPI; - sourceTree = ""; - }; - 089952562D03478D0022AEF9 /* CategoryController */ = { - isa = PBXGroup; - children = ( - 0899525B2D0347BD0022AEF9 /* SearchCategoryView.swift */, - 089952572D0347AC0022AEF9 /* SearchCategoryController.swift */, - 089952592D0347B40022AEF9 /* SearchCategoryReactor.swift */, - ); - path = CategoryController; - sourceTree = ""; - }; - 0899525D2D0366950022AEF9 /* BeforeSearch */ = { - isa = PBXGroup; - children = ( - 086DD9312D0095DA00B97D3B /* View */, - 086DD9292D0086AA00B97D3B /* SearchController.swift */, - 086DD92B2D0086B100B97D3B /* SearchReactor.swift */, - ); - path = BeforeSearch; - sourceTree = ""; - }; - 0899525E2D0366A20022AEF9 /* AfterSearch */ = { - isa = PBXGroup; - children = ( - 089952762D0476590022AEF9 /* View */, - 089952652D046CCD0022AEF9 /* SearchResultController.swift */, - 089952672D046CD80022AEF9 /* SearchResultReactor.swift */, - ); - path = AfterSearch; - sourceTree = ""; - }; - 0899526F2D0474430022AEF9 /* PopUpAPI */ = { - isa = PBXGroup; - children = ( - 089952502D033C410022AEF9 /* GetSearchBottomPopUpListResponse.swift */, - 0899526D2D0474340022AEF9 /* GetSearchPopUpListResponse.swift */, - 083C861F2D087445003F441C /* GetPopUpDetailResponse.swift */, - 08A2E4982D1C08D600102313 /* GetPopUpCommentResponse.swift */, - ); - path = PopUpAPI; - sourceTree = ""; - }; - 089952712D0475D70022AEF9 /* SearchResultCountSection */ = { - isa = PBXGroup; - children = ( - 089952722D0475E90022AEF9 /* SearchResultCountSection.swift */, - 089952742D0475F20022AEF9 /* SearchResultCountSectionCell.swift */, - ); - path = SearchResultCountSection; - sourceTree = ""; - }; - 089952762D0476590022AEF9 /* View */ = { - isa = PBXGroup; - children = ( - 089952712D0475D70022AEF9 /* SearchResultCountSection */, - 089952692D046CDE0022AEF9 /* SearchResultView.swift */, - ); - path = View; - sourceTree = ""; - }; - 089B4FD62D9A576F00FC0CC3 /* ImageLoader */ = { - isa = PBXGroup; - children = ( - 089B4FD72D9A57AE00FC0CC3 /* ImageLoader.swift */, - 089B4FDE2D9A8F9A00FC0CC3 /* MemoryStorage.swift */, - 08CFD3912D9BDE99004CDD50 /* DiskStorage.swift */, - ); - path = ImageLoader; - sourceTree = ""; - }; - 08A2E4772D1B069300102313 /* ImageDetail */ = { - isa = PBXGroup; - children = ( - 08A2E4782D1B06A300102313 /* ImageDetailView.swift */, - 08A2E47A2D1B06AA00102313 /* ImageDetailController.swift */, - 08A2E47C2D1B06B000102313 /* ImageDetailReactor.swift */, - ); - path = ImageDetail; - sourceTree = ""; - }; - 08A2E47E2D1BCDB800102313 /* CommentDetail */ = { - isa = PBXGroup; - children = ( - 08A2E4872D1BD86600102313 /* View */, - 08A2E4812D1BCDEA00102313 /* CommentDetailController.swift */, - 08A2E4832D1BCDEF00102313 /* CommentDetailReactor.swift */, - ); - path = CommentDetail; - sourceTree = ""; - }; - 08A2E4872D1BD86600102313 /* View */ = { - isa = PBXGroup; - children = ( - 08A2E4882D1BDA7500102313 /* CommentDetailContentSection */, - 08A2E4852D1BD85C00102313 /* CommentDetailImageSection.swift */, - 08A2E47F2D1BCDE300102313 /* CommentDetailView.swift */, - ); - path = View; - sourceTree = ""; - }; - 08A2E4882D1BDA7500102313 /* CommentDetailContentSection */ = { - isa = PBXGroup; - children = ( - 08A2E4892D1BDA8400102313 /* CommentDetailContentSection.swift */, - 08A2E48B2D1BDA8A00102313 /* CommentDetailContentSectionCell.swift */, - ); - path = CommentDetailContentSection; - sourceTree = ""; - }; - 08A2E48D2D1BF6D900102313 /* CommentList */ = { - isa = PBXGroup; - children = ( - 08A2E49A2D1C414C00102313 /* View */, - 08A2E4902D1BF6EA00102313 /* CommentListController.swift */, - 08A2E4922D1BF6EF00102313 /* CommentListReactor.swift */, - ); - path = CommentList; - sourceTree = ""; - }; - 08A2E49A2D1C414C00102313 /* View */ = { - isa = PBXGroup; - children = ( - 08A2E49B2D1C415500102313 /* CommentListTitleSection */, - 08A2E48E2D1BF6E500102313 /* CommentListView.swift */, - ); - path = View; - sourceTree = ""; - }; - 08A2E49B2D1C415500102313 /* CommentListTitleSection */ = { - isa = PBXGroup; - children = ( - 08A2E49C2D1C416800102313 /* CommentListTitleSection.swift */, - 08A2E49E2D1C417000102313 /* CommentListTitleSectionCell.swift */, - ); - path = CommentListTitleSection; - sourceTree = ""; - }; - 08B191332CF366500057BC04 /* Extension */ = { - isa = PBXGroup; - children = ( - 08B1919F2CF4AA0E0057BC04 /* Reactive+.swift */, - 0841BABB2CFB59E200049E31 /* String?+.swift */, - 08B191402CF367FF0057BC04 /* UIFont+.swift */, - 08B1913E2CF367FA0057BC04 /* UIColor+.swift */, - 08B191352CF366670057BC04 /* UICollectionReusableView+.swift */, - 08B191342CF366670057BC04 /* UICollectionViewCell+.swift */, - 08B191362CF366670057BC04 /* UITableViewCell+.swift */, - 08B1913A2CF366A00057BC04 /* UIApplication+.swift */, - 08B191772CF442230057BC04 /* UIImage+.swift */, - 0841BAAB2CFA35F300049E31 /* UILabel+.swift */, - 0841BABD2CFB5AA600049E31 /* Date?+.swift */, - 086DD8C72CFDEA9200B97D3B /* UIView+.swift */, - 086DD8DF2CFF2C3700B97D3B /* UIImageView+.swift */, - 086DD92F2D0090E900B97D3B /* UITextField+.swift */, - 081898B62D2D23A90067BF01 /* UINavigationController+.swift */, - ); - path = Extension; - sourceTree = ""; - }; - 08B1914A2CF41D680057BC04 /* Font */ = { - isa = PBXGroup; - children = ( - 08B191422CF41D680057BC04 /* GothicA1-Bold.ttf */, - 08B191432CF41D680057BC04 /* GothicA1-Light.ttf */, - 08B191442CF41D680057BC04 /* GothicA1-Medium.ttf */, - 08B191452CF41D680057BC04 /* GothicA1-Regular.ttf */, - 08B191462CF41D680057BC04 /* Poppins-Bold.ttf */, - 08B191472CF41D680057BC04 /* Poppins-Light.ttf */, - 08B191482CF41D680057BC04 /* Poppins-Medium.ttf */, - 08B191492CF41D680057BC04 /* Poppins-Regular.ttf */, - ); - path = Font; - sourceTree = ""; - }; - 08B191572CF41E550057BC04 /* Main */ = { - isa = PBXGroup; - children = ( - 08B1915E2CF430C00057BC04 /* View */, - 08B191582CF41E610057BC04 /* SignUpMainController.swift */, - 08B1915A2CF41E690057BC04 /* SignUpMainReactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 08B1915E2CF430C00057BC04 /* View */ = { - isa = PBXGroup; - children = ( - 08B1915C2CF41E6F0057BC04 /* SignUpMainView.swift */, - ); - path = View; - sourceTree = ""; - }; - 08B1915F2CF430D40057BC04 /* Components */ = { - isa = PBXGroup; - children = ( - 08B191B12CF5C0A60057BC04 /* PPPicker.swift */, - 08B191702CF4398D0057BC04 /* PPLabel.swift */, - 08B191752CF440C40057BC04 /* PPButton.swift */, - 08B191662CF432220057BC04 /* PPCancelHeaderView.swift */, - BD226D502CF6DB290038C984 /* PPReturnHeaderView.swift */, - 08B191A32CF5A7030057BC04 /* PPSegmentedControl.swift */, - 08B191642CF430F80057BC04 /* PPProgressIndicator */, - ); - path = Components; - sourceTree = ""; - }; - 08B191642CF430F80057BC04 /* PPProgressIndicator */ = { - isa = PBXGroup; - children = ( - 08B191602CF430E70057BC04 /* PPProgressView.swift */, - 08B191622CF430F30057BC04 /* PPProgressIndicator.swift */, - ); - path = PPProgressIndicator; - sourceTree = ""; - }; - 08B191682CF434A10057BC04 /* Step1 */ = { - isa = PBXGroup; - children = ( - 08B191722CF43D900057BC04 /* View */, - 08B191692CF434B80057BC04 /* SignUpStep1Controller.swift */, - 08B1916D2CF434CF0057BC04 /* SignUpStep1Reactor.swift */, - ); - path = Step1; - sourceTree = ""; - }; - 08B191722CF43D900057BC04 /* View */ = { - isa = PBXGroup; - children = ( - 08B1916B2CF434C30057BC04 /* SignUpStep1View.swift */, - 08B191732CF43DF40057BC04 /* SignUpCheckBoxButton.swift */, - 08B191792CF452B30057BC04 /* SignUpTermsView.swift */, - ); - path = View; - sourceTree = ""; - }; - 08B1917B2CF46DD40057BC04 /* TermsDetail */ = { - isa = PBXGroup; - children = ( - 08B1917E2CF46DF20057BC04 /* TermsDetailView.swift */, - 08B1917C2CF46DE30057BC04 /* TermsDetailController.swift */, - ); - path = TermsDetail; - sourceTree = ""; - }; - 08B191802CF48A5C0057BC04 /* Step2 */ = { - isa = PBXGroup; - children = ( - 08B191832CF48A820057BC04 /* SignUpStep2View.swift */, - 08B191812CF48A7B0057BC04 /* SignUpStep2Controller.swift */, - 08B191852CF48A8B0057BC04 /* SignUpStep2Reactor.swift */, - 08B191872CF48FAE0057BC04 /* NickNameState.swift */, - 081898DB2D326DC10067BF01 /* IntroState.swift */, - ); - path = Step2; - sourceTree = ""; - }; - 08B191892CF49FDE0057BC04 /* Step3 */ = { - isa = PBXGroup; - children = ( - 08B191992CF4A63E0057BC04 /* View */, - 08B1918E2CF4A0020057BC04 /* SignUpStep3Controller.swift */, - 08B191902CF4A00E0057BC04 /* SignUpStep3Reactor.swift */, - ); - path = Step3; - sourceTree = ""; - }; - 08B191922CF4A0E00057BC04 /* Step4 */ = { - isa = PBXGroup; - children = ( - 08B191AA2CF5BF8E0057BC04 /* AgeSelectedModal */, - 08B191A92CF5BF860057BC04 /* Main */, - ); - path = Step4; - sourceTree = ""; - }; - 08B191992CF4A63E0057BC04 /* View */ = { - isa = PBXGroup; - children = ( - 08B1919A2CF4A7700057BC04 /* TagSection */, - 08B1918C2CF49FF70057BC04 /* SignUpStep3View.swift */, - ); - path = View; - sourceTree = ""; - }; - 08B1919A2CF4A7700057BC04 /* TagSection */ = { - isa = PBXGroup; - children = ( - 08B1919B2CF4A77C0057BC04 /* TagSection.swift */, - 08B1919D2CF4A7830057BC04 /* TagSectionCell.swift */, - ); - path = TagSection; - sourceTree = ""; - }; - 08B191A82CF5A94B0057BC04 /* View */ = { - isa = PBXGroup; - children = ( - 08B191932CF4A0F00057BC04 /* SignUpStep4View.swift */, - 08B191A62CF5A9430057BC04 /* AgeSelectedButton.swift */, - ); - path = View; - sourceTree = ""; - }; - 08B191A92CF5BF860057BC04 /* Main */ = { - isa = PBXGroup; - children = ( - 08B191A82CF5A94B0057BC04 /* View */, - 08B191952CF4A0FA0057BC04 /* SignUpStep4Controller.swift */, - 08B191972CF4A1010057BC04 /* SignUpStep4Reactor.swift */, - ); - path = Main; - sourceTree = ""; - }; - 08B191AA2CF5BF8E0057BC04 /* AgeSelectedModal */ = { - isa = PBXGroup; - children = ( - 08B191AD2CF5BFA60057BC04 /* AgeSelectedView.swift */, - 08B191AB2CF5BF9D0057BC04 /* AgeSelectedController.swift */, - 08B191AF2CF5BFAE0057BC04 /* AgeSelectedReactor.swift */, - ); - path = AgeSelectedModal; - sourceTree = ""; - }; - 08CBE9FF2D38986E00248007 /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - BD91034F2CF6149D00BBCCAE /* LoginResponseDTO.swift */, - 08CBEA022D38989E00248007 /* PostTokenReissueResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 08CBEA042D38990600248007 /* AuthAPI */ = { - isa = PBXGroup; - children = ( - BD91037A2CF614A900BBCCAE /* LoginResponse.swift */, - 08CBEA052D38991600248007 /* PostTokenReissueResponse.swift */, - ); - path = AuthAPI; - sourceTree = ""; - }; - 08CBEA382D3FABD300248007 /* ToastMaker */ = { - isa = PBXGroup; - children = ( - 08B191A12CF4AE890057BC04 /* ToastMaker.swift */, - 08CBEA392D3FABE100248007 /* ToastView.swift */, - 08CBEA3B2D3FABED00248007 /* BookMarkToastView.swift */, - ); - path = ToastMaker; - sourceTree = ""; - }; - 08DC61F62CF76831002A2F44 /* SignUpComplete */ = { - isa = PBXGroup; - children = ( - 08DC61F72CF76843002A2F44 /* SignUpCompleteView.swift */, - 08DC61F92CF7684F002A2F44 /* SignUpCompleteController.swift */, - 08DC61FB2CF76862002A2F44 /* SignUpCompleteReactor.swift */, - ); - path = SignUpComplete; - sourceTree = ""; - }; - 08DC62012CF8ABDA002A2F44 /* Home */ = { - isa = PBXGroup; - children = ( - 086DD8CA2CFDFE9900B97D3B /* List */, - 086DD8C92CFDFE9100B97D3B /* Main */, - ); - path = Home; - sourceTree = ""; - }; - 08DC62082CF8ADB7002A2F44 /* View */ = { - isa = PBXGroup; - children = ( - 0841BAB22CFABEA900049E31 /* HomePopularCardSection */, - 0841BAAD2CFA38CB00049E31 /* HomeCardSection */, - 0841BAA62CFA353500049E31 /* HomeTitleSection */, - 0841BAA12CFA319400049E31 /* SpacingSection */, - 08DC62092CF8ADFF002A2F44 /* ImageBannerSection */, - 0841BAB72CFAC41300049E31 /* SectionBackGroundDecorationView.swift */, - 08DC62022CF8AC06002A2F44 /* HomeView.swift */, - 0841BAB92CFAE5BE00049E31 /* HomeHeaderView.swift */, - ); - path = View; - sourceTree = ""; - }; - 08DC62092CF8ADFF002A2F44 /* ImageBannerSection */ = { - isa = PBXGroup; - children = ( - 0841BA9D2CFA085700049E31 /* ImageBannerSection */, - 0841BA9E2CFA085F00049E31 /* ImageBannerChildSection */, - ); - path = ImageBannerSection; - sourceTree = ""; - }; - 08DC620E2CF8B36D002A2F44 /* ResponseDTO */ = { - isa = PBXGroup; - children = ( - BD9103512CF6149D00BBCCAE /* BannerPopUpStoreDTO.swift */, - BD9103542CF6149D00BBCCAE /* PopUpStoreResponseDTO.swift */, - BD9103522CF6149D00BBCCAE /* GetHomeInfoResponseDTO.swift */, - ); - path = ResponseDTO; - sourceTree = ""; - }; - 08DE8A0E2D5255000049BCAC /* DetailEmptyCommetSection */ = { - isa = PBXGroup; - children = ( - 08DE8A0F2D5255110049BCAC /* DetailEmptyCommetSection.swift */, - 08DE8A112D5255180049BCAC /* DetailEmptyCommetSectionCell.swift */, - ); - path = DetailEmptyCommetSection; - sourceTree = ""; - }; - 08DE8A132D525A4A0049BCAC /* Strings */ = { - isa = PBXGroup; - children = ( - 08DE8A172D525BA20049BCAC /* Terms.plist */, - ); - path = Strings; - sourceTree = ""; - }; - 08DE8A192D5261CD0049BCAC /* Terms */ = { - isa = PBXGroup; - children = ( - 08DE8A1A2D5261DE0049BCAC /* MyPageTermsController.swift */, - 08DE8A1C2D5261E70049BCAC /* MyPageTermsReactor.swift */, - ); - path = Terms; - sourceTree = ""; - }; - 08DE8A3D2D54DCAF0049BCAC /* MyCommentedPopUpGridSection */ = { - isa = PBXGroup; - children = ( - 08DE8A3E2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift */, - 08DE8A402D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift */, - ); - path = MyCommentedPopUpGridSection; - sourceTree = ""; - }; - 4E685EB52D12CEB6001EF91C /* FillterSheetView */ = { - isa = PBXGroup; - children = ( - 4E685EAA2D12CEB6001EF91C /* BalloonBackgroundView.swift */, - 4E685EAB2D12CEB6001EF91C /* BalloonChipCell.swift */, - 4E685EAD2D12CEB6001EF91C /* FilterBottomSheetReactor.swift */, - 4E685EAE2D12CEB6001EF91C /* FilterBottomSheetView.swift */, - 4E685EAF2D12CEB6001EF91C /* FilterBottomSheetViewController.swift */, - 4E685EB02D12CEB6001EF91C /* FilterCell.swift */, - 4E685EB12D12CEB6001EF91C /* FilterChip.swift */, - 4E685EB22D12CEB6001EF91C /* FilterChipsView.swift */, - ); - path = FillterSheetView; - sourceTree = ""; - }; - 4E685EC02D12CEB6001EF91C /* MapPopupCardView */ = { - isa = PBXGroup; - children = ( - 4E685EBE2D12CEB6001EF91C /* MapPopupCarouselView.swift */, - 4E685EBF2D12CEB6001EF91C /* PopupCardCell.swift */, - ); - path = MapPopupCardView; - sourceTree = ""; - }; - 4E685EC52D12CEB6001EF91C /* StoreListView */ = { - isa = PBXGroup; - children = ( - 4E685EC12D12CEB6001EF91C /* StoreListCell.swift */, - 4E685EC22D12CEB6001EF91C /* StoreListReactor.swift */, - 4E685EC32D12CEB6001EF91C /* StoreListView.swift */, - 4EA998992D21C2FC009DC30B /* StoreListSection.swift */, - 4E685EC42D12CEB6001EF91C /* StoreListViewController.swift */, - ); - path = StoreListView; - sourceTree = ""; - }; - 4E685ECD2D12CEB6001EF91C /* Map */ = { - isa = PBXGroup; - children = ( - 4EC23F432D6F7E6D00558673 /* MapView */, - 4E685EB52D12CEB6001EF91C /* FillterSheetView */, - 4E685EC52D12CEB6001EF91C /* StoreListView */, - 4E685EC02D12CEB6001EF91C /* MapPopupCardView */, - 4EE5A3D12D40E3B100A2469A /* FindMap */, - ); - path = Map; - sourceTree = ""; - }; - 4E755B1B2D2B9ABF00ADFB21 /* Admin */ = { - isa = PBXGroup; - children = ( - 4E755B222D2B9C5D00ADFB21 /* AdminViewController.swift */, - 4E755B242D2B9C6C00ADFB21 /* AdminView.swift */, - 4E755B262D2B9C7C00ADFB21 /* AdminStoreCell.swift */, - 4E755B282D2BA65A00ADFB21 /* AdminReactor.swift */, - 4E9405302D6F7C790002B590 /* AdminRegister */, - 4E94052F2D6F7C670002B590 /* AdminBottomSheet */, - 4EEA1D8E2D352012003E7DE9 /* ImageCell.swift */, - ); - path = Admin; - sourceTree = ""; - }; - 4E94052F2D6F7C670002B590 /* AdminBottomSheet */ = { - isa = PBXGroup; - children = ( - 4E9C12772D2BC7A0006744D6 /* AdminBottomSheetView.swift */, - 4E9C12792D2BC811006744D6 /* AdminBottomSheetViewController.swift */, - 4E6CA4842D34D6ED0034D09A /* AdminBottomSheetReactor.swift */, - ); - path = AdminBottomSheet; - sourceTree = ""; - }; - 4E9405302D6F7C790002B590 /* AdminRegister */ = { - isa = PBXGroup; - children = ( - 4E9C12802D2BE0A6006744D6 /* PopUpStoreRegisterViewController.swift */, - 4E643FC02D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift */, - 4E643FC22D738D930046AF29 /* PopUpStoreRegisterView.swift */, - 4EEA13062DA7CDDA00775256 /* PopUpImagesCollectionView.swift */, - ); - path = AdminRegister; - sourceTree = ""; - }; - 4EA2C93B2D424D2600F4D97C /* MapGuideView */ = { - isa = PBXGroup; - children = ( - 4E6A066F2D42A96100B2A658 /* FullScreenMapViewController.swift */, - 4E9790C42D40E13500210499 /* MapGuideViewController.swift */, - 4EE5A3D22D40E4A600A2469A /* MapGuideReactor.swift */, - ); - path = MapGuideView; - sourceTree = ""; - }; - 4EC23F432D6F7E6D00558673 /* MapView */ = { - isa = PBXGroup; - children = ( - 4E8AA29C2D59A2340029DF75 /* MarkerTooltipView.swift */, - 4E685EC72D12CEB6001EF91C /* MapMarker.swift */, - 4E685EC82D12CEB6001EF91C /* MapReactor.swift */, - 4E685EC92D12CEB6001EF91C /* MapSearchInput.swift */, - 4E685ECB2D12CEB6001EF91C /* MapView.swift */, - 4E685ECC2D12CEB6001EF91C /* MapViewController.swift */, - ); - path = MapView; - sourceTree = ""; - }; - 4EDDEFB22D2D284B00CFAFA5 /* Common */ = { - isa = PBXGroup; - children = ( - 4EAB809C2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift */, - 4EAB809E2D3F8EF50041AF30 /* ViewportBounds.swift */, - 4EED9BAB2D22730400B288E7 /* FilterType.swift */, - 4E6C07052D4B6E56008A962A /* RegionDefinitions.swift */, - 4E685EC62D12CEB6001EF91C /* MapFilterChips.swift */, - 4E9A465F2D55D1270010578A /* MapUtilities.swift */, - 4E6C07092D4B6E81008A962A /* ClusteringManager.swift */, - 4E6C07072D4B6E74008A962A /* ClusteringModels.swift */, - 4EDE57022D5E70650014D924 /* LocationPermissionBottomSheet.swift */, - 4EDDEFB32D2D285900CFAFA5 /* DateTimePickerManager.swift */, - 4EEA1D902D352027003E7DE9 /* ExtendedImage.swift */, - ); - path = Common; - sourceTree = ""; - }; - 4EE5A3D12D40E3B100A2469A /* FindMap */ = { - isa = PBXGroup; - children = ( - 4EA2C93B2D424D2600F4D97C /* MapGuideView */, - ); - path = FindMap; - sourceTree = ""; - }; - BD9103502CF6149D00BBCCAE /* AuthAPI */ = { - isa = PBXGroup; - children = ( - BD91034E2CF6149D00BBCCAE /* AuthAPIEndPoint.swift */, - 08CBE9FF2D38986E00248007 /* ResponseDTO */, - ); - path = AuthAPI; - sourceTree = ""; - }; - BD9103562CF6149D00BBCCAE /* HomeAPI */ = { - isa = PBXGroup; - children = ( - BD9103532CF6149D00BBCCAE /* HomeAPIEndpoint.swift */, - 08DC620E2CF8B36D002A2F44 /* ResponseDTO */, - ); - path = HomeAPI; - sourceTree = ""; - }; - BD91035A2CF6149D00BBCCAE /* Repository */ = { - isa = PBXGroup; - children = ( - 4E755B2E2D2BA7FB00ADFB21 /* AdminRepository.swift */, - BD9103572CF6149D00BBCCAE /* AuthAPIRepositoryImpl.swift */, - BD9103582CF6149D00BBCCAE /* HomeAPIRepository.swift */, - BD9103592CF6149D00BBCCAE /* SignUpRepositoryImpl.swift */, - 086DD8D92CFF194700B97D3B /* UserAPIRepositoryImpl.swift */, - 089952522D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift */, - 083C86772D0EE382003F441C /* CommentAPIRepository.swift */, - 4E685EB62D12CEB6001EF91C /* MapRepository.swift */, - ); - path = Repository; - sourceTree = ""; - }; - BD91035F2CF6149D00BBCCAE /* SignUpAPI */ = { - isa = PBXGroup; - children = ( - BD91035B2CF6149D00BBCCAE /* CheckNickNameRequestDTO.swift */, - BD91035C2CF6149D00BBCCAE /* GetCategoryListResponseDTO.swift */, - BD91035D2CF6149D00BBCCAE /* SignUpAPIEndpoint.swift */, - BD91035E2CF6149D00BBCCAE /* SignUpRequestDTO.swift */, - ); - path = SignUpAPI; - sourceTree = ""; - }; - BD9103602CF6149D00BBCCAE /* Network */ = { - isa = PBXGroup; - children = ( - 08DC62102CF8B446002A2F44 /* SortedRequestDTO.swift */, - 05FBCAC02DABEB1100215BE6 /* MapAPI */, - 05FBCABE2DABE79A00215BE6 /* AdminAPI */, - 083C86712D0EE2A3003F441C /* CommentAPI */, - 089952472D033A0E0022AEF9 /* PopUpAPI */, - 086DD8D42CFF181500B97D3B /* UserAPI */, - BD9103502CF6149D00BBCCAE /* AuthAPI */, - BD9103562CF6149D00BBCCAE /* HomeAPI */, - BD91035F2CF6149D00BBCCAE /* SignUpAPI */, +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 05878B6F2DAD2E10004F81E2 /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + /Localized/Resource/LaunchScreen.storyboard, + Resource/Debug.xcconfig, + Resource/Info.plist, ); - path = Network; - sourceTree = ""; - }; - BD9103752CF614A900BBCCAE /* UseCase */ = { - isa = PBXGroup; - children = ( - BD9103702CF614A900BBCCAE /* HomeAPIUseCaseImpl.swift */, - BD9103722CF614A900BBCCAE /* SignUpAPIUseCaseImpl.swift */, - BD9103742CF614A900BBCCAE /* AuthAPIUseCaseImpl.swift */, - 086DD8DD2CFF19C400B97D3B /* UserAPIUseCaseImpl.swift */, - 4EA2C93E2D424D7400F4D97C /* MapDirectionUseCase.swift */, - 089952542D033D480022AEF9 /* PopUpAPIUseCaseImpl.swift */, - 083C86792D0EE3BB003F441C /* CommentAPIUseCaseImpl.swift */, - 4E755B2A2D2BA76E00ADFB21 /* AdminUseCase.swift */, - 4E685EB82D12CEB6001EF91C /* MapUseCase.swift */, - ); - path = UseCase; - sourceTree = ""; - }; - BD91037B2CF614A900BBCCAE /* Entities */ = { - isa = PBXGroup; - children = ( - 08CBEA042D38990600248007 /* AuthAPI */, - 086F89DE2D1E7CBE00CA4FC9 /* UserAPI */, - BD9103762CF614A900BBCCAE /* BannerPopUpStore.swift */, - 4E685EBB2D12CEB6001EF91C /* MapPopUpStore.swift */, - BD9103772CF614A900BBCCAE /* Category.swift */, - BD9103782CF614A900BBCCAE /* GetHomeInfoResponse.swift */, - BD9103792CF614A900BBCCAE /* PopUpStoreResponse.swift */, - 0899526F2D0474430022AEF9 /* PopUpAPI */, - ); - path = Entities; - sourceTree = ""; - }; - BD91037F2CF614A900BBCCAE /* Repository */ = { - isa = PBXGroup; - children = ( - 4EA2C93C2D424D3300F4D97C /* MapDirectionRepository.swift */, - BD91037C2CF614A900BBCCAE /* AuthRepository.swift */, - ); - path = Repository; - sourceTree = ""; + target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; }; - BD91038F2CF6166800BBCCAE /* View */ = { - isa = PBXGroup; - children = ( - BD91038E2CF6166800BBCCAE /* SplashView.swift */, +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + 05878B702DAD2E10004F81E2 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */ = { + isa = PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet; + buildPhase = BDCA41BB2CF35AC0005EECF6 /* Resources */; + membershipExceptions = ( + Resource/Base.lproj/LaunchScreen.storyboard, ); - path = View; - sourceTree = ""; }; - BD9103912CF6166800BBCCAE /* Splash */ = { - isa = PBXGroup; - children = ( - BD91038F2CF6166800BBCCAE /* View */, - BD9103902CF6166800BBCCAE /* SplashController.swift */, +/* End PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 058789AD2DAD2E10004F81E2 /* Poppool */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (05878B6F2DAD2E10004F81E2 /* PBXFileSystemSynchronizedBuildFileExceptionSet */, 05878B702DAD2E10004F81E2 /* PBXFileSystemSynchronizedGroupBuildPhaseMembershipExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Poppool; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + BDCA41BA2CF35AC0005EECF6 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */, + BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, + BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, + BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, + 082197A12D426DCB0054094A /* Then in Frameworks */, + 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, + 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, + 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, + BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, + BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, + BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, + BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, + 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, + 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, + 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, + BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, + 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, + 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, + 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */, ); - path = Splash; - sourceTree = ""; + runOnlyForDeploymentPostprocessing = 0; }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ BDCA41B42CF35AC0005EECF6 = { isa = PBXGroup; children = ( 05229DD02D99519200D88E73 /* .swiftlint.yml */, - BDCA41BF2CF35AC0005EECF6 /* Poppool */, + 058789AD2DAD2E10004F81E2 /* Poppool */, BDCA41BE2CF35AC0005EECF6 /* Products */, ); sourceTree = ""; @@ -2930,20 +106,6 @@ name = Products; sourceTree = ""; }; - BDCA41BF2CF35AC0005EECF6 /* Poppool */ = { - isa = PBXGroup; - children = ( - BDE30CE02CF87A9700C21E08 /* Poppool.entitlements */, - 083A256B2CF361190099B58E /* Application */, - 083A256D2CF3613C0099B58E /* Presentation */, - 083A259E2CF362310099B58E /* Domain */, - 083A259F2CF362360099B58E /* Data */, - 083A25A02CF3623C0099B58E /* Infrastructure */, - 083A256C2CF361210099B58E /* Resource */, - ); - path = Poppool; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -2960,6 +122,9 @@ ); dependencies = ( ); + fileSystemSynchronizedGroups = ( + 058789AD2DAD2E10004F81E2 /* Poppool */, + ); name = Poppool; packageProductDependencies = ( BDCA41F12CF35D0D005EECF6 /* SnapKit */, @@ -3001,7 +166,6 @@ }; }; buildConfigurationList = BDCA41B82CF35AC0005EECF6 /* Build configuration list for PBXProject "Poppool" */; - compatibilityVersion = "Xcode 14.0"; developmentRegion = ko; hasScannedForEncodings = 0; knownRegions = ( @@ -3027,6 +191,7 @@ 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, ); + preferredProjectObjectVersion = 56; productRefGroup = BDCA41BE2CF35AC0005EECF6 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -3041,19 +206,6 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 08B191562CF41D6F0057BC04 /* PP_splash.json in Resources */, - 08B1914B2CF41D690057BC04 /* GothicA1-Bold.ttf in Resources */, - 08DE8A182D525BA20049BCAC /* Terms.plist in Resources */, - 08B1914D2CF41D690057BC04 /* GothicA1-Medium.ttf in Resources */, - 08B1914F2CF41D690057BC04 /* Poppins-Bold.ttf in Resources */, - 08B191502CF41D690057BC04 /* Poppins-Light.ttf in Resources */, - 08B191552CF41D6F0057BC04 /* PP_loading.json in Resources */, - BDCA41CA2CF35AC1005EECF6 /* Assets.xcassets in Resources */, - BDCA41CD2CF35AC1005EECF6 /* Base in Resources */, - 08B191512CF41D690057BC04 /* Poppins-Medium.ttf in Resources */, - 08B1914C2CF41D690057BC04 /* GothicA1-Light.ttf in Resources */, - 08B1914E2CF41D690057BC04 /* GothicA1-Regular.ttf in Resources */, - 08B191522CF41D690057BC04 /* Poppins-Regular.ttf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -3086,456 +238,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 081898AE2D2CFC230067BF01 /* GetWithdrawlListResponseDTO.swift in Sources */, - 088DE25A2D1458620030FA9E /* DetailCommentImageCell.swift in Sources */, - 089952662D046CCD0022AEF9 /* SearchResultController.swift in Sources */, - 0818993F2D35FBE00067BF01 /* GetRecentPopUpResponseDTO.swift in Sources */, - 08B191962CF4A0FA0057BC04 /* SignUpStep4Controller.swift in Sources */, - 0841BABC2CFB59E200049E31 /* String?+.swift in Sources */, - 0841BA872CF9F62400049E31 /* PreSignedURLDTO.swift in Sources */, - 086F89EC2D2009EB00CA4FC9 /* SubLoginController.swift in Sources */, - 083C86262D087A4E003F441C /* DetailTitleSectionCell.swift in Sources */, - 4E9A46602D55D1270010578A /* MapUtilities.swift in Sources */, - 086DD8DE2CFF19C400B97D3B /* UserAPIUseCaseImpl.swift in Sources */, - 081898C32D30AE2C0067BF01 /* GetMyProfileResponseDTO.swift in Sources */, - 08A2E4802D1BCDE300102313 /* CommentDetailView.swift in Sources */, - 081898BA2D2E5F4C0067BF01 /* MyCommentView.swift in Sources */, - 089952492D033A1C0022AEF9 /* PopUpAPIEndPoint.swift in Sources */, - 083A25C82CF363C00099B58E /* LoginController.swift in Sources */, - 081899292D3500C50067BF01 /* FAQReactor.swift in Sources */, - 083A25B72CF362670099B58E /* RequestEndpoint.swift in Sources */, - 4E6C070A2D4B6E81008A962A /* ClusteringManager.swift in Sources */, - 08A2E47B2D1B06AA00102313 /* ImageDetailController.swift in Sources */, - 083A25B82CF362670099B58E /* IndicatorMaker.swift in Sources */, - 08DE8A102D5255110049BCAC /* DetailEmptyCommetSection.swift in Sources */, - 086F89E62D1FE91800CA4FC9 /* OtherUserCommentSectionCell.swift in Sources */, - 08B191B62CF6092B0057BC04 /* AppleLoginService.swift in Sources */, - 081899542D363E6A0067BF01 /* BookMarkPopUpViewTypeModalReactor.swift in Sources */, - 083C866B2D0ECB4F003F441C /* InstaGuideSectionCell.swift in Sources */, - 083C86202D087445003F441C /* GetPopUpDetailResponse.swift in Sources */, - 4EA2C9432D424DF900F4D97C /* FindDirectionEndPoint.swift in Sources */, - 081898FD2D33D9ED0067BF01 /* GetBlockUserListResponse.swift in Sources */, - 083A25B22CF362670099B58E /* NetworkError.swift in Sources */, - 081899522D363E640067BF01 /* BookMarkPopUpViewTypeModalController.swift in Sources */, - BD9103692CF6149D00BBCCAE /* HomeAPIRepository.swift in Sources */, - 083A25832CF361EF0099B58E /* BaseViewController.swift in Sources */, - 089952752D0475F20022AEF9 /* SearchResultCountSectionCell.swift in Sources */, - 4EECA3942D56770B00A07CCA /* MapPopUpStore.swift in Sources */, - 4E9C12782D2BC7A0006744D6 /* AdminBottomSheetView.swift in Sources */, - 086DD9362D00963900B97D3B /* SearchTitleSection.swift in Sources */, - 086F89D92D1E79E200CA4FC9 /* GetOtherUserCommentListRequestDTO.swift in Sources */, - 083C864F2D0DD3A6003F441C /* AddCommentImageSection.swift in Sources */, - 4EEA13072DA7CDDA00775256 /* PopUpImagesCollectionView.swift in Sources */, - 08B191372CF366680057BC04 /* UICollectionViewCell+.swift in Sources */, - 083A25B42CF362670099B58E /* Responsable.swift in Sources */, - 08CBEA0D2D38ED0D00248007 /* CountButtonView.swift in Sources */, - 0841BAA82CFA354500049E31 /* HomeTitleSection.swift in Sources */, - 08B191782CF442230057BC04 /* UIImage+.swift in Sources */, - 08DE8A122D5255180049BCAC /* DetailEmptyCommetSectionCell.swift in Sources */, - 081898BC2D2E5F510067BF01 /* MyCommentReactor.swift in Sources */, - 0841BA892CF9F62400049E31 /* PreSignedURLResponseDTO.swift in Sources */, - 088DE24A2D12F3360030FA9E /* DetailInfoSection.swift in Sources */, - 081898D02D30EA900067BF01 /* PutUserTailoredInfoRequestDTO.swift in Sources */, - 081898E62D3391CB0067BF01 /* GetMyCommentResponse.swift in Sources */, - 0818991E2D34DF7D0067BF01 /* MyPageNoticeDetailView.swift in Sources */, - BD9103682CF6149D00BBCCAE /* AuthAPIRepositoryImpl.swift in Sources */, - 08DC61F82CF76843002A2F44 /* SignUpCompleteView.swift in Sources */, - BD9103612CF6149D00BBCCAE /* AuthAPIEndPoint.swift in Sources */, - 086DD9422D01EEF700B97D3B /* SearchCountTitleSectionCell.swift in Sources */, - 081898A72D2CC01D0067BF01 /* WithdrawlReasonReactor.swift in Sources */, - 081899332D35F1090067BF01 /* MyPageBookmarkView.swift in Sources */, - 08CBEA3A2D3FABE100248007 /* ToastView.swift in Sources */, - 081899052D34080B0067BF01 /* BlockUserListSectionCell.swift in Sources */, - 4E685EEA2D12CEB6001EF91C /* MapViewController.swift in Sources */, - 4E6C07062D4B6E56008A962A /* RegionDefinitions.swift in Sources */, - 089952582D0347AC0022AEF9 /* SearchCategoryController.swift in Sources */, - 0841BAAA2CFA354C00049E31 /* HomeTitleSectionCell.swift in Sources */, - 08B191612CF430E70057BC04 /* PPProgressView.swift in Sources */, - 086F89EA2D2009E300CA4FC9 /* SubLoginView.swift in Sources */, - 081899352D35F10F0067BF01 /* MyPageBookmarkController.swift in Sources */, - 083A25B32CF362670099B58E /* Requestable.swift in Sources */, - BD9103932CF6166800BBCCAE /* SplashController.swift in Sources */, - 08B1916A2CF434B80057BC04 /* SignUpStep1Controller.swift in Sources */, - 4E685ED52D12CEB6001EF91C /* FilterChip.swift in Sources */, - 083C861C2D087337003F441C /* GetPopUpDetailRequestDTO.swift in Sources */, - BD9103882CF614A900BBCCAE /* GetHomeInfoResponse.swift in Sources */, - 081899022D3407F50067BF01 /* BlockUserListSection.swift in Sources */, - 081899222D34DF8E0067BF01 /* MyPageNoticeDetailReactor.swift in Sources */, - 4E685EDB2D12CEB6001EF91C /* MapAPIEndpoint.swift in Sources */, - 086F89CC2D1E42B000CA4FC9 /* CommentUserBlockController.swift in Sources */, - 08DC62032CF8AC06002A2F44 /* HomeView.swift in Sources */, - 089B4FD82D9A57AE00FC0CC3 /* ImageLoader.swift in Sources */, - BD9103622CF6149D00BBCCAE /* LoginResponseDTO.swift in Sources */, - 083C86642D0EC4A5003F441C /* InstaCommentAddReactor.swift in Sources */, - 08DE8A3F2D54DCC40049BCAC /* MyCommentedPopUpGridSection.swift in Sources */, - 081898C52D30AEF40067BF01 /* GetMyProfileResponse.swift in Sources */, - BD9103922CF6166800BBCCAE /* SplashView.swift in Sources */, - 089B4FDF2D9A8F9A00FC0CC3 /* MemoryStorage.swift in Sources */, - 0899526E2D0474340022AEF9 /* GetSearchPopUpListResponse.swift in Sources */, - 08B191392CF366680057BC04 /* UITableViewCell+.swift in Sources */, - 08A2E48F2D1BF6E500102313 /* CommentListView.swift in Sources */, - 4E755B1D2D2B9AD300ADFB21 /* GetAdminPopUpStoreListResponseDTO.swift in Sources */, - 083A25992CF362090099B58E /* Sectionable.swift in Sources */, - 086DD8E02CFF2C3700B97D3B /* UIImageView+.swift in Sources */, - 081898DA2D32559B0067BF01 /* PutUserProfileRequestDTO.swift in Sources */, - 08B191AC2CF5BF9D0057BC04 /* AgeSelectedController.swift in Sources */, - 4E755B1F2D2B9AE500ADFB21 /* AdminAPIEndpoint.swift in Sources */, - 083C86622D0EC49E003F441C /* InstaCommentAddController.swift in Sources */, - 08B1919C2CF4A77C0057BC04 /* TagSection.swift in Sources */, - BD91038B2CF614A900BBCCAE /* AuthRepository.swift in Sources */, - 08DC61F32CF75037002A2F44 /* KeyChainService.swift in Sources */, - 086DD93B2D009A1C00B97D3B /* CancelableTagSection.swift in Sources */, - 4E755B232D2B9C5D00ADFB21 /* AdminViewController.swift in Sources */, - 08A2E49F2D1C417000102313 /* CommentListTitleSectionCell.swift in Sources */, - 086F89D52D1E6DB100CA4FC9 /* OtherUserCommentController.swift in Sources */, - 083C866E2D0ECB87003F441C /* InstaGuideChildSection.swift in Sources */, - 081898A32D2CC0110067BF01 /* WithdrawlReasonView.swift in Sources */, - 08B1917A2CF452B30057BC04 /* SignUpTermsView.swift in Sources */, - 08B191B82CF6092F0057BC04 /* AuthServiceable.swift in Sources */, - 0841BAB82CFAC41300049E31 /* SectionBackGroundDecorationView.swift in Sources */, - 086F89F52D2269E300CA4FC9 /* MyPageReactor.swift in Sources */, - 089952682D046CD80022AEF9 /* SearchResultReactor.swift in Sources */, - 086DD9402D01EEEB00B97D3B /* SearchCountTitleSection.swift in Sources */, - 083C864C2D0DCF9B003F441C /* AddCommentDescriptionSectionCell.swift in Sources */, - 086DD8CE2CFDFEB000B97D3B /* HomeListView.swift in Sources */, - 08B191C22CF615CA0057BC04 /* KeyPath.swift in Sources */, - 083C86242D087A44003F441C /* DetailTitleSection.swift in Sources */, - 08A2E4822D1BCDEA00102313 /* CommentDetailController.swift in Sources */, - 0841BAA32CFA31A300049E31 /* SpacingSection.swift in Sources */, - 08B191982CF4A1010057BC04 /* SignUpStep4Reactor.swift in Sources */, - 086F8A0A2D2621EE00CA4FC9 /* MyPageMyCommentTitleSection.swift in Sources */, - 089952602D0366C40022AEF9 /* SearchMainController.swift in Sources */, - 081898D42D30F5840067BF01 /* CategoryEditModalController.swift in Sources */, - 088DE2562D144A830030FA9E /* DetailCommentSectionCell.swift in Sources */, - 08B1919E2CF4A7830057BC04 /* TagSectionCell.swift in Sources */, - 081899142D34CAEA0067BF01 /* GetNoticeDetailResponse.swift in Sources */, - 081898D22D30F57D0067BF01 /* CategoryEditModalView.swift in Sources */, - 083C860B2D073A15003F441C /* DetailView.swift in Sources */, - 086F89C52D1E347E00CA4FC9 /* CommentUserInfoController.swift in Sources */, - BD9103862CF614A900BBCCAE /* BannerPopUpStore.swift in Sources */, - 08B191862CF48A8B0057BC04 /* SignUpStep2Reactor.swift in Sources */, - 08DC62052CF8AC0E002A2F44 /* HomeController.swift in Sources */, - 4EA2C93D2D424D3300F4D97C /* MapDirectionRepository.swift in Sources */, - 083C86702D0ECB8E003F441C /* InstaGuideChildSectionCell.swift in Sources */, - 082197B02D4E4E190054094A /* NormalCommentEditView.swift in Sources */, - 086F89D02D1E60A100CA4FC9 /* PostUserBlockRequestDTO.swift in Sources */, - 081898CE2D30D5C60067BF01 /* InfoEditModalReactor.swift in Sources */, - 083C865D2D0DEFD5003F441C /* CommentCheckReactor.swift in Sources */, - BD9103632CF6149D00BBCCAE /* BannerPopUpStoreDTO.swift in Sources */, - 4EDE57032D5E70650014D924 /* LocationPermissionBottomSheet.swift in Sources */, - 081898D62D30F58A0067BF01 /* CategoryEditModalReactor.swift in Sources */, - 081898B32D2D20D70067BF01 /* WithdrawlCompleteView.swift in Sources */, - 086DD92A2D0086AA00B97D3B /* SearchController.swift in Sources */, - 083C860F2D073A23003F441C /* DetailReactor.swift in Sources */, - BD91038A2CF614A900BBCCAE /* LoginResponse.swift in Sources */, - 086F89F92D226EEB00CA4FC9 /* GetMyPageResponse.swift in Sources */, - 083A25BF2CF362770099B58E /* Logger.swift in Sources */, - 08DC61FC2CF76862002A2F44 /* SignUpCompleteReactor.swift in Sources */, - 08DE8A412D54DCCA0049BCAC /* MyCommentedPopUpGridSectionCell.swift in Sources */, - 0818992F2D3506290067BF01 /* FAQDropdownSection.swift in Sources */, - BD9103832CF614A900BBCCAE /* SignUpAPIUseCaseImpl.swift in Sources */, - 4E6CA4852D34D6ED0034D09A /* AdminBottomSheetReactor.swift in Sources */, - 4EE5A3D32D40E4A600A2469A /* MapGuideReactor.swift in Sources */, - 08DC62072CF8AC14002A2F44 /* HomeReactor.swift in Sources */, - 081898F72D33D6B70067BF01 /* BlockUserManageReactor.swift in Sources */, - 082197B22D4E4E200054094A /* NormalCommentEditController.swift in Sources */, - 081899502D363E5C0067BF01 /* BookMarkPopUpViewTypeModalView.swift in Sources */, - 081899452D35FEA10067BF01 /* RecentPopUpSection.swift in Sources */, - 083A259A2CF362090099B58E /* SectionDecorationItem.swift in Sources */, - BD9103892CF614A900BBCCAE /* PopUpStoreResponse.swift in Sources */, - 086F89C72D1E348400CA4FC9 /* CommentUserInfoReactor.swift in Sources */, - 0818990E2D34B68C0067BF01 /* GetNoticeListResponseDTO.swift in Sources */, - 086DD8C82CFDEA9200B97D3B /* UIView+.swift in Sources */, - 08A2E4842D1BCDEF00102313 /* CommentDetailReactor.swift in Sources */, - 4E685ED92D12CEB6001EF91C /* MapRepository.swift in Sources */, - 083A25B62CF362670099B58E /* MultipartEndPoint.swift in Sources */, - 0818993B2D35F1250067BF01 /* MyPageRecentController.swift in Sources */, - 089952462D031E740022AEF9 /* SearchSortedView.swift in Sources */, - 08DC620B2CF8AE0F002A2F44 /* ImageBannerSection.swift in Sources */, - 086DD9302D0090E900B97D3B /* UITextField+.swift in Sources */, - 0818991A2D34D6430067BF01 /* NoticeListSectionCell.swift in Sources */, - 086F89D72D1E6DB700CA4FC9 /* OtherUserCommentReactor.swift in Sources */, - 083C867A2D0EE3BB003F441C /* CommentAPIUseCaseImpl.swift in Sources */, - 4E7823A82D2E84E800AC5110 /* AdminRepository.swift in Sources */, - 086F89DB2D1E7A6C00CA4FC9 /* GetOtherUserCommentedPopUpListResponseDTO.swift in Sources */, - 086F89D32D1E6DA600CA4FC9 /* OtherUserCommentView.swift in Sources */, - 4E685ECE2D12CEB6001EF91C /* BalloonBackgroundView.swift in Sources */, - 4E755B292D2BA65A00ADFB21 /* AdminReactor.swift in Sources */, - 086DD8D62CFF182100B97D3B /* UserAPIEndPoint.swift in Sources */, - 081899372D35F1140067BF01 /* MyPageBookmarkReactor.swift in Sources */, - 4E685EE22D12CEB6001EF91C /* StoreListView.swift in Sources */, - 086F89C32D1E347700CA4FC9 /* CommentUserInfoView.swift in Sources */, - 08B191942CF4A0F00057BC04 /* SignUpStep4View.swift in Sources */, - 08B1918D2CF49FF70057BC04 /* SignUpStep3View.swift in Sources */, - 086F89CE2D1E42B500CA4FC9 /* CommentUserBlockReactor.swift in Sources */, - BD9103642CF6149D00BBCCAE /* GetHomeInfoResponseDTO.swift in Sources */, - 081898E22D338FA40067BF01 /* ListCountButtonSectionCell.swift in Sources */, - 08A2E47D2D1B06B000102313 /* ImageDetailReactor.swift in Sources */, - 08B191B42CF609260057BC04 /* KakaoLoginService.swift in Sources */, - BDCA41C12CF35AC0005EECF6 /* AppDelegate.swift in Sources */, - 083C863A2D0C7F0A003F441C /* CommentSelectedReactor.swift in Sources */, - 0841BAB42CFABED700049E31 /* HomePopularCardSection.swift in Sources */, - 081899182D34D63E0067BF01 /* NoticeListSection.swift in Sources */, - 083C86362D0C7EF4003F441C /* CommentSelectedController.swift in Sources */, - 08A2E4792D1B06A300102313 /* ImageDetailView.swift in Sources */, - 4E7823A92D2E84FB00AC5110 /* AdminStoreCell.swift in Sources */, - 088DE2542D144A7E0030FA9E /* DetailCommentSection.swift in Sources */, - 4E755B2B2D2BA76E00ADFB21 /* AdminUseCase.swift in Sources */, - 083C86292D088080003F441C /* DetailContentSection.swift in Sources */, - 08DC61FA2CF7684F002A2F44 /* SignUpCompleteController.swift in Sources */, - 083C864A2D0DCF96003F441C /* AddCommentDescriptionSection.swift in Sources */, - 4E9790C52D40E13500210499 /* MapGuideViewController.swift in Sources */, - 08B191A42CF5A7030057BC04 /* PPSegmentedControl.swift in Sources */, - 081898DC2D326DC10067BF01 /* IntroState.swift in Sources */, - 0899525C2D0347BD0022AEF9 /* SearchCategoryView.swift in Sources */, - 083C865B2D0DEFCF003F441C /* CommentCheckController.swift in Sources */, - 083C86382D0C7EFC003F441C /* CommentSelectedView.swift in Sources */, - 0818994A2D36322B0067BF01 /* PopUpCardSection.swift in Sources */, - 086F8A052D23CB3300CA4FC9 /* MyPageProfileSection.swift in Sources */, - 081899252D3500B80067BF01 /* FAQView.swift in Sources */, - 086F89EE2D2009F100CA4FC9 /* SubLoginReactor.swift in Sources */, - 08DC61F52CF765B5002A2F44 /* UserDefaultService.swift in Sources */, - 08A2E46C2D15BC5000102313 /* CommentLikeRequestDTO.swift in Sources */, - 086F8A072D23CB3800CA4FC9 /* MyPageProfileSectionCell.swift in Sources */, - 4E643FC12D738D7F0046AF29 /* PopUpStoreRegisterReactor.swift in Sources */, - 08B191AE2CF5BFA60057BC04 /* AgeSelectedView.swift in Sources */, - 4E685EE72D12CEB6001EF91C /* MapSearchInput.swift in Sources */, - 08A2E4912D1BF6EA00102313 /* CommentListController.swift in Sources */, - 0899524D2D033AA70022AEF9 /* GetOpenPopUpListResponseDTO.swift in Sources */, - 081898962D2965C90067BF01 /* ProfileEditView.swift in Sources */, - 083C86602D0EC496003F441C /* InstaCommentAddView.swift in Sources */, - 081898F02D33A3A30067BF01 /* MyCommentSortedModalReactor.swift in Sources */, - 081898E42D3391550067BF01 /* GetMyCommentedPopUpResponseDTO.swift in Sources */, - 088DE25D2D145E3A0030FA9E /* DetailSimilarSection.swift in Sources */, - 4EEA1D912D352027003E7DE9 /* ExtendedImage.swift in Sources */, - 083C863D2D0C8BC4003F441C /* NormalCommentAddController.swift in Sources */, - 08B1916E2CF434CF0057BC04 /* SignUpStep1Reactor.swift in Sources */, - 08B1918F2CF4A0020057BC04 /* SignUpStep3Controller.swift in Sources */, - 08A2E4952D1C078300102313 /* GetPopUpCommentRequestDTO.swift in Sources */, - 08B191912CF4A00E0057BC04 /* SignUpStep3Reactor.swift in Sources */, - 083A259C2CF362090099B58E /* InOutputable.swift in Sources */, - 089952642D0366DA0022AEF9 /* SearchMainReactor.swift in Sources */, - 086F89E02D1E7CC700CA4FC9 /* GetOtherUserCommentedPopUpListResponse.swift in Sources */, - BD9103652CF6149D00BBCCAE /* HomeAPIEndpoint.swift in Sources */, - 086DD8E32CFF356300B97D3B /* HomeCardGridSection.swift in Sources */, - 0841BABE2CFB5AA600049E31 /* Date?+.swift in Sources */, - 08CFD3922D9BDE99004CDD50 /* DiskStorage.swift in Sources */, - 4E685EE12D12CEB6001EF91C /* StoreListReactor.swift in Sources */, - 4E685EE52D12CEB6001EF91C /* MapMarker.swift in Sources */, - 081898FB2D33D9320067BF01 /* GetBlockUserListResponseDTO.swift in Sources */, - 08DC620D2CF8AE16002A2F44 /* ImageBannerSectionCell.swift in Sources */, - 081899412D35FDA10067BF01 /* GetRecentPopUpResponse.swift in Sources */, - 08B1915B2CF41E690057BC04 /* SignUpMainReactor.swift in Sources */, - 4E685ED62D12CEB6001EF91C /* FilterChipsView.swift in Sources */, - 08B191882CF48FAE0057BC04 /* NickNameState.swift in Sources */, - 08A2E48C2D1BDA8A00102313 /* CommentDetailContentSectionCell.swift in Sources */, - 081898982D2965D20067BF01 /* ProfileEditReactor.swift in Sources */, - 083C86562D0DD7EE003F441C /* AddCommentSectionCell.swift in Sources */, - 08B191B22CF5C0A60057BC04 /* PPPicker.swift in Sources */, - 086DD92E2D0086B900B97D3B /* SearchView.swift in Sources */, - 0841BA822CF9F5DF00049E31 /* PreSignedService.swift in Sources */, - 086DD92C2D0086B100B97D3B /* SearchReactor.swift in Sources */, - 08A2E4972D1C07F500102313 /* GetPopUpCommentResponseDTO.swift in Sources */, - BD226D512CF6DB290038C984 /* PPReturnHeaderView.swift in Sources */, - 08B1913F2CF367FA0057BC04 /* UIColor+.swift in Sources */, - 086F8A0C2D2621F400CA4FC9 /* MyPageMyCommentTitleSectionCell.swift in Sources */, - BD91036D2CF6149D00BBCCAE /* SignUpAPIEndpoint.swift in Sources */, - 08A2E4862D1BD85C00102313 /* CommentDetailImageSection.swift in Sources */, - 0818993D2D35F12A0067BF01 /* MyPageRecentReactor.swift in Sources */, - 0899526A2D046CDE0022AEF9 /* SearchResultView.swift in Sources */, - 081898B02D2CFCA40067BF01 /* GetWithdrawlListResponse.swift in Sources */, - 083C86692D0ECB47003F441C /* InstaGuideSection.swift in Sources */, - 081899392D35F11F0067BF01 /* MyPageRecentView.swift in Sources */, - 088DE24C2D12F33B0030FA9E /* DetailInfoSectionCell.swift in Sources */, - 083C86762D0EE2CF003F441C /* PostCommentRequestDTO.swift in Sources */, - 083C861E2D08737F003F441C /* GetPopUpDetailResponseDTO.swift in Sources */, - 086F8A0F2D26297900CA4FC9 /* MyPageCommentSection.swift in Sources */, - 08B1916C2CF434C30057BC04 /* SignUpStep1View.swift in Sources */, - 083A25BC2CF362670099B58E /* ProviderImpl.swift in Sources */, - 4EEA1D8F2D352012003E7DE9 /* ImageCell.swift in Sources */, - 086DD93D2D009A2600B97D3B /* CancelableTagSectionCell.swift in Sources */, - 4E9C127A2D2BC811006744D6 /* AdminBottomSheetViewController.swift in Sources */, - 086DD8DA2CFF194700B97D3B /* UserAPIRepositoryImpl.swift in Sources */, - 4E685ECF2D12CEB6001EF91C /* BalloonChipCell.swift in Sources */, - 083A25CA2CF363C60099B58E /* LoginReactor.swift in Sources */, - 081898BE2D2E5F590067BF01 /* MyCommentController.swift in Sources */, - 083A25BA2CF362670099B58E /* TokenInterceptor.swift in Sources */, - 0841BAC32CFB600800049E31 /* TabbarController.swift in Sources */, - 089952442D031E6D0022AEF9 /* SearchSortedReactor.swift in Sources */, - 086F89E42D1FE91300CA4FC9 /* OtherUserCommentSection.swift in Sources */, - 083C86782D0EE382003F441C /* CommentAPIRepository.swift in Sources */, - 08B191B02CF5BFAE0057BC04 /* AgeSelectedReactor.swift in Sources */, - 4E685EDA2D12CEB6001EF91C /* MapUseCase.swift in Sources */, - 083C863F2D0C8BCE003F441C /* NormalCommentAddView.swift in Sources */, - 4EA9989A2D21C2FC009DC30B /* StoreListSection.swift in Sources */, - 083C86452D0DCDE9003F441C /* AddCommentTitleSectionCell.swift in Sources */, - 083C86412D0C8BD8003F441C /* NormalCommentAddReactor.swift in Sources */, - 089952732D0475E90022AEF9 /* SearchResultCountSection.swift in Sources */, - 08CBEA062D38991600248007 /* PostTokenReissueResponse.swift in Sources */, - 4EED9BAC2D22730400B288E7 /* FilterType.swift in Sources */, - 08B191A02CF4AA0E0057BC04 /* Reactive+.swift in Sources */, - 4E685ED32D12CEB6001EF91C /* FilterBottomSheetViewController.swift in Sources */, - 089952512D033C410022AEF9 /* GetSearchBottomPopUpListResponse.swift in Sources */, - 0841BABA2CFAE5BE00049E31 /* HomeHeaderView.swift in Sources */, - 081898E82D3392480067BF01 /* GetMyCommentRequestDTO.swift in Sources */, - 0841BAAC2CFA35F300049E31 /* UILabel+.swift in Sources */, - 081898F52D33D6B10067BF01 /* BlockUserManageController.swift in Sources */, - 08CBEA3E2D3FF6A100248007 /* PopUpCardView.swift in Sources */, - 086F89F32D2269DE00CA4FC9 /* MyPageController.swift in Sources */, - 0818994C2D3632320067BF01 /* PopUpCardSectionCell.swift in Sources */, - BD91036A2CF6149D00BBCCAE /* SignUpRepositoryImpl.swift in Sources */, - 081898EC2D33A3960067BF01 /* MyCommentSortedModalView.swift in Sources */, - BD91036C2CF6149D00BBCCAE /* GetCategoryListResponseDTO.swift in Sources */, - 086F8A182D265C5F00CA4FC9 /* MyPageListSection.swift in Sources */, - 086F8A1A2D265C6300CA4FC9 /* MyPageListSectionCell.swift in Sources */, - 081899272D3500BF0067BF01 /* FAQController.swift in Sources */, - 08B191672CF432220057BC04 /* PPCancelHeaderView.swift in Sources */, - BD9103812CF614A900BBCCAE /* HomeAPIUseCaseImpl.swift in Sources */, - 08DC62112CF8B446002A2F44 /* SortedRequestDTO.swift in Sources */, - 4EAB809F2D3F8EF50041AF30 /* ViewportBounds.swift in Sources */, - 08B191712CF4398D0057BC04 /* PPLabel.swift in Sources */, - 4E685ED22D12CEB6001EF91C /* FilterBottomSheetView.swift in Sources */, - 0899524F2D033B5A0022AEF9 /* GetSearchPopUpListRequestDTO.swift in Sources */, - 0899525A2D0347B40022AEF9 /* SearchCategoryReactor.swift in Sources */, - 083A25B92CF362670099B58E /* FormDataInterceptor.swift in Sources */, - 4E685EE42D12CEB6001EF91C /* MapFilterChips.swift in Sources */, - BD91036E2CF6149D00BBCCAE /* SignUpRequestDTO.swift in Sources */, - 08B191632CF430F30057BC04 /* PPProgressIndicator.swift in Sources */, - 081898942D2965C20067BF01 /* ProfileEditController.swift in Sources */, - 0818992D2D3506240067BF01 /* FAQDropdownSectionCell.swift in Sources */, - 08B191742CF43DF40057BC04 /* SignUpCheckBoxButton.swift in Sources */, - 086DD8D32CFDFF1500B97D3B /* HomePopUpType.swift in Sources */, - 4E643FC32D738D930046AF29 /* PopUpStoreRegisterView.swift in Sources */, - 081898CC2D30D5BF0067BF01 /* InfoEditModalController.swift in Sources */, - 08DE8A0D2D5236840049BCAC /* PutCommentRequestDTO.swift in Sources */, - 4E685EE92D12CEB6001EF91C /* MapView.swift in Sources */, - 4E755B212D2B9BAB00ADFB21 /* AdminResponseDTO.swift in Sources */, - 08B191382CF366680057BC04 /* UICollectionReusableView+.swift in Sources */, - 4E685EE02D12CEB6001EF91C /* StoreListCell.swift in Sources */, - 4EDDEFB42D2D285900CFAFA5 /* DateTimePickerManager.swift in Sources */, - 08B1915D2CF41E6F0057BC04 /* SignUpMainView.swift in Sources */, - 0841BAA52CFA31A900049E31 /* SpacingSectionCell.swift in Sources */, - 4E685EDF2D12CEB6001EF91C /* PopupCardCell.swift in Sources */, - 083A25B52CF362670099B58E /* Endpoint.swift in Sources */, - 081898A52D2CC0180067BF01 /* WithdrawlReasonController.swift in Sources */, - BDCA41C32CF35AC0005EECF6 /* SceneDelegate.swift in Sources */, - 08B1917F2CF46DF20057BC04 /* TermsDetailView.swift in Sources */, - 4E6A06702D42A96100B2A658 /* FullScreenMapViewController.swift in Sources */, - 4E685EDD2D12CEB6001EF91C /* MapPopUpStoreDTO.swift in Sources */, - 08B191412CF367FF0057BC04 /* UIFont+.swift in Sources */, - 0841BA8E2CF9F8A100049E31 /* ImageBannerChildSection.swift in Sources */, - 083C860D2D073A1C003F441C /* DetailController.swift in Sources */, - 08A2E4932D1BF6EF00102313 /* CommentListReactor.swift in Sources */, - 088DE2582D144B0F0030FA9E /* DetailCommentProfileView.swift in Sources */, - 0818989E2D2BAA610067BF01 /* WithdrawlCheckModalController.swift in Sources */, - 081898902D295DC80067BF01 /* MyPageLogoutSectionCell.swift in Sources */, - 081898EE2D33A39D0067BF01 /* MyCommentSortedModalController.swift in Sources */, - 081898AA2D2CEA2F0067BF01 /* WithdrawlCheckSectionCell.swift in Sources */, - 086DD8CC2CFDFEA800B97D3B /* HomeListController.swift in Sources */, - 086DD9342D00962500B97D3B /* SearchTitleSectionCell.swift in Sources */, - 08B191592CF41E610057BC04 /* SignUpMainController.swift in Sources */, - 0899526C2D0473EC0022AEF9 /* GetSearchPopUpListResponseDTO.swift in Sources */, - 081898B52D2D20E30067BF01 /* WithdrawlCompleteController.swift in Sources */, - 086F89F12D2269D800CA4FC9 /* MyPageView.swift in Sources */, - 081898F32D33D6AC0067BF01 /* BlockUserManageView.swift in Sources */, - 08A2E48A2D1BDA8400102313 /* CommentDetailContentSection.swift in Sources */, - 4E685EDE2D12CEB6001EF91C /* MapPopupCarouselView.swift in Sources */, - 08B1917D2CF46DE30057BC04 /* TermsDetailController.swift in Sources */, - 08CBEA0B2D38DBD600248007 /* LastLoginView.swift in Sources */, - 4E8AA29D2D59A2340029DF75 /* MarkerTooltipView.swift in Sources */, - 0841BAB12CFA38F500049E31 /* HomeCardSectionCell.swift in Sources */, - 4E685EE32D12CEB6001EF91C /* StoreListViewController.swift in Sources */, - 0818990A2D34B3620067BF01 /* MyPageNoticeController.swift in Sources */, - 083C862B2D08808C003F441C /* DetailContentSectionCell.swift in Sources */, - BD91036B2CF6149D00BBCCAE /* CheckNickNameRequestDTO.swift in Sources */, - 0841BAAF2CFA38EA00049E31 /* HomeCardSection.swift in Sources */, - 081898A02D2BAA670067BF01 /* WithdrawlCheckModalReactor.swift in Sources */, - 08B191842CF48A820057BC04 /* SignUpStep2View.swift in Sources */, - 0841BA882CF9F62400049E31 /* PresignedURLRequestDTO.swift in Sources */, - 0841BA8C2CF9F67100049E31 /* PreSignedAPIEndPoint.swift in Sources */, - 08DE8A1B2D5261DE0049BCAC /* MyPageTermsController.swift in Sources */, - 4E78706F2D37CB2200465FC9 /* PopUpStoreRegisterViewController.swift in Sources */, - 081899082D34B35A0067BF01 /* MyPageNoticeView.swift in Sources */, - 4E78706E2D37CB1900465FC9 /* ProfileEditListButton.swift in Sources */, - 081898D82D310C160067BF01 /* PutUserCategoryRequestDTO.swift in Sources */, - 08A2E4992D1C08D600102313 /* GetPopUpCommentResponse.swift in Sources */, - 08CBEA3C2D3FABED00248007 /* BookMarkToastView.swift in Sources */, - 0818988E2D295DC30067BF01 /* MyPageLogoutSection.swift in Sources */, - 089952552D033D480022AEF9 /* PopUpAPIUseCaseImpl.swift in Sources */, - BD9103662CF6149D00BBCCAE /* PopUpStoreResponseDTO.swift in Sources */, - 0841BA802CF9F34100049E31 /* ImageBannerChildSectionCell.swift in Sources */, - 4EA2C93F2D424D7400F4D97C /* MapDirectionUseCase.swift in Sources */, - 088DE2512D13019E0030FA9E /* DetailCommentTitleSectionCell.swift in Sources */, - 083C86592D0DEFC3003F441C /* CommentCheckView.swift in Sources */, - 082197AB2D4E3EEF0054094A /* CommentMyMenuReactor.swift in Sources */, - 083C86732D0EE2B1003F441C /* CommentAPIEndPoint.swift in Sources */, - 081898AC2D2CEA940067BF01 /* WithdrawlCheckSection.swift in Sources */, - 08B1913B2CF366A00057BC04 /* UIApplication+.swift in Sources */, - 083C86542D0DD7E9003F441C /* AddCommentSection.swift in Sources */, - 081898E02D338F9C0067BF01 /* ListCountButtonSection.swift in Sources */, - 4E685EE62D12CEB6001EF91C /* MapReactor.swift in Sources */, - 083A25BB2CF362670099B58E /* Provider.swift in Sources */, - 0899524B2D033A9C0022AEF9 /* GetClosePopUpListResponseDTO.swift in Sources */, - 0818990C2D34B3670067BF01 /* MyPageNoticeReactor.swift in Sources */, - 08B191A72CF5A9430057BC04 /* AgeSelectedButton.swift in Sources */, - 081898CA2D30D5BA0067BF01 /* InfoEditModalView.swift in Sources */, - 081899102D34B7240067BF01 /* GetNoticeListResponse.swift in Sources */, - 089952622D0366D30022AEF9 /* SearchMainView.swift in Sources */, - BD9103872CF614A900BBCCAE /* Category.swift in Sources */, - 083A25822CF361EF0099B58E /* BaseTabmanController.swift in Sources */, - 088DE25F2D145E3F0030FA9E /* DetailSimilarSectionCell.swift in Sources */, - 082197AD2D4E49370054094A /* DeleteCommentRequestDTO.swift in Sources */, - 082197B42D4E4E280054094A /* NormalCommentEditReactor.swift in Sources */, - 081898FF2D33DA440067BF01 /* GetBlockUserListRequestDTO.swift in Sources */, - BD9103852CF614A900BBCCAE /* AuthAPIUseCaseImpl.swift in Sources */, - 08CBEA032D38989E00248007 /* PostTokenReissueResponseDTO.swift in Sources */, - 08DE8A1D2D5261E70049BCAC /* MyPageTermsReactor.swift in Sources */, - 08B191762CF440C40057BC04 /* PPButton.swift in Sources */, - 083A25CC2CF363CB0099B58E /* LoginView.swift in Sources */, - 083A259B2CF362090099B58E /* SectionSupplementaryItem.swift in Sources */, - 4EA2C9412D424D8400F4D97C /* GetPopUpDirectionResponseDTO.swift in Sources */, - 086F89CA2D1E42A700CA4FC9 /* CommentUserBlockView.swift in Sources */, - 086DD8D02CFDFEB900B97D3B /* HomeListReactor.swift in Sources */, - 081899202D34DF880067BF01 /* MyPageNoticeDetailController.swift in Sources */, - 4EAB809D2D3F78AA0041AF30 /* NMFMapViewDelegateProxy.swift in Sources */, - 083C86472D0DCDFB003F441C /* AddCommentTitleSection.swift in Sources */, - 0841BAB62CFABEDC00049E31 /* HomePopularCardSectionCell.swift in Sources */, - 082197A92D4E3EE90054094A /* CommentMyMenuController.swift in Sources */, - 08B191822CF48A7B0057BC04 /* SignUpStep2Controller.swift in Sources */, - 086F8A112D26297D00CA4FC9 /* MyPageCommentSectionCell.swift in Sources */, - 4E685ED12D12CEB6001EF91C /* FilterBottomSheetReactor.swift in Sources */, - 4E6C07082D4B6E74008A962A /* ClusteringModels.swift in Sources */, - 08A2E49D2D1C416800102313 /* CommentListTitleSection.swift in Sources */, - 0818989C2D2BAA570067BF01 /* WithdrawlCheckModalView.swift in Sources */, - 081899122D34CA9E0067BF01 /* GetNoticeDetailResponseDTO.swift in Sources */, - 086DD8D82CFF185200B97D3B /* PostBookmarkPopUpRequestDTO.swift in Sources */, - 089952422D031E650022AEF9 /* SearchSortedController.swift in Sources */, - 089952532D033C940022AEF9 /* PopUpAPIRepositoryImpl.swift in Sources */, - 4E755B252D2B9C6C00ADFB21 /* AdminView.swift in Sources */, - 4E685ED42D12CEB6001EF91C /* FilterCell.swift in Sources */, - 088DE24F2D13019A0030FA9E /* DetailCommentTitleSection.swift in Sources */, - 081898B72D2D23A90067BF01 /* UINavigationController+.swift in Sources */, - 08B191A22CF4AE890057BC04 /* ToastMaker.swift in Sources */, - 082197A72D4E3EE00054094A /* CommentMyMenuView.swift in Sources */, - 086F89F72D226DF600CA4FC9 /* GetMyPageResponseDTO.swift in Sources */, - 083C86512D0DD3AB003F441C /* AddCommentImageSectionCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXVariantGroup section */ - BDCA41CB2CF35AC1005EECF6 /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - BDCA41CC2CF35AC1005EECF6 /* Base */, - 08DE8A162D525A9B0049BCAC /* ko */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - /* Begin XCBuildConfiguration section */ BDCA41E52CF35AC2005EECF6 /* Debug */ = { isa = XCBuildConfiguration; @@ -3661,7 +368,8 @@ }; BDCA41E82CF35AC2005EECF6 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 057151572D9D2E0800260615 /* Debug.xcconfig */; + baseConfigurationReferenceAnchor = 058789AD2DAD2E10004F81E2 /* Poppool */; + baseConfigurationReferenceRelativePath = Resource/Debug.xcconfig; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -3706,7 +414,8 @@ }; BDCA41E92CF35AC2005EECF6 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 057151572D9D2E0800260615 /* Debug.xcconfig */; + baseConfigurationReferenceAnchor = 058789AD2DAD2E10004F81E2 /* Poppool */; + baseConfigurationReferenceRelativePath = Resource/Debug.xcconfig; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/Poppool/Poppool/Domain/Entities/GetBlockUserListResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetBlockUserListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetBlockUserListResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetBlockUserListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetMyCommentResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetMyCommentResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetMyCommentResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetMyPageResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetMyPageResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetMyPageResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetMyPageResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetMyProfileResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetMyProfileResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetMyProfileResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetMyProfileResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetNoticeDetailResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeDetailResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetNoticeDetailResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeDetailResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetNoticeListResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetNoticeListResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetOtherUserCommentedPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetOtherUserCommentedPopUpListResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetOtherUserCommentedPopUpListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetRecentPopUpResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetRecentPopUpResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetRecentPopUpResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetRecentPopUpResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/GetWithdrawlListResponse.swift b/Poppool/Poppool/Domain/Entities/UserAPI/GetWithdrawlListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetWithdrawlListResponse.swift rename to Poppool/Poppool/Domain/Entities/UserAPI/GetWithdrawlListResponse.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterReactor.swift deleted file mode 100644 index 6e355394..00000000 --- a/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterReactor.swift +++ /dev/null @@ -1,306 +0,0 @@ -//// -//// PopUpStoreRegisterReactor.swift -//// Poppool -//// -//// Created by 김기현 on 1/14/25. -//// -// -// import Foundation -// import ReactorKit -// import RxSwift -// import UIKit -// -// final class PopUpStoreRegisterReactor: Reactor { -// -// // MARK: - Action -// enum Action { -// /// 화면 최초 로드 -// case viewDidLoad -// -// /// 사용자 입력값 갱신 -// case updateName(String) -// case updateDesc(String) -// case updateCategory(String) -// case updateAddress(String) -// case updateLatitude(String) // 문자열 -> Double 변환 -// case updateLongitude(String) -// case updateMarkerTitle(String) -// case updateMarkerSnippet(String) -// case updateStartDate(Date) -// case updateEndDate(Date) -// case updateStartTime(Date) -// case updateEndTime(Date) -// -// /// 이미지 관련 -// case addImage(ExtendedImage) // 개별 이미지 추가 -// case removeImage(Int) // 특정 인덱스 이미지 삭제 -// case toggleMainImage(Int) // 대표이미지 토글 -// -// /// "저장/등록" 버튼 탭 -// case tapRegister -// } -// -// // MARK: - Mutation -// enum Mutation { -// /// 폼 데이터 갱신 -// case setName(String) -// case setDesc(String) -// case setCategory(String) -// case setAddress(String) -// case setLatitude(Double) -// case setLongitude(Double) -// case setMarkerTitle(String) -// case setMarkerSnippet(String) -// case setStartDate(Date?) -// case setEndDate(Date?) -// case setStartTime(Date?) -// case setEndTime(Date?) -// -// /// 이미지 변경 -// case addImage(ExtendedImage) -// case removeImageAt(Int) -// case toggleMain(Int) -// -// /// 등록 성공 여부 -// case setRegistered(Bool) -// } -// -// // MARK: - State -// struct State { -// // 폼 입력값 -// var name: String = "" -// var desc: String = "" -// var category: String = "게임" -// var address: String = "" -// var latitude: Double = 0 -// var longitude: Double = 0 -// var markerTitle: String = "" -// var markerSnippet: String = "" -// var startDate: Date? -// var endDate: Date? -// var startTime: Date? -// var endTime: Date? -// -// -// // 이미지 목록 -// var images: [ExtendedImage] = [] -// -// // 최종 등록 여부 -// var isRegistered: Bool = false -// } -// -// // ReactorKit 필수 -// let initialState: State = State() -// -// // 주입받는 의존성 -// private let adminUseCase: AdminUseCase -// -// // disposeBag (mutate 안에서는 ReactorKit이 관리) -// private let disposeBagInternal = DisposeBag() -// -// // MARK: - Init -// init(adminUseCase: AdminUseCase) { -// self.adminUseCase = adminUseCase -// } -// -// // MARK: - mutate -// func mutate(action: Action) -> Observable { -// switch action { -// -// case .viewDidLoad: -// return .empty() -// -// // 텍스트 입력 업데이트 -// case let .updateName(name): -// return .just(.setName(name)) -// -// case let .updateDesc(desc): -// return .just(.setDesc(desc)) -// -// case let .updateCategory(cat): -// return .just(.setCategory(cat)) -// -// case let .updateAddress(addr): -// return .just(.setAddress(addr)) -// -// case let .updateLatitude(latString): -// // 문자 -> Double 변환 -// let lat = Double(latString) ?? 0 -// return .just(.setLatitude(lat)) -// -// case let .updateLongitude(lonString): -// let lon = Double(lonString) ?? 0 -// return .just(.setLongitude(lon)) -// -// case let .updateMarkerTitle(title): -// return .just(.setMarkerTitle(title)) -// -// case let .updateMarkerSnippet(snippet): -// return .just(.setMarkerSnippet(snippet)) -// -// case let .updateStartDate(date): -// return .just(.setStartDate(date)) -// -// case let .updateEndDate(date): -// return .just(.setEndDate(date)) -// -// case let .updateStartTime(time): -// return .just(.setStartTime(time)) -// -// case let .updateEndTime(time): -// return .just(.setEndTime(time)) -// -// // 이미지 관련 -// case let .addImage(img): -// return .just(.addImage(img)) -// -// case let .removeImage(index): -// return .just(.removeImageAt(index)) -// -// case let .toggleMainImage(index): -// return .just(.toggleMain(index)) -// -// // "저장" 액션 -// case .tapRegister: -// return doRegister() -// } -// } -// -// // MARK: - reduce -// func reduce(state: State, mutation: Mutation) -> State { -// var newState = state -// -// switch mutation { -// case let .setName(name): -// newState.name = name -// -// case let .setDesc(desc): -// newState.desc = desc -// -// case let .setCategory(cat): -// newState.category = cat -// -// case let .setAddress(addr): -// newState.address = addr -// -// case let .setLatitude(lat): -// newState.latitude = lat -// -// case let .setLongitude(lon): -// newState.longitude = lon -// -// case let .setMarkerTitle(title): -// newState.markerTitle = title -// -// case let .setMarkerSnippet(snippet): -// newState.markerSnippet = snippet -// -// case let .setStartDate(date): -// newState.startDate = date -// -// case let .setEndDate(date): -// newState.endDate = date -// -// case let .setStartTime(time): -// newState.startTime = time -// -// case let .setEndTime(time): -// newState.endTime = time -// -// // 이미지 -// case let .addImage(img): -// newState.images.append(img) -// -// case let .removeImageAt(index): -// if index >= 0 && index < newState.images.count { -// newState.images.remove(at: index) -// } -// -// case let .toggleMain(idx): -// // 모든 이미지 isMain=false 후 idx만 true -// for i in 0.. Observable { -// // 1) 폼 유효성 검사 -// guard validateForm() else { -// // 유효성 실패시엔 Mutation 없이 .empty() (혹은 에러 Mutation) -// return .empty() -// } -// -// // 2) 대표 vs 서브 이미지 -// let mainImg = currentState.images.first(where: { $0.isMain }) -// ?? currentState.images.first! -// let mainUrl = mainImg.filePath -// let subImages = currentState.images -// .filter { $0.filePath != mainUrl } -// .map { $0.filePath } -// -// // 3) 날짜/시간 -> 문자열 변환 -// let dateFormatter = DateFormatter() -// dateFormatter.dateFormat = "yyyy-MM-dd" -// let startDateStr = currentState.startDate.map { dateFormatter.string(from: $0) } ?? "2025-01-01" -// let endDateStr = currentState.endDate.map { dateFormatter.string(from: $0) } ?? "2025-12-31" -// -// // 4) DTO -// let request = CreatePopUpStoreRequestDTO( -// name: currentState.name, -// categoryId: convertCategoryToId(currentState.category), -// desc: currentState.desc, -// address: currentState.address, -// startDate: startDateStr, -// endDate: endDateStr, -// mainImageUrl: mainUrl, -// bannerYn: false, -// imageUrlList: subImages, -// latitude: currentState.latitude, -// longitude: currentState.longitude, -// markerTitle: currentState.markerTitle, -// markerSnippet: currentState.markerSnippet, -// startDateBeforeEndDate: true -// ) -// -// // 5) 서버 호출 -> 결과에 따라 Mutation -// return adminUseCase.createStore(request: request) -// .map { _ in Mutation.setRegistered(true) } -// .catch { error in -// // 에러 시 로깅/별도 처리 -// return .empty() -// } -// .asObservable() -// } -// -// // MARK: - validateForm() -// private func validateForm() -> Bool { -// // 간단 예시 -// if currentState.name.isEmpty { return false } -// if currentState.desc.isEmpty { return false } -// if currentState.address.isEmpty { return false } -// if currentState.latitude == 0 && currentState.longitude == 0 { return false } -// if currentState.markerTitle.isEmpty || currentState.markerSnippet.isEmpty { return false } -// // 이미지 >=1, 대표 1장 -// if currentState.images.isEmpty { return false } -// if !currentState.images.contains(where: { $0.isMain }) { return false } -// return true -// } -// -// /// 예시: 카테고리 문자열 -> ID 변환 (임의 로직) -// private func convertCategoryToId(_ cat: String) -> Int64 { -// switch cat { -// case "게임": return 101 -// case "라이프스타일": return 102 -// default: return 100 -// } -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterView.swift b/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterView.swift deleted file mode 100644 index 6255da1a..00000000 --- a/Poppool/Poppool/Presentation/Scene/Admin/PopUpStoreRegisterView.swift +++ /dev/null @@ -1,424 +0,0 @@ -//// -//// PopUpStoreRegisterView.swift -//// Poppool -//// -//// Created by 김기현 on 1/14/25. -//// -// -// import UIKit -// import SnapKit -// import Then -// -// final class PopUpStoreRegisterView: UIView { -// -// // MARK: - Callbacks (Closure) -// /// "이미지 추가" 버튼 탭 -// var onAddImageTapped: (() -> Void)? -// /// "전체삭제" 버튼 탭 -// var onRemoveAllTapped: (() -> Void)? -// /// 대표이미지 체크 토글 (콜렉션셀에서 index 전달) -// var onToggleMainImage: ((Int) -> Void)? -// /// 개별 이미지 삭제(index) -// var onDeleteImage: ((Int) -> Void)? -// -// /// "카테고리 선택" 버튼 탭 -// var onCategoryButtonTapped: (() -> Void)? -// /// "기간 선택" 버튼 탭 -// var onPeriodButtonTapped: (() -> Void)? -// /// "시간 선택" 버튼 탭 -// var onTimeButtonTapped: (() -> Void)? -// /// "저장" 버튼 탭 -// var onSaveTapped: (() -> Void)? -// -// // MARK: - Subviews -// // (1) 상단 "이름" 입력 필드 -// private let nameTextField = UITextField().then { -// $0.placeholder = "팝업스토어 이름을 입력해 주세요." -// $0.font = .systemFont(ofSize: 14) -// $0.textColor = .darkGray -// $0.borderStyle = .roundedRect -// } -// -// // (2) 이미지 버튼들 -// private let addImageButton = UIButton(type: .system).then { -// $0.setTitle("이미지 추가", for: .normal) -// $0.setTitleColor(.systemBlue, for: .normal) -// } -// private let removeAllButton = UIButton(type: .system).then { -// $0.setTitle("전체 삭제", for: .normal) -// $0.setTitleColor(.red, for: .normal) -// } -// -// // (3) 이미지 콜렉션뷰 -// private let imagesCollectionView: UICollectionView = { -// let layout = UICollectionViewFlowLayout() -// layout.scrollDirection = .horizontal -// layout.itemSize = CGSize(width: 80, height: 100) -// layout.minimumLineSpacing = 8 -// -// let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) -// cv.backgroundColor = .clear -// cv.register(PopUpImageCell.self, forCellWithReuseIdentifier: PopUpImageCell.identifier) -// return cv -// }() -// -// // (4) 카테고리/기간/시간 버튼 -// private let categoryButton = UIButton(type: .system).then { -// $0.setTitle("카테고리 선택 ▾", for: .normal) -// $0.setTitleColor(.darkGray, for: .normal) -// $0.titleLabel?.font = .systemFont(ofSize:14) -// $0.layer.cornerRadius = 8 -// $0.layer.borderWidth = 1 -// $0.layer.borderColor = UIColor.lightGray.cgColor -// $0.contentHorizontalAlignment = .left -// $0.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) -// } -// private let periodButton = UIButton(type: .system).then { -// $0.setTitle("기간 선택 ▾", for: .normal) -// $0.setTitleColor(.darkGray, for: .normal) -// $0.titleLabel?.font = UIFont.systemFont(ofSize:14) -// $0.layer.cornerRadius = 8 -// $0.layer.borderWidth = 1 -// $0.layer.borderColor = UIColor.lightGray.cgColor -// $0.contentHorizontalAlignment = .left -// $0.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) -// } -// private let timeButton = UIButton(type: .system).then { -// $0.setTitle("시간 선택 ▾", for: .normal) -// $0.setTitleColor(.darkGray, for: .normal) -// $0.titleLabel?.font = UIFont.systemFont(ofSize:14) -// $0.layer.cornerRadius = 8 -// $0.layer.borderWidth = 1 -// $0.layer.borderColor = UIColor.lightGray.cgColor -// $0.contentHorizontalAlignment = .left -// $0.contentEdgeInsets = UIEdgeInsets(top:7, left:8, bottom:7, right:8) -// } -// -// // (5) "저장" 버튼 -// private let saveButton = UIButton(type: .system).then { -// $0.setTitle("저장", for: .normal) -// $0.setTitleColor(.white, for: .normal) -// $0.backgroundColor = .lightGray -// $0.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .bold) -// $0.layer.cornerRadius = 8 -// $0.isEnabled = false -// } -// -// // (6) 스크롤/스택 -// private let scrollView = UIScrollView() -// private let contentView = UIView() -// private let verticalStack = UIStackView().then { -// $0.axis = .vertical -// $0.spacing = 8 -// $0.distribution = .fill -// } -// -// // MARK: - Internal Data -// /// 외부(뷰컨)에서 주입할 "이미지 목록" -// private var images: [ExtendedImage] = [] -// -// // MARK: - Public computed properties -// /// 입력한 이름 get/set -// var storeName: String { -// get { nameTextField.text ?? "" } -// set { nameTextField.text = newValue } -// } -// -// /// 현재 카테고리 버튼 타이틀 -// var categoryText: String { -// get { categoryButton.title(for: .normal) ?? "" } -// set { categoryButton.setTitle(newValue, for: .normal) } -// } -// -// // MARK: - Init -// override init(frame: CGRect) { -// super.init(frame: frame) -// setupLayout() -// setupActions() -// setupCollectionView() -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// // MARK: - Setup -// private func setupLayout() { -// backgroundColor = UIColor(white:0.95, alpha:1) -// -// // 1) 스크롤+컨텐츠 -// addSubview(scrollView) -// scrollView.snp.makeConstraints { make in -// make.top.left.right.equalToSuperview() -// make.bottom.equalToSuperview().offset(-64) // 아래 "저장"버튼을 띄우기 위해 예시 -// } -// scrollView.addSubview(contentView) -// contentView.snp.makeConstraints { make in -// make.edges.equalToSuperview() -// make.width.equalTo(scrollView.snp.width) -// } -// -// // 2) 수직스택 -// contentView.addSubview(verticalStack) -// verticalStack.snp.makeConstraints { make in -// make.top.equalToSuperview().offset(16) -// make.left.right.equalToSuperview().inset(16) -// make.bottom.equalToSuperview() -// } -// -// // (A) 이름 필드 -// let nameRow = makeRow(title: "이름", rightView: nameTextField) -// verticalStack.addArrangedSubview(nameRow) -// -// // (B) 이미지 버튼 (add/remove) -// let buttonStack = UIStackView(arrangedSubviews: [addImageButton, removeAllButton]) -// buttonStack.axis = .horizontal -// buttonStack.distribution = .fillEqually -// buttonStack.spacing = 8 -// verticalStack.addArrangedSubview(buttonStack) -// buttonStack.snp.makeConstraints { make in -// make.height.equalTo(40) -// } -// -// // (C) 콜렉션뷰 -// verticalStack.addArrangedSubview(imagesCollectionView) -// imagesCollectionView.snp.makeConstraints { make in -// make.height.equalTo(100) -// } -// -// // (D) 카테고리 버튼 -// let catRow = makeRow(title: "카테고리", rightView: categoryButton) -// verticalStack.addArrangedSubview(catRow) -// -// // (E) 기간 버튼 -// let periodRow = makeRow(title: "기간", rightView: periodButton) -// verticalStack.addArrangedSubview(periodRow) -// -// // (F) 시간 버튼 -// let timeRow = makeRow(title: "시간", rightView: timeButton) -// verticalStack.addArrangedSubview(timeRow) -// -// // (여기에 위치/마커/설명 등 다른 항목도 같은 방식으로) -// -// // 3) 저장 버튼 (화면 하단 고정) -// addSubview(saveButton) -// saveButton.snp.makeConstraints { make in -// make.left.right.equalToSuperview().inset(16) -// make.bottom.equalTo(safeAreaLayoutGuide).offset(-8) -// make.height.equalTo(44) -// } -// } -// -// private func setupActions() { -// // (1) 이미지추가 -> onAddImageTapped -// addImageButton.addTarget(self, action: #selector(tapAddImage), for: .touchUpInside) -// // (2) 전체삭제 -> onRemoveAllTapped -// removeAllButton.addTarget(self, action: #selector(tapRemoveAll), for: .touchUpInside) -// // (3) 카테고리 -> onCategoryButtonTapped -// categoryButton.addTarget(self, action: #selector(tapCategory), for: .touchUpInside) -// // (4) 기간 -> onPeriodButtonTapped -// periodButton.addTarget(self, action: #selector(tapPeriod), for: .touchUpInside) -// // (5) 시간 -> onTimeButtonTapped -// timeButton.addTarget(self, action: #selector(tapTime), for: .touchUpInside) -// // (6) 저장 -> onSaveTapped -// saveButton.addTarget(self, action: #selector(tapSave), for: .touchUpInside) -// } -// -// private func setupCollectionView() { -// imagesCollectionView.dataSource = self -// imagesCollectionView.delegate = self -// } -// -// // MARK: - Public Methods -// /// 외부에서 "이미지 목록"을 세팅할 때 사용 -// func updateImages(_ newImages: [ExtendedImage]) { -// self.images = newImages -// imagesCollectionView.reloadData() -// updateSaveButtonState() -// } -// -// /// 저장버튼 활성화 업데이트 -// private func updateSaveButtonState() { -// // 예: 이미지가 1장 이상 있을 때만 활성화 -// let hasImages = !images.isEmpty -// saveButton.isEnabled = hasImages -// saveButton.backgroundColor = hasImages ? .systemBlue : .lightGray -// } -// -// // MARK: - Actions -// @objc private func tapAddImage() { -// onAddImageTapped?() -// } -// @objc private func tapRemoveAll() { -// onRemoveAllTapped?() -// } -// @objc private func tapCategory() { -// onCategoryButtonTapped?() -// } -// @objc private func tapPeriod() { -// onPeriodButtonTapped?() -// } -// @objc private func tapTime() { -// onTimeButtonTapped?() -// } -// @objc private func tapSave() { -// onSaveTapped?() -// } -// -// // MARK: - Helpers -// private func makeRow(title: String, rightView: UIView) -> UIView { -// let row = UIView() -// -// // 왼쪽 BG -// let leftBG = UIView() -// leftBG.backgroundColor = UIColor(white:0.94, alpha:1) -// row.addSubview(leftBG) -// leftBG.snp.makeConstraints { make in -// make.top.left.bottom.equalToSuperview() -// make.width.equalTo(80) -// } -// -// // 왼쪽 라벨 -// let label = UILabel() -// label.text = title -// label.font = .systemFont(ofSize:15, weight:.bold) -// label.textColor = .black -// label.textAlignment = .center -// leftBG.addSubview(label) -// label.snp.makeConstraints { make in -// make.centerY.equalToSuperview() -// make.left.right.equalToSuperview().inset(8) -// } -// -// // 오른쪽 BG -// let rightBG = UIView() -// rightBG.backgroundColor = .white -// row.addSubview(rightBG) -// rightBG.snp.makeConstraints { make in -// make.top.bottom.right.equalToSuperview() -// make.left.equalTo(leftBG.snp.right) -// } -// -// // 오른쪽 컨텐츠 (파라미터) -// rightBG.addSubview(rightView) -// rightView.snp.makeConstraints { make in -// make.top.equalToSuperview().offset(8) -// make.bottom.equalToSuperview().offset(-8) -// make.left.equalToSuperview().offset(8) -// make.right.equalToSuperview().offset(-8) -// make.height.equalTo(36).priority(.medium) -// } -// -// // 구분선 -// let sep = UIView() -// sep.backgroundColor = UIColor.lightGray.withAlphaComponent(0.3) -// row.addSubview(sep) -// sep.snp.makeConstraints { make in -// make.left.right.bottom.equalToSuperview() -// make.height.equalTo(1) -// } -// -// return row -// } -// } -// -//// MARK: - UICollectionViewDataSource -// extension PopUpStoreRegisterView: UICollectionViewDataSource { -// func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { -// return images.count -// } -// -// func collectionView(_ collectionView: UICollectionView, -// cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { -// guard let cell = collectionView.dequeueReusableCell( -// withReuseIdentifier: PopUpImageCell.identifier, -// for: indexPath -// ) as? PopUpImageCell else { -// return UICollectionViewCell() -// } -// let item = images[indexPath.item] -// cell.configure(with: item) -// -// cell.onMainCheckToggled = { [weak self] in -// self?.onToggleMainImage?(indexPath.item) -// } -// cell.onDeleteTapped = { [weak self] in -// self?.onDeleteImage?(indexPath.item) -// } -// return cell -// } -// } -// -//// MARK: - UICollectionViewDelegateFlowLayout -// extension PopUpStoreRegisterView: UICollectionViewDelegateFlowLayout { -// // 혹시 셀 사이즈/간격을 동적으로 조정하고 싶다면 여기서 -// } -// -//// MARK: - PopUpImageCell (같은 파일) -// final class PopUpImageCell: UICollectionViewCell { -// static let identifier = "PopUpImageCell" -// -// // 콜백 -// var onMainCheckToggled: (() -> Void)? -// var onDeleteTapped: (() -> Void)? -// -// private let thumbImageView = UIImageView().then { -// $0.contentMode = .scaleAspectFill -// $0.layer.cornerRadius = 6 -// $0.clipsToBounds = true -// } -// private let mainCheckButton = UIButton(type: .system).then { -// $0.setTitle("대표", for: .normal) -// $0.setTitleColor(.white, for: .normal) -// $0.backgroundColor = .gray -// $0.titleLabel?.font = .systemFont(ofSize:12, weight:.medium) -// $0.layer.cornerRadius = 4 -// } -// private let deleteButton = UIButton(type: .system).then { -// $0.setTitle("삭제", for: .normal) -// $0.setTitleColor(.red, for: .normal) -// $0.titleLabel?.font = .systemFont(ofSize:12, weight:.medium) -// } -// -// override init(frame: CGRect) { -// super.init(frame: frame) -// contentView.addSubview(thumbImageView) -// contentView.addSubview(mainCheckButton) -// contentView.addSubview(deleteButton) -// -// thumbImageView.snp.makeConstraints { make in -// make.top.left.right.equalToSuperview() -// make.height.equalTo(thumbImageView.snp.width) -// } -// mainCheckButton.snp.makeConstraints { make in -// make.top.equalTo(thumbImageView.snp.bottom).offset(4) -// make.left.equalToSuperview() -// make.width.equalTo(40) -// make.height.equalTo(24) -// } -// deleteButton.snp.makeConstraints { make in -// make.top.equalTo(thumbImageView.snp.bottom).offset(4) -// make.right.equalToSuperview() -// make.width.equalTo(40) -// make.height.equalTo(24) -// } -// -// mainCheckButton.addTarget(self, action: #selector(didTapMainCheck), for: .touchUpInside) -// deleteButton.addTarget(self, action: #selector(didTapDelete), for: .touchUpInside) -// } -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// @objc private func didTapMainCheck() { -// onMainCheckToggled?() -// } -// @objc private func didTapDelete() { -// onDeleteTapped?() -// } -// -// func configure(with item: ExtendedImage) { -// thumbImageView.image = item.image -// mainCheckButton.backgroundColor = item.isMain ? .systemRed : .gray -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/CustomClusterRenderer.swift b/Poppool/Poppool/Presentation/Scene/Map/CustomClusterRenderer.swift deleted file mode 100644 index 82fa4b7d..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/CustomClusterRenderer.swift +++ /dev/null @@ -1,15 +0,0 @@ -// import GoogleMaps -// -// class CustomClusterRenderer: GMUDefaultClusterRenderer { -// override func willRenderMarker(_ marker: GMSMarker) { -// super.willRenderMarker(marker) -// // 클러스터일 경우 처리 -// if let cluster = marker.userData as? GMUCluster { -// let customView = MapMarker() -// // 예: 클러스터의 이름과 count를 injection -// customView.injection(with: .init(isSelected: false, isCluster: true, regionName: cluster.clusterIdentifier, count: cluster.count)) -// // marker의 iconView에 적용 -// marker.iconView = customView -// } -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/CategoryFilterView.swift b/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/CategoryFilterView.swift deleted file mode 100644 index 06fd3d99..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/CategoryFilterView.swift +++ /dev/null @@ -1,35 +0,0 @@ -// import UIKit -// import SnapKit -// -// final class CategoryFilterView: UIView { -// private let stackView = UIStackView() -// private let categories = ["게임", "라이프스타일", "엔터테인먼트", "패션", "음식/요리", "키즈"] -// -// override init(frame: CGRect) { -// super.init(frame: frame) -// setupLayout() -// setupCategories() -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// private func setupLayout() { -// addSubview(stackView) -// stackView.axis = .vertical -// stackView.spacing = 12 -// stackView.snp.makeConstraints { make in -// make.top.equalToSuperview().offset(20) -// make.leading.trailing.equalToSuperview().inset(20) -// } -// } -// -// private func setupCategories() { -// for c in categories { -// let chip = FilterChip() -// chip.setTitle(c, style: .inactive) -// stackView.addArrangedSubview(chip) -// } -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterTabsView.swift b/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterTabsView.swift deleted file mode 100644 index 99dfe49c..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterTabsView.swift +++ /dev/null @@ -1,39 +0,0 @@ -// import UIKit -// import RxSwift -// import RxCocoa -// -// final class FilterTabsView: UIView { -// private let tabs = ["지역", "카테고리"] -// let segmentedControl = UISegmentedControl() -// -// var rx: Reactive { -// return Reactive(self) -// } -// -// override init(frame: CGRect) { -// super.init(frame: frame) -// setupTabs() -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// private func setupTabs() { -// tabs.enumerated().forEach { index, title in -// segmentedControl.insertSegment(withTitle: title, at: index, animated: false) -// } -// segmentedControl.selectedSegmentIndex = 0 -// addSubview(segmentedControl) -// -// segmentedControl.snp.makeConstraints { make in -// make.edges.equalToSuperview() -// } -// } -// } -// -// extension Reactive where Base: FilterTabsView { -// var selectedIndex: ControlProperty { -// return base.segmentedControl.rx.selectedSegmentIndex -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/LocationFilterView.swift b/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/LocationFilterView.swift deleted file mode 100644 index 567dfd1d..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/LocationFilterView.swift +++ /dev/null @@ -1,40 +0,0 @@ -// import UIKit -// import SnapKit -// -// final class LocationFilterView: UIView { -// private let scrollView = UIScrollView() -// private let contentStack = UIStackView() -// -// private let regions = ["서울", "경기", "인천", "부산", "제주"] -// -// override init(frame: CGRect) { -// super.init(frame: frame) -// setupLayout() -// setupRegions() -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// private func setupLayout() { -// addSubview(scrollView) -// scrollView.snp.makeConstraints { $0.edges.equalToSuperview() } -// -// scrollView.addSubview(contentStack) -// contentStack.axis = .horizontal -// contentStack.spacing = 8 -// contentStack.snp.makeConstraints { make in -// make.edges.equalToSuperview().inset(20) -// make.height.equalTo(40) -// } -// } -// -// private func setupRegions() { -// for r in regions { -// let chip = FilterChip() -// chip.setTitle(r, style: .inactive) -// contentStack.addArrangedSubview(chip) -// } -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/GuideMapViewController.swift b/Poppool/Poppool/Presentation/Scene/Map/FindMap/GuideMapViewController.swift deleted file mode 100644 index 8b137891..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/FindMap/GuideMapViewController.swift +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideAppService.swift b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideAppService.swift deleted file mode 100644 index 048c1f2f..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideAppService.swift +++ /dev/null @@ -1,71 +0,0 @@ -import CoreLocation -import UIKit - -enum MapAppType { - case naver - case kakao - case apple - - static func from(string: String) -> MapAppType? { - switch string.lowercased() { - case "naver": - return .naver - case "kakao": - return .kakao - case "apple", "applemap": - return .apple - default: - return nil - } - } - - func urlScheme(coordinate: CLLocationCoordinate2D, name: String, address: String) -> String { - let encodedName = name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" - let encodedAddress = address.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "" - - switch self { - case .naver: - return "nmap://place?lat=\(coordinate.latitude)&lng=\(coordinate.longitude)&name=\(encodedName)&addr=\(encodedAddress)&appname=com.poppool.app" - case .kakao: - return "kakaomap://look?p=\(coordinate.latitude),\(coordinate.longitude)" - case .apple: - return "maps://?q=\(encodedName)&ll=\(coordinate.latitude),\(coordinate.longitude)&z=16" - } - } - - var appStoreURL: String { - switch self { - case .naver: - return "https://apps.apple.com/kr/app/id311867728" - case .kakao: - return "https://apps.apple.com/kr/app/id304608425" - case .apple: - return "https://apps.apple.com/kr/app/id1108185179" - } - } -} - -class MapAppService { - static func openMapApp(_ appTypeString: String, coordinate: CLLocationCoordinate2D, name: String, address: String) -> Observable { - guard let appType = MapAppType.from(string: appTypeString) else { - return Observable.just("지원하지 않는 맵 앱입니다.") - } - - let urlScheme = appType.urlScheme(coordinate: coordinate, name: name, address: address) - - Logger.log(message: "🗺 맵 앱 열기 시도: \(urlScheme)", category: .debug) - - if let url = URL(string: urlScheme), UIApplication.shared.canOpenURL(url) { - Logger.log(message: "✅ \(appType) 앱 실행", category: .debug) - UIApplication.shared.open(url, options: [:], completionHandler: nil) - return Observable.empty() - } else { - Logger.log(message: "❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) - if let appStoreURL = URL(string: appType.appStoreURL) { - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) - return Observable.just("\(appTypeString) 앱이 설치되어 있지 않아 앱스토어로 이동합니다.") - } - return Observable.just("앱을 열 수 없습니다.") - } - } -} diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapStoreCard.swift b/Poppool/Poppool/Presentation/Scene/Map/MapStoreCard.swift deleted file mode 100644 index a4c86d8f..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/MapStoreCard.swift +++ /dev/null @@ -1,116 +0,0 @@ -// import UIKit -// import SnapKit -// -// final class MapStoreCard: UIView { -// // MARK: - Components -// private let containerView: UIView = { -// let view = UIView() -// view.backgroundColor = .white -// view.layer.cornerRadius = 16 -// view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] -// view.layer.shadowColor = UIColor.black.cgColor -// view.layer.shadowOpacity = 0.1 -// view.layer.shadowRadius = 4 -// view.layer.shadowOffset = CGSize(width: 0, height: -2) -// return view -// }() -// -// private let thumbnailImageView: UIImageView = { -// let iv = UIImageView() -// iv.contentMode = .scaleAspectFill -// iv.clipsToBounds = true -// iv.layer.cornerRadius = 8 -// return iv -// }() -// -// private let categoryLabel = PPLabel(style: .regular, fontSize: 12) -// private let titleLabel: PPLabel = { -// let label = PPLabel(style: .bold, fontSize: 16) -// label.numberOfLines = 2 // 최대 2줄로 제한 -// return label -// }() -// -// private let locationLabel = PPLabel(style: .regular, fontSize: 12) -// private let dateLabel = PPLabel(style: .regular, fontSize: 12) -// -// // MARK: - Init -// init() { -// super.init(frame: .zero) -// setupLayout() -// configureUI() -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// } -// -//// MARK: - Setup -// private extension MapStoreCard { -// func setupLayout() { -// addSubview(containerView) -// -// [thumbnailImageView, categoryLabel, titleLabel, locationLabel, dateLabel].forEach { -// containerView.addSubview($0) -// } -// -// containerView.snp.makeConstraints { make in -// make.edges.equalToSuperview().inset(16) -// } -// -// -// thumbnailImageView.snp.makeConstraints { make in -// make.leading.equalToSuperview() -// make.top.bottom.equalToSuperview().inset(20) -// make.width.equalTo(80) // 고정된 너비 -// } -// -// categoryLabel.snp.makeConstraints { make in -// make.leading.equalTo(thumbnailImageView.snp.trailing).offset(16) -// make.trailing.equalToSuperview().inset(16) -// make.top.equalTo(thumbnailImageView) -// } -// -// titleLabel.snp.makeConstraints { make in -// make.leading.equalTo(categoryLabel) -// make.trailing.equalToSuperview().inset(16) -// make.top.equalTo(categoryLabel.snp.bottom).offset(8) -// } -// -// locationLabel.snp.makeConstraints { make in -// make.leading.equalTo(categoryLabel) -// make.top.equalTo(titleLabel.snp.bottom).offset(4) -// } -// -// dateLabel.snp.makeConstraints { make in -// make.leading.equalTo(locationLabel.snp.trailing).offset(8) -// make.centerY.equalTo(locationLabel) -// } -// -// } -// -// func configureUI() { -// categoryLabel.textColor = .g700 -// locationLabel.textColor = .g500 -// dateLabel.textColor = .g500 -// } -// } -// -//// MARK: - Inputable -// extension MapStoreCard: Inputable { -// struct Input { -// let image: UIImage? -// let category: String -// let title: String -// let location: String -// let date: String -// } -// -// func injection(with input: Input) { -// thumbnailImageView.image = input.image ?? UIImage(named: "default_thumbnail") -// categoryLabel.text = input.category -// titleLabel.text = input.title -// locationLabel.text = input.location -// dateLabel.text = input.date -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/MicroClusterMarkerView.swift b/Poppool/Poppool/Presentation/Scene/Map/MicroClusterMarkerView.swift deleted file mode 100644 index f7d21f38..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/MicroClusterMarkerView.swift +++ /dev/null @@ -1,2 +0,0 @@ -import Foundation -import UIKit diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListPanelLayout.swift b/Poppool/Poppool/Presentation/Scene/Map/StoreListPanelLayout.swift deleted file mode 100644 index 5dd5201d..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/StoreListPanelLayout.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// import FloatingPanel -// import UIKit -// -// class StoreListPanelLayout: FloatingPanelLayout { -// let position: FloatingPanelPosition = .bottom -// let initialState: FloatingPanelState = .half -// -// var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] { -// return [ -// .full: FloatingPanelLayoutAnchor(absoluteInset: 120, edge: .top, referenceGuide: .superview), -// .half: FloatingPanelLayoutAnchor(fractionalInset: 0.6, edge: .bottom, referenceGuide: .safeArea), -// .tip: FloatingPanelLayoutAnchor(absoluteInset: -100, edge: .bottom, referenceGuide: .safeArea) // 완전히 내림 -// ] -// } -// -// func backdropAlpha(for state: FloatingPanelState) -> CGFloat { -// return 0.0 -// } -// -// func shouldMove(for proposedTargetState: FloatingPanelState) -> Bool { -// return true -// } -// -// var cornerRadius: CGFloat { return 0 } -// -// func surfaceLayout(for size: CGSize) -> NSCollectionLayoutDimension { -// return .fractionalWidth(1.0) -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListHeaderView.swift b/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListHeaderView.swift deleted file mode 100644 index 65f574b0..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListHeaderView.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// import UIKit -// import SnapKit -// import RxSwift -// -// -// class StoreListHeaderView: UICollectionReusableView { -// static let identifier = "StoreListHeaderView" -// -// let searchInput = MapSearchInput() -// let filterChips = MapFilterChips() -// -// var disposeBag = DisposeBag() -// override init(frame: CGRect) { -// super.init(frame: frame) -//// print("[DEBUG] StoreListHeaderView 초기화 - frame: \(frame)") -// setupLayout() -// searchInput.setBackgroundColorForList() -// -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } -// -// private func setupLayout() { -// -// backgroundColor = .white -// addSubview(searchInput) -// addSubview(filterChips) -// -// -// searchInput.snp.makeConstraints { make in -// make.top.equalToSuperview().offset(16) -// make.left.equalToSuperview().offset(20) -// make.right.equalToSuperview().inset(16) -// make.height.equalTo(37) -// -// -// } -// -// filterChips.snp.makeConstraints { make in -// make.top.equalTo(searchInput.snp.bottom).offset(11) -// make.left.right.equalToSuperview().inset(20) -// make.height.equalTo(36) -// make.bottom.equalToSuperview().offset(-20) -// } -// -// -// layoutIfNeeded() -// -// -// } -// } diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListPanelLayout.swift b/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListPanelLayout.swift deleted file mode 100644 index ab3b649d..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListPanelLayout.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// StoreListPanelLayout.swift -// Poppool -// -// Created by 김기현 on 12/20/24. -// - -import FloatingPanel -import UIKit - -class StoreListPanelLayout: FloatingPanelLayout { - let position: FloatingPanelPosition = .bottom - let initialState: FloatingPanelState = .half - - var anchors: [FloatingPanelState: FloatingPanelLayoutAnchoring] { - return [ - // 스택뷰 (검색바 + 필터칩) 바로 아래에 맞춰서 올라오도록 설정 - .full: FloatingPanelLayoutAnchor(absoluteInset: 90, edge: .top, referenceGuide: .safeArea), - .half: FloatingPanelLayoutAnchor(fractionalInset: 0.6, edge: .bottom, referenceGuide: .safeArea) - ] - } - - func backdropAlpha(for state: FloatingPanelState) -> CGFloat { - return 0.0 - } - - // 스크롤 뷰와의 상호작용 방지 - func shouldMove(for proposedTargetState: FloatingPanelState) -> Bool { - return true - } - - // 패널의 모서리 둥글기 설정 - var cornerRadius: CGFloat { return 0 } // 페이지처럼 보이도록 모서리 둥글기 제거 - - // 화면 전체를 덮도록 surface 레이아웃 설정 - func surfaceLayout(for size: CGSize) -> NSCollectionLayoutDimension { - return .fractionalWidth(1.0) - } -} diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreLocation.swift b/Poppool/Poppool/Presentation/Scene/Map/StoreLocation.swift deleted file mode 100644 index e8e7b76c..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/StoreLocation.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// StoreLocation.swift -// Poppool -// -// Created by 김기현 on 2/7/25. -// - -import Foundation diff --git a/Poppool/Poppool/Presentation/Scene/Map/TestViewController.swift b/Poppool/Poppool/Presentation/Scene/Map/TestViewController.swift deleted file mode 100644 index 82bd615b..00000000 --- a/Poppool/Poppool/Presentation/Scene/Map/TestViewController.swift +++ /dev/null @@ -1,153 +0,0 @@ -// -// ViewController.swift -// Poppool -// -// Created by Porori on 11/24/24. -// - -import UIKit - -import RxCocoa -import RxGesture -import RxSwift -import SnapKit - -class TestViewController: UIViewController { - - private let topView: UIView = { - let view = UIView() - view.backgroundColor = .w100 - view.alpha = 0 - return view - }() - - private let topViewLabel: UILabel = { - let label = UILabel() - label.text = "Top View Label" - return label - }() - - private let bottomView: UIView = { - let view = UIView() - view.backgroundColor = .w100 - return view - }() - - private let gestureBar: UIView = { - let view = UIView() - view.backgroundColor = .g200 - return view - }() - - private let listButton: PPButton = { - return PPButton(style: .secondary, text: "리스트 버튼") - }() - - private let disposeBag = DisposeBag() - - private var bottomViewTopConstraints: Constraint? - - enum ModalState { - case top - case middle - case bottom - } - - var modalState: ModalState = .bottom - - override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .blue - setUpConstratins() - bind() - } - - func setUpConstratins() { - view.addSubview(listButton) - listButton.snp.makeConstraints { make in - make.leading.trailing.bottom.equalToSuperview().inset(20) - make.height.equalTo(50) - } - - view.addSubview(topView) - topView.snp.makeConstraints { make in - make.top.leading.trailing.equalToSuperview() - make.bottom.equalTo(view.safeAreaLayoutGuide.snp.top).offset(104) - } - - topView.addSubview(topViewLabel) - topViewLabel.snp.makeConstraints { make in - make.center.equalToSuperview() - } - - view.addSubview(bottomView) - bottomView.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview() - bottomViewTopConstraints = make.top.equalTo(topView.snp.bottom).offset(700).constraint - make.height.equalTo(700) - } - - bottomView.addSubview(gestureBar) - gestureBar.snp.makeConstraints { make in - make.width.equalTo(50) - make.height.equalTo(20) - make.top.equalToSuperview().inset(20) - make.centerX.equalToSuperview() - } - } - - func bind() { - listButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - print("listButtonTapped") - UIView.animate(withDuration: 0.3) { - owner.bottomViewTopConstraints?.update(offset: 124) - owner.topView.alpha = 0 - owner.view.layoutIfNeeded() - } - } - .disposed(by: disposeBag) - - gestureBar.rx.swipeGesture(.up) - .skip(1) - .withUnretained(self) - .subscribe { (owner, _) in - print("swipe up") - UIView.animate(withDuration: 0.3) { - owner.bottomViewTopConstraints?.update(offset: 0) - owner.topView.alpha = 1 - owner.view.layoutIfNeeded() - owner.modalState = .top - } - } - .disposed(by: disposeBag) - - gestureBar.rx.swipeGesture(.down) - .skip(1) - .withUnretained(self) - .subscribe { (owner, _) in - print("swipe down") - switch owner.modalState { - case .top: - UIView.animate(withDuration: 0.3) { - owner.bottomViewTopConstraints?.update(offset: 124) - owner.topView.alpha = 0 - owner.view.layoutIfNeeded() - owner.modalState = .middle - } - case .middle: - UIView.animate(withDuration: 0.3) { - owner.bottomViewTopConstraints?.update(offset: 700) - owner.topView.alpha = 0 - owner.view.layoutIfNeeded() - owner.modalState = .bottom - } - case .bottom: - break - } - - } - .disposed(by: disposeBag) - } -} From fd2d10c857a8f1e026b1cbd4f97bd52bd638be62 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 20:53:00 +0900 Subject: [PATCH 104/393] =?UTF-8?q?refactor/#112:=20KeyPath=20=E2=86=92=20?= =?UTF-8?q?Secrets=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 동일한 객체 네이밍이 존해서 변경함 --- Poppool/Poppool/Application/AppDelegate.swift | 4 +- .../Network/AdminAPI/AdminAPIEndpoint.swift | 16 +++---- .../Network/AuthAPI/AuthAPIEndPoint.swift | 4 +- .../CommentAPI/CommentAPIEndPoint.swift | 6 +-- .../Network/HomeAPI/HomeAPIEndpoint.swift | 8 ++-- .../MapAPI/FindDirectionEndPoint.swift | 2 +- .../Data/Network/MapAPI/MapAPIEndpoint.swift | 4 +- .../Network/PopUpAPI/PopUpAPIEndPoint.swift | 10 ++--- .../Network/SignUpAPI/SignUpAPIEndpoint.swift | 6 +-- .../Network/UserAPI/UserAPIEndPoint.swift | 42 +++++++++---------- .../PreSignedAPIEndPoint.swift | 6 +-- .../PreSignedService/PreSignedService.swift | 2 +- .../Presentation/Extension/UIImageView+.swift | 4 +- .../Scene/Admin/AdminStoreCell.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 2 +- .../Resource/{KeyPath.swift => Secrets.swift} | 2 +- 16 files changed, 60 insertions(+), 60 deletions(-) rename Poppool/Poppool/Resource/{KeyPath.swift => Secrets.swift} (98%) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 5b018dd2..bb8a380f 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -7,8 +7,8 @@ import UIKit class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { - KakaoSDK.initSDK(appKey: KeyPath.kakaoAuthAppKey) - NMFAuthManager.shared().clientId = KeyPath.naverMapClientID + KakaoSDK.initSDK(appKey: Secrets.kakaoAuthAppKey) + NMFAuthManager.shared().clientId = Secrets.naverMapClientID let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() diff --git a/Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift b/Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift index b5ba885d..2f9a6433 100644 --- a/Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift @@ -14,7 +14,7 @@ struct AdminAPIEndpoint { size: size ) return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores/list", method: .get, queryParameters: params @@ -26,7 +26,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .get, queryParameters: ["popUpStoreId": id] @@ -38,7 +38,7 @@ struct AdminAPIEndpoint { request: CreatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .post, bodyParameters: request @@ -50,7 +50,7 @@ struct AdminAPIEndpoint { request: UpdatePopUpStoreRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .put, bodyParameters: request @@ -62,7 +62,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/popup-stores", method: .delete, queryParameters: ["popUpStoreId": id] @@ -74,7 +74,7 @@ struct AdminAPIEndpoint { request: CreateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice", method: .post, bodyParameters: request @@ -86,7 +86,7 @@ struct AdminAPIEndpoint { request: UpdateNoticeRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice/\(id)", method: .put, bodyParameters: request @@ -97,7 +97,7 @@ struct AdminAPIEndpoint { id: Int64 ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/admin/notice/\(id)", method: .delete ) diff --git a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift index 4647490b..58d72570 100644 --- a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift @@ -18,7 +18,7 @@ struct AuthAPIEndPoint { /// - Returns: Endpoint static func auth_tryLogin(with userCredential: Encodable, path: String) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/auth/\(path)", method: .post, bodyParameters: userCredential, @@ -28,7 +28,7 @@ struct AuthAPIEndPoint { static func postTokenReissue() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/auth/token/reissue", method: .post ) diff --git a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift index 02f4d689..600d3978 100644 --- a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift @@ -13,7 +13,7 @@ struct CommentAPIEndPoint { static func postCommentAdd(request: PostCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .post, bodyParameters: request @@ -22,7 +22,7 @@ struct CommentAPIEndPoint { static func deleteComment(request: DeleteCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct CommentAPIEndPoint { static func editComment(request: PutCommentRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/comments", method: .put, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift index 30e19486..accb193d 100644 --- a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift @@ -13,7 +13,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/home", method: .get, queryParameters: request @@ -24,7 +24,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/home/popular/popup-stores", method: .get, queryParameters: request @@ -35,7 +35,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/home/new/popup-stores", method: .get, queryParameters: request @@ -46,7 +46,7 @@ struct HomeAPIEndpoint { request: SortedRequestDTO ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/home/custom/popup-stores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift b/Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift index 691cf788..184c0040 100644 --- a/Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift +++ b/Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift @@ -13,7 +13,7 @@ struct FindDirectionEndPoint { popUpStoreId: Int64 ) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(popUpStoreId)/directions", method: .get ) diff --git a/Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift b/Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift index 3b5c7107..da8bc4fd 100644 --- a/Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift @@ -26,7 +26,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/locations/popup-stores", method: .get, queryParameters: params @@ -44,7 +44,7 @@ struct MapAPIEndpoint { ) return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/locations/search", method: .get, queryParameters: params diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift index 788b52d8..6fc60f13 100644 --- a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift @@ -13,7 +13,7 @@ struct PopUpAPIEndPoint { static func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/popup/closed", method: .get, queryParameters: request @@ -22,7 +22,7 @@ struct PopUpAPIEndPoint { static func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/popup/open", method: .get, queryParameters: request @@ -31,7 +31,7 @@ struct PopUpAPIEndPoint { static func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/search/popup-stores", method: .get, queryParameters: request @@ -40,7 +40,7 @@ struct PopUpAPIEndPoint { static func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/detail", method: .get, queryParameters: request @@ -49,7 +49,7 @@ struct PopUpAPIEndPoint { static func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/popup/\(request.popUpStoreId)/comments", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift index 147877c2..f827b0f0 100644 --- a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift +++ b/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift @@ -14,7 +14,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_checkNickName(with request: CheckNickNameRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/signup/check-nickname", method: .get, queryParameters: request @@ -25,7 +25,7 @@ struct SignUpAPIEndpoint { /// - Returns: Endpoint static func signUp_getCategoryList() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/signup/categories", method: .get ) @@ -36,7 +36,7 @@ struct SignUpAPIEndpoint { /// - Returns: RequestEndpoint static func signUp_trySignUp(with request: SignUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/signup", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift index 9ca2708d..032c756e 100644 --- a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift @@ -13,7 +13,7 @@ struct UserAPIEndPoint { static func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .post, queryParameters: request @@ -22,7 +22,7 @@ struct UserAPIEndPoint { static func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .delete, queryParameters: request @@ -31,7 +31,7 @@ struct UserAPIEndPoint { static func postCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/likes", method: .post, queryParameters: request @@ -40,7 +40,7 @@ struct UserAPIEndPoint { static func deleteCommentLike(request: CommentLikeRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/likes", method: .delete, queryParameters: request @@ -49,7 +49,7 @@ struct UserAPIEndPoint { static func postUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/block", method: .post, queryParameters: request @@ -58,7 +58,7 @@ struct UserAPIEndPoint { static func deleteUserBlock(request: PostUserBlockRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/unblock", method: .delete, queryParameters: request @@ -67,7 +67,7 @@ struct UserAPIEndPoint { static func getOtherUserCommentPopUpList(request: GetOtherUserCommentListRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/\(request.commenterId ?? "")/comments", method: .get, queryParameters: request @@ -76,7 +76,7 @@ struct UserAPIEndPoint { static func getMyPage() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/my-page", method: .get ) @@ -84,7 +84,7 @@ struct UserAPIEndPoint { static func postLogout() -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/logout", method: .post ) @@ -92,7 +92,7 @@ struct UserAPIEndPoint { static func getWithdrawlList() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/withdrawl/surveys", method: .get ) @@ -100,7 +100,7 @@ struct UserAPIEndPoint { static func postWithdrawl(request: PostWithdrawlListRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/delete", method: .post, bodyParameters: request @@ -109,7 +109,7 @@ struct UserAPIEndPoint { static func getMyProfile() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/profiles", method: .get ) @@ -117,7 +117,7 @@ struct UserAPIEndPoint { static func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/tailored-info", method: .put, bodyParameters: request @@ -126,7 +126,7 @@ struct UserAPIEndPoint { static func putUserCategory(request: PutUserCategoryRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/interests", method: .put, bodyParameters: request @@ -135,7 +135,7 @@ struct UserAPIEndPoint { static func putUserProfile(request: PutUserProfileRequestDTO) -> RequestEndpoint { return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/profiles", method: .put, bodyParameters: request @@ -144,7 +144,7 @@ struct UserAPIEndPoint { static func getMyCommentedPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/commented/popup", method: .get, queryParameters: request @@ -153,7 +153,7 @@ struct UserAPIEndPoint { static func getBlockUserList(request: GetBlockUserListRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/blocked", method: .get, queryParameters: request @@ -162,7 +162,7 @@ struct UserAPIEndPoint { static func getNoticeList() -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/notice/list", method: .get ) @@ -170,7 +170,7 @@ struct UserAPIEndPoint { static func getNoticeDetail(noticeID: Int64) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/notice/\(noticeID)", method: .get ) @@ -178,7 +178,7 @@ struct UserAPIEndPoint { static func getRecentPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/recent-popupstores", method: .get, queryParameters: request @@ -187,7 +187,7 @@ struct UserAPIEndPoint { static func getBookmarkPopUp(request: SortedRequestDTO) -> Endpoint { return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", method: .get, queryParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift index e6e12f83..556bf5d2 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift @@ -11,7 +11,7 @@ struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/files/upload-preSignedUrl", method: .post, bodyParameters: request @@ -21,7 +21,7 @@ struct PreSignedAPIEndPoint { static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug) return Endpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/files/download-preSignedUrl", method: .post, bodyParameters: request @@ -31,7 +31,7 @@ struct PreSignedAPIEndPoint { static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint { Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug) return RequestEndpoint( - baseURL: KeyPath.popPoolBaseURL, + baseURL: Secrets.popPoolBaseURL, path: "/files/delete", method: .post, bodyParameters: request diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift index 26efb286..0aaa3410 100644 --- a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift +++ b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift @@ -287,7 +287,7 @@ extension PreSignedService { } } func fullImageURL(from filePath: String) -> URL? { - let baseURL = KeyPath.popPoolS3BaseURL + let baseURL = Secrets.popPoolS3BaseURL // URL 인코딩 처리를 더 엄격하게 guard let encodedPath = filePath diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift index bd1738d3..a1a0b349 100644 --- a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift +++ b/Poppool/Poppool/Presentation/Extension/UIImageView+.swift @@ -6,7 +6,7 @@ extension UIImageView { self.image = UIImage(named: "image_default") return } - let imageURLString = KeyPath.popPoolS3BaseURL + path + let imageURLString = Secrets.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in DispatchQueue.main.async { @@ -22,7 +22,7 @@ extension UIImageView { completion() return } - let imageURLString = KeyPath.popPoolS3BaseURL + path + let imageURLString = Secrets.popPoolS3BaseURL + path if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { let imageURL = URL(string: cenvertimageURL) ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift index 5ab3d4c4..a29c4ee6 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift @@ -83,7 +83,7 @@ final class AdminStoreCell: UITableViewCell { statusChip.text = "운영" // mainImageUrl에서 baseURL 부분 제거 - let imagePath = store.mainImageUrl.replacingOccurrences(of: KeyPath.popPoolS3BaseURL, with: "") + let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 964cdb5f..8b4e249b 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -372,7 +372,7 @@ extension DetailReactor { func showSharedBoard(controller: BaseViewController) { let storeName = titleSection.inputDataList.first?.title ?? "" - let imagePath = KeyPath.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") + let imagePath = Secrets.popPoolS3BaseURL + (imageBannerSection.inputDataList.first?.imagePaths.first ?? "") // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), diff --git a/Poppool/Poppool/Resource/KeyPath.swift b/Poppool/Poppool/Resource/Secrets.swift similarity index 98% rename from Poppool/Poppool/Resource/KeyPath.swift rename to Poppool/Poppool/Resource/Secrets.swift index 4bc23b90..7338561a 100644 --- a/Poppool/Poppool/Resource/KeyPath.swift +++ b/Poppool/Poppool/Resource/Secrets.swift @@ -1,6 +1,6 @@ import Foundation -enum KeyPath { +enum Secrets { static var kakaoAuthAppKey: String { return getValue(forKey: "KAKAO_AUTH_APP_KEY") } From 51dcfeeff743d5e91804aaddbf12697eca095352 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 21:37:38 +0900 Subject: [PATCH 105/393] =?UTF-8?q?refactor/#112:=20Extension=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{Presentation => Infrastructure}/Extension/Date?+.swift | 0 .../{Presentation => Infrastructure}/Extension/Reactive+.swift | 0 .../{Presentation => Infrastructure}/Extension/String?+.swift | 0 .../Extension/UIApplication+.swift | 0 .../Extension/UICollectionReusableView+.swift | 0 .../Extension/UICollectionViewCell+.swift | 0 .../{Presentation => Infrastructure}/Extension/UIColor+.swift | 0 .../{Presentation => Infrastructure}/Extension/UIFont+.swift | 0 .../{Presentation => Infrastructure}/Extension/UIImage+.swift | 0 .../{Presentation => Infrastructure}/Extension/UIImageView+.swift | 0 .../{Presentation => Infrastructure}/Extension/UILabel+.swift | 0 .../Extension/UINavigationController+.swift | 0 .../Extension/UITableViewCell+.swift | 0 .../{Presentation => Infrastructure}/Extension/UITextField+.swift | 0 .../{Presentation => Infrastructure}/Extension/UIView+.swift | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/Date?+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/Reactive+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/String?+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIApplication+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UICollectionReusableView+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UICollectionViewCell+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIColor+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIFont+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIImage+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIImageView+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UILabel+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UINavigationController+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UITableViewCell+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UITextField+.swift (100%) rename Poppool/Poppool/{Presentation => Infrastructure}/Extension/UIView+.swift (100%) diff --git a/Poppool/Poppool/Presentation/Extension/Date?+.swift b/Poppool/Poppool/Infrastructure/Extension/Date?+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/Date?+.swift rename to Poppool/Poppool/Infrastructure/Extension/Date?+.swift diff --git a/Poppool/Poppool/Presentation/Extension/Reactive+.swift b/Poppool/Poppool/Infrastructure/Extension/Reactive+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/Reactive+.swift rename to Poppool/Poppool/Infrastructure/Extension/Reactive+.swift diff --git a/Poppool/Poppool/Presentation/Extension/String?+.swift b/Poppool/Poppool/Infrastructure/Extension/String?+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/String?+.swift rename to Poppool/Poppool/Infrastructure/Extension/String?+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIApplication+.swift b/Poppool/Poppool/Infrastructure/Extension/UIApplication+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIApplication+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIApplication+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UICollectionReusableView+.swift b/Poppool/Poppool/Infrastructure/Extension/UICollectionReusableView+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UICollectionReusableView+.swift rename to Poppool/Poppool/Infrastructure/Extension/UICollectionReusableView+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UICollectionViewCell+.swift b/Poppool/Poppool/Infrastructure/Extension/UICollectionViewCell+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UICollectionViewCell+.swift rename to Poppool/Poppool/Infrastructure/Extension/UICollectionViewCell+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIColor+.swift b/Poppool/Poppool/Infrastructure/Extension/UIColor+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIColor+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIColor+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIFont+.swift b/Poppool/Poppool/Infrastructure/Extension/UIFont+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIFont+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIFont+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIImage+.swift b/Poppool/Poppool/Infrastructure/Extension/UIImage+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIImage+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIImage+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIImageView+.swift b/Poppool/Poppool/Infrastructure/Extension/UIImageView+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIImageView+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIImageView+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UILabel+.swift b/Poppool/Poppool/Infrastructure/Extension/UILabel+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UILabel+.swift rename to Poppool/Poppool/Infrastructure/Extension/UILabel+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UINavigationController+.swift b/Poppool/Poppool/Infrastructure/Extension/UINavigationController+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UINavigationController+.swift rename to Poppool/Poppool/Infrastructure/Extension/UINavigationController+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UITableViewCell+.swift b/Poppool/Poppool/Infrastructure/Extension/UITableViewCell+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UITableViewCell+.swift rename to Poppool/Poppool/Infrastructure/Extension/UITableViewCell+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UITextField+.swift b/Poppool/Poppool/Infrastructure/Extension/UITextField+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UITextField+.swift rename to Poppool/Poppool/Infrastructure/Extension/UITextField+.swift diff --git a/Poppool/Poppool/Presentation/Extension/UIView+.swift b/Poppool/Poppool/Infrastructure/Extension/UIView+.swift similarity index 100% rename from Poppool/Poppool/Presentation/Extension/UIView+.swift rename to Poppool/Poppool/Infrastructure/Extension/UIView+.swift From 226e19c73017e46952f4fb4006ef67741230f714 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 21:41:07 +0900 Subject: [PATCH 106/393] =?UTF-8?q?refactor/#112:=20=EC=98=88=EC=83=81=20?= =?UTF-8?q?=EB=AA=A8=EB=93=88=ED=99=94=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=ED=8F=B4=EB=8D=94=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/{Repository => RepositoryImpl}/AdminRepository.swift | 0 .../{Repository => RepositoryImpl}/AuthAPIRepositoryImpl.swift | 0 .../Repository => Data/RepositoryImpl}/AuthRepository.swift | 0 .../{Repository => RepositoryImpl}/CommentAPIRepository.swift | 0 .../Data/{Repository => RepositoryImpl}/HomeAPIRepository.swift | 0 .../RepositoryImpl}/MapDirectionRepository.swift | 0 .../Data/{Repository => RepositoryImpl}/MapRepository.swift | 0 .../{Repository => RepositoryImpl}/PopUpAPIRepositoryImpl.swift | 0 .../{Repository => RepositoryImpl}/SignUpRepositoryImpl.swift | 0 .../{Repository => RepositoryImpl}/UserAPIRepositoryImpl.swift | 0 .../Poppool/Domain/{UseCase => UseCaseImpl}/AdminUseCase.swift | 0 .../Domain/{UseCase => UseCaseImpl}/AuthAPIUseCaseImpl.swift | 0 .../Domain/{UseCase => UseCaseImpl}/CommentAPIUseCaseImpl.swift | 0 .../Domain/{UseCase => UseCaseImpl}/HomeAPIUseCaseImpl.swift | 0 .../Domain/{UseCase => UseCaseImpl}/MapDirectionUseCase.swift | 0 Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/MapUseCase.swift | 0 .../Domain/{UseCase => UseCaseImpl}/PopUpAPIUseCaseImpl.swift | 0 .../Domain/{UseCase => UseCaseImpl}/SignUpAPIUseCaseImpl.swift | 0 .../Domain/{UseCase => UseCaseImpl}/UserAPIUseCaseImpl.swift | 0 .../Entity}/AuthAPI/LoginResponse.swift | 0 .../Entity}/AuthAPI/PostTokenReissueResponse.swift | 0 .../Entities => DomainInterface/Entity}/BannerPopUpStore.swift | 0 .../{Domain/Entities => DomainInterface/Entity}/Category.swift | 0 .../Entities => DomainInterface/Entity}/GetHomeInfoResponse.swift | 0 .../Entities => DomainInterface/Entity}/MapPopUpStore.swift | 0 .../Entity}/PopUpAPI/GetPopUpCommentResponse.swift | 0 .../Entity}/PopUpAPI/GetPopUpDetailResponse.swift | 0 .../Entity}/PopUpAPI/GetSearchBottomPopUpListResponse.swift | 0 .../Entity}/PopUpAPI/GetSearchPopUpListResponse.swift | 0 .../Entities => DomainInterface/Entity}/PopUpStoreResponse.swift | 0 .../Entity}/UserAPI/GetBlockUserListResponse.swift | 0 .../Entity}/UserAPI/GetMyCommentResponse.swift | 0 .../Entity}/UserAPI/GetMyPageResponse.swift | 0 .../Entity}/UserAPI/GetMyProfileResponse.swift | 0 .../Entity}/UserAPI/GetNoticeDetailResponse.swift | 0 .../Entity}/UserAPI/GetNoticeListResponse.swift | 0 .../Entity}/UserAPI/GetOtherUserCommentedPopUpListResponse.swift | 0 .../Entity}/UserAPI/GetRecentPopUpResponse.swift | 0 .../Entity}/UserAPI/GetWithdrawlListResponse.swift | 0 39 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/AdminRepository.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/AuthAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{Domain/Repository => Data/RepositoryImpl}/AuthRepository.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/CommentAPIRepository.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/HomeAPIRepository.swift (100%) rename Poppool/Poppool/{Domain/Repository => Data/RepositoryImpl}/MapDirectionRepository.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/MapRepository.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/PopUpAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/SignUpRepositoryImpl.swift (100%) rename Poppool/Poppool/Data/{Repository => RepositoryImpl}/UserAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/AdminUseCase.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/AuthAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/CommentAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/HomeAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/MapDirectionUseCase.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/MapUseCase.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/PopUpAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/SignUpAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/Domain/{UseCase => UseCaseImpl}/UserAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/AuthAPI/LoginResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/AuthAPI/PostTokenReissueResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/BannerPopUpStore.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/Category.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/GetHomeInfoResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/MapPopUpStore.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/PopUpAPI/GetPopUpCommentResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/PopUpAPI/GetPopUpDetailResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/PopUpAPI/GetSearchBottomPopUpListResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/PopUpAPI/GetSearchPopUpListResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/PopUpStoreResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetBlockUserListResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetMyCommentResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetMyPageResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetMyProfileResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetNoticeDetailResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetNoticeListResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetOtherUserCommentedPopUpListResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetRecentPopUpResponse.swift (100%) rename Poppool/Poppool/{Domain/Entities => DomainInterface/Entity}/UserAPI/GetWithdrawlListResponse.swift (100%) diff --git a/Poppool/Poppool/Data/Repository/AdminRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/AdminRepository.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/AdminRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/AdminRepository.swift diff --git a/Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/AuthAPIRepositoryImpl.swift rename to Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Domain/Repository/AuthRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift similarity index 100% rename from Poppool/Poppool/Domain/Repository/AuthRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift diff --git a/Poppool/Poppool/Data/Repository/CommentAPIRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepository.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/CommentAPIRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepository.swift diff --git a/Poppool/Poppool/Data/Repository/HomeAPIRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepository.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/HomeAPIRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepository.swift diff --git a/Poppool/Poppool/Domain/Repository/MapDirectionRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepository.swift similarity index 100% rename from Poppool/Poppool/Domain/Repository/MapDirectionRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepository.swift diff --git a/Poppool/Poppool/Data/Repository/MapRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/MapRepository.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/MapRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/MapRepository.swift diff --git a/Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/PopUpAPIRepositoryImpl.swift rename to Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/SignUpRepositoryImpl.swift rename to Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/Repository/UserAPIRepositoryImpl.swift rename to Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/AdminUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCase.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/AdminUseCase.swift rename to Poppool/Poppool/Domain/UseCaseImpl/AdminUseCase.swift diff --git a/Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/AuthAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/CommentAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/HomeAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/MapDirectionUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/MapDirectionUseCase.swift rename to Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift diff --git a/Poppool/Poppool/Domain/UseCase/MapUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/MapUseCase.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/MapUseCase.swift rename to Poppool/Poppool/Domain/UseCaseImpl/MapUseCase.swift diff --git a/Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/PopUpAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/SignUpAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCase/UserAPIUseCaseImpl.swift rename to Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/Entities/AuthAPI/LoginResponse.swift b/Poppool/Poppool/DomainInterface/Entity/AuthAPI/LoginResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/AuthAPI/LoginResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/AuthAPI/LoginResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/AuthAPI/PostTokenReissueResponse.swift b/Poppool/Poppool/DomainInterface/Entity/AuthAPI/PostTokenReissueResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/AuthAPI/PostTokenReissueResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/AuthAPI/PostTokenReissueResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/BannerPopUpStore.swift b/Poppool/Poppool/DomainInterface/Entity/BannerPopUpStore.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/BannerPopUpStore.swift rename to Poppool/Poppool/DomainInterface/Entity/BannerPopUpStore.swift diff --git a/Poppool/Poppool/Domain/Entities/Category.swift b/Poppool/Poppool/DomainInterface/Entity/Category.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/Category.swift rename to Poppool/Poppool/DomainInterface/Entity/Category.swift diff --git a/Poppool/Poppool/Domain/Entities/GetHomeInfoResponse.swift b/Poppool/Poppool/DomainInterface/Entity/GetHomeInfoResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/GetHomeInfoResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/GetHomeInfoResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/MapPopUpStore.swift b/Poppool/Poppool/DomainInterface/Entity/MapPopUpStore.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/MapPopUpStore.swift rename to Poppool/Poppool/DomainInterface/Entity/MapPopUpStore.swift diff --git a/Poppool/Poppool/Domain/Entities/PopUpAPI/GetPopUpCommentResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpCommentResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/PopUpAPI/GetPopUpCommentResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpCommentResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/PopUpAPI/GetPopUpDetailResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpDetailResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/PopUpAPI/GetPopUpDetailResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpDetailResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/PopUpAPI/GetSearchBottomPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchBottomPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/PopUpAPI/GetSearchBottomPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchBottomPopUpListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/PopUpAPI/GetSearchPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/PopUpAPI/GetSearchPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchPopUpListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/PopUpStoreResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpStoreResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/PopUpStoreResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpStoreResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetBlockUserListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetBlockUserListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetBlockUserListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetBlockUserListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetMyCommentResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyCommentResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetMyCommentResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyCommentResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetMyPageResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyPageResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetMyPageResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyPageResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetMyProfileResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyProfileResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetMyProfileResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyProfileResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeDetailResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeDetailResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeDetailResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeDetailResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetNoticeListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetOtherUserCommentedPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetOtherUserCommentedPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetOtherUserCommentedPopUpListResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetRecentPopUpResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetRecentPopUpResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetRecentPopUpResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetRecentPopUpResponse.swift diff --git a/Poppool/Poppool/Domain/Entities/UserAPI/GetWithdrawlListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetWithdrawlListResponse.swift similarity index 100% rename from Poppool/Poppool/Domain/Entities/UserAPI/GetWithdrawlListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserAPI/GetWithdrawlListResponse.swift From 1db8dab61a1dfc7e0bbba899fc5133cb6d79fe09 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 21:46:15 +0900 Subject: [PATCH 107/393] =?UTF-8?q?refactor/#112:=20AdminRepository=20?= =?UTF-8?q?=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ository.swift => AdminRepositoryImpl.swift} | 18 +++--------------- .../Repository/AdminRepository.swift | 15 +++++++++++++++ .../Scene/Admin/AdminViewController.swift | 2 +- .../Scene/MyPage/Main/MyPageReactor.swift | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) rename Poppool/Poppool/Data/RepositoryImpl/{AdminRepository.swift => AdminRepositoryImpl.swift} (88%) create mode 100644 Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/AdminRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/AdminRepositoryImpl.swift similarity index 88% rename from Poppool/Poppool/Data/RepositoryImpl/AdminRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/AdminRepositoryImpl.swift index 7d0bd1e6..71de6881 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/AdminRepository.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/AdminRepositoryImpl.swift @@ -1,21 +1,9 @@ -import Alamofire import Foundation + +import Alamofire import RxSwift -import UIKit - -protocol AdminRepository { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable - func fetchStoreDetail(id: Int64) -> Observable - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable - func deleteStore(id: Int64) -> Observable - - func createNotice(request: CreateNoticeRequestDTO) -> Observable - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable - func deleteNotice(id: Int64) -> Observable -} -final class DefaultAdminRepository: AdminRepository { +final class AdminRepositoryImpl: AdminRepository { // MARK: - Properties private let provider: Provider diff --git a/Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift b/Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift new file mode 100644 index 00000000..da4112ec --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift @@ -0,0 +1,15 @@ +import Foundation + +import RxSwift + +protocol AdminRepository { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable + func fetchStoreDetail(id: Int64) -> Observable + func createStore(request: CreatePopUpStoreRequestDTO) -> Observable + func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable + func deleteStore(id: Int64) -> Observable + + func createNotice(request: CreateNoticeRequestDTO) -> Observable + func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable + func deleteNotice(id: Int64) -> Observable +} diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift index 5e5474f4..d343a466 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift @@ -16,7 +16,7 @@ final class AdminViewController: BaseViewController, View { private let adminUseCase: AdminUseCase // MARK: - Init - init(nickname: String, adminUseCase: AdminUseCase = DefaultAdminUseCase(repository: DefaultAdminRepository(provider: ProviderImpl()))) { + init(nickname: String, adminUseCase: AdminUseCase = DefaultAdminUseCase(repository: AdminRepositoryImpl(provider: ProviderImpl()))) { self.nickname = nickname self.adminUseCase = adminUseCase self.mainView = AdminView(frame: .zero) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 7ac24f0b..503861b4 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -275,7 +275,7 @@ final class MyPageReactor: Reactor { let adminVC = AdminViewController(nickname: nickname) adminVC.reactor = AdminReactor( useCase: DefaultAdminUseCase( - repository: DefaultAdminRepository(provider: ProviderImpl()) + repository: AdminRepositoryImpl(provider: ProviderImpl()) ) ) controller.navigationController?.pushViewController(adminVC, animated: true) From 6ed7c3d8b8feb2ebc87b604a4341c7b055928cef Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 21:57:14 +0900 Subject: [PATCH 108/393] =?UTF-8?q?refactor/#112:=20Repository=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AuthAPIRepositoryImpl.swift | 10 ++----- .../Data/RepositoryImpl/AuthRepository.swift | 22 --------------- ...y.swift => CommentAPIRepositoryImpl.swift} | 9 +----- ...tory.swift => HomeAPIRepositoryImpl.swift} | 10 ++----- ...swift => MapDirectionRepositoryImpl.swift} | 14 ++-------- ...pository.swift => MapRepositoryImpl.swift} | 27 ++---------------- .../PopUpAPIRepositoryImpl.swift | 9 +----- .../RepositoryImpl/SignUpRepositoryImpl.swift | 10 ++----- .../UserAPIRepositoryImpl.swift | 9 +----- .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 4 +-- .../UseCaseImpl/HomeAPIUseCaseImpl.swift | 2 +- .../Repository/AuthAPIRepository.swift | 8 ++++++ .../Repository/CommentAPIRepository.swift | 9 ++++++ .../Repository/HomeAPIRepository.swift | 10 +++++++ .../Repository/MapDirectionRepository.swift | 7 +++++ .../Repository/MapRepository.swift | 28 +++++++++++++++++++ .../Repository/PopUpAPIRepository.swift | 12 ++++++++ .../Repository/SignUpRepository.swift | 17 +++++++++++ .../Repository/UserAPIRepository.swift | 27 ++++++++++++++++++ .../CommentList/CommentListReactor.swift | 2 +- .../NormalCommentAddReactor.swift | 2 +- .../NormalCommentEditReactor.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 2 +- .../MapGuideView/MapGuideReactor.swift | 2 +- .../MapGuideView/MapGuideViewController.swift | 4 +-- .../TabbarController/TabbarController.swift | 4 +-- 26 files changed, 143 insertions(+), 119 deletions(-) delete mode 100644 Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift rename Poppool/Poppool/Data/RepositoryImpl/{CommentAPIRepository.swift => CommentAPIRepositoryImpl.swift} (87%) rename Poppool/Poppool/Data/RepositoryImpl/{HomeAPIRepository.swift => HomeAPIRepositoryImpl.swift} (91%) rename Poppool/Poppool/Data/RepositoryImpl/{MapDirectionRepository.swift => MapDirectionRepositoryImpl.swift} (72%) rename Poppool/Poppool/Data/RepositoryImpl/{MapRepository.swift => MapRepositoryImpl.swift} (81%) create mode 100644 Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/MapRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift create mode 100644 Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift index 7e01f977..dbb6c689 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -1,14 +1,8 @@ -// -// AuthRepository.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation + import RxSwift -final class AuthAPIRepositoryImpl { +final class AuthAPIRepositoryImpl: AuthAPIRepository { var provider: Provider diff --git a/Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift deleted file mode 100644 index 0a8c43be..00000000 --- a/Poppool/Poppool/Data/RepositoryImpl/AuthRepository.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// AuthRepository.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - -import Foundation -import RxSwift - -// protocol AuthRepository { -// -// /// 네트워크 요청을 처리하는 프로바이더 -// var provider: Provider { get set } -// -// /// 로그인 시도 메서드 -// /// - Parameters: -// /// - userCredential: 사용자 자격 증명 정보 (Encodable) -// /// - socialType: 소셜 로그인 타입 (예: "google", "facebook") -// /// - Returns: 로그인 응답을 나타내는 Observable 객체 -// func tryLogIn(userCredential: Encodable, socialType: String) -> Observable -// } diff --git a/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift similarity index 87% rename from Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift index f5264dbf..22ae8766 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepository.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift @@ -1,15 +1,8 @@ -// -// CommentAPIRepository.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import Foundation import RxSwift -final class CommentAPIRepository { +final class CommentAPIRepositoryImpl: CommentAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift similarity index 91% rename from Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift index 2bd55980..89ef1e10 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepository.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -1,14 +1,8 @@ -// -// HomeAPIRepository.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation + import RxSwift -final class HomeAPIRepository { +final class HomeAPIRepositoryImpl: HomeAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift similarity index 72% rename from Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index fcb8e508..b4c3a861 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepository.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -1,18 +1,8 @@ -// -// MapDirectionRepository.swift -// Poppool -// -// Created by 김기현 on 1/23/25. -// - import Foundation -import RxSwift -protocol MapDirectionRepository { - func getPopUpDirection(popUpStoreId: Int64) -> Observable -} +import RxSwift -final class DefaultMapDirectionRepository: MapDirectionRepository { +final class MapDirectionRepositoryImpl: MapDirectionRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapRepository.swift b/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift similarity index 81% rename from Poppool/Poppool/Data/RepositoryImpl/MapRepository.swift rename to Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift index a10bffc5..c9487f87 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/MapRepository.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -1,32 +1,9 @@ -// -// MapRepository.swift -// Poppool -// -// Created by 김기현 on 12/3/24. -// - import Foundation -import RxSwift -protocol MapRepository { - func fetchStoresInBounds( - northEastLat: Double, - northEastLon: Double, - southWestLat: Double, - southWestLon: Double, - categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> - - func searchStores( - query: String, - categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> - - func fetchCategories() -> Observable<[Category]> -} +import RxSwift // MARK: - Implementation -class DefaultMapRepository: MapRepository { +class MapRepositoryImpl: MapRepository { private let provider: Provider init(provider: Provider) { diff --git a/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift index 3c786005..735bd339 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -1,15 +1,8 @@ -// -// PopUpAPIRepositoryImpl.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation import RxSwift -struct PopUpAPIRepositoryImpl { +final class PopUpAPIRepositoryImpl: PopUpAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift index 6402b3dc..4cdef81e 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -1,14 +1,8 @@ -// -// SignUpRepositoryImpl.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation + import RxSwift -final class SignUpRepositoryImpl { +final class SignUpRepositoryImpl: SignUpRepository { var provider: Provider diff --git a/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift index 55a66ddb..a146c460 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -1,15 +1,8 @@ -// -// UserAPIRepositoryImpl.swift -// Poppool -// -// Created by SeoJunYoung on 12/3/24. -// - import Foundation import RxSwift -final class UserAPIRepositoryImpl { +final class UserAPIRepositoryImpl: UserAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index f6d83409..a0ab23d7 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -11,9 +11,9 @@ import RxSwift final class CommentAPIUseCaseImpl { - var repository: CommentAPIRepository + var repository: CommentAPIRepositoryImpl - init(repository: CommentAPIRepository) { + init(repository: CommentAPIRepositoryImpl) { self.repository = repository } diff --git a/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index 1664dd60..fe41ddc4 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -9,7 +9,7 @@ import Foundation import RxSwift final class HomeAPIUseCaseImpl { - var repository = HomeAPIRepository(provider: ProviderImpl()) + var repository = HomeAPIRepositoryImpl(provider: ProviderImpl()) func fetchHome( page: Int32?, diff --git a/Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift b/Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift new file mode 100644 index 00000000..630c801b --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift @@ -0,0 +1,8 @@ +import Foundation + +import RxSwift + +protocol AuthAPIRepository { + func tryLogIn(userCredential: Encodable, socialType: String) -> Observable + func postTokenReissue() -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift new file mode 100644 index 00000000..bd079fc8 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift @@ -0,0 +1,9 @@ +import Foundation + +import RxSwift + +protocol CommentAPIRepository { + func postCommentAdd(request: PostCommentRequestDTO) -> Completable + func deleteComment(request: DeleteCommentRequestDTO) -> Completable + func editComment(request: PutCommentRequestDTO) -> Completable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift b/Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift new file mode 100644 index 00000000..2c6082ed --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift @@ -0,0 +1,10 @@ +import Foundation + +import RxSwift + +protocol HomeAPIRepository { + func fetchHome(request: SortedRequestDTO) -> Observable + func fetchCustomPopUp(request: SortedRequestDTO) -> Observable + func fetchNewPopUp(request: SortedRequestDTO) -> Observable + func fetchPopularPopUp(request: SortedRequestDTO) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift b/Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift new file mode 100644 index 00000000..d73ff0a0 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +protocol MapDirectionRepository { + func getPopUpDirection(popUpStoreId: Int64) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/MapRepository.swift b/Poppool/Poppool/DomainInterface/Repository/MapRepository.swift new file mode 100644 index 00000000..b50ef36b --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/MapRepository.swift @@ -0,0 +1,28 @@ +// +// MapRepository.swift +// Poppool +// +// Created by 송영훈 on 4/14/25. +// + + +import Foundation + +import RxSwift + +protocol MapRepository { + func fetchStoresInBounds( + northEastLat: Double, + northEastLon: Double, + southWestLat: Double, + southWestLon: Double, + categories: [Int64] + ) -> Observable<[MapPopUpStoreDTO]> + + func searchStores( + query: String, + categories: [Int64] + ) -> Observable<[MapPopUpStoreDTO]> + + func fetchCategories() -> Observable<[Category]> +} \ No newline at end of file diff --git a/Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift b/Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift new file mode 100644 index 00000000..4cec5864 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift @@ -0,0 +1,12 @@ +import Foundation + +import RxSwift + +protocol PopUpAPIRepository { + func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable + func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Observable + func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable + func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable + func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Observable + func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift b/Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift new file mode 100644 index 00000000..d403fe94 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift @@ -0,0 +1,17 @@ +import Foundation + +import RxSwift + +protocol SignUpRepository { + func checkNickName(nickName: String) -> Observable + func fetchCategoryList() -> Observable<[Category]> + func trySignUp( + nickName: String, + gender: String, + age: Int32, + socialEmail: String, + socialType: String, + interests: [Int64], + appleAuthorizationCode: String? + ) -> Completable +} diff --git a/Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift new file mode 100644 index 00000000..5d37ef5f --- /dev/null +++ b/Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift @@ -0,0 +1,27 @@ +import Foundation + +import RxSwift + +protocol UserAPIRepository { + func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable + func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable + func postCommentLike(request: CommentLikeRequestDTO) -> Completable + func deleteCommentLike(request: CommentLikeRequestDTO) -> Completable + func postUserBlock(request: PostUserBlockRequestDTO) -> Completable + func deleteUserBlock(request: PostUserBlockRequestDTO) -> Completable + func getOtherUserCommentList(request: GetOtherUserCommentListRequestDTO) -> Observable + func getMyPage() -> Observable + func postLogout() -> Completable + func getWithdrawlList() -> Observable + func postWithdrawl(request: PostWithdrawlListRequestDTO) -> Completable + func getMyProfile() -> Observable + func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> Completable + func putUserCategory(request: PutUserCategoryRequestDTO) -> Completable + func putUserProfile(request: PutUserProfileRequestDTO) -> Completable + func getMyCommentedPopUp(request: SortedRequestDTO) -> Observable + func getBlockUserList(request: GetBlockUserListRequestDTO) -> Observable + func getNoticeList() -> Observable + func getNoticeDetail(noticeID: Int64) -> Observable + func getRecentPopUp(request: SortedRequestDTO) -> Observable + func getBookmarkPopUp(request: SortedRequestDTO) -> Observable +} diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index 3823d183..af0fc685 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -51,7 +51,7 @@ final class CommentListReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index 63b51884..8e7d528b 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -47,7 +47,7 @@ final class NormalCommentAddReactor: Reactor { private var popUpID: Int64 private var popUpName: String - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) private let imageService = PreSignedService() lazy var compositionalLayout: UICollectionViewCompositionalLayout = { diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index dee4c3cc..73d67b40 100644 --- a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -48,7 +48,7 @@ final class NormalCommentEditReactor: Reactor { private var popUpName: String private var originComment: DetailCommentSection.CellType.Input - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) private let imageService = PreSignedService() lazy var compositionalLayout: UICollectionViewCompositionalLayout = { diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift index 8b4e249b..ced7d370 100644 --- a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift @@ -60,7 +60,7 @@ final class DetailReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepository(provider: ProviderImpl())) + private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index dd3fef65..551a396c 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -47,7 +47,7 @@ final class MapGuideReactor: Reactor { // MARK: - Init init( popUpStoreId: Int64, - repository: MapDirectionRepository = DefaultMapDirectionRepository(provider: ProviderImpl()) + repository: MapDirectionRepository = MapDirectionRepositoryImpl(provider: ProviderImpl()) ) { self.popUpStoreId = popUpStoreId self.directionRepository = repository diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 60d2d07c..7282142a 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -172,9 +172,9 @@ final class MapGuideViewController: UIViewController, View { guard let strongSelf = self else { return } let providerInstance = ProviderImpl() - let repositoryInstance = DefaultMapRepository(provider: providerInstance) + let repositoryInstance = MapRepositoryImpl(provider: providerInstance) let useCaseInstance = DefaultMapUseCase(repository: repositoryInstance) - let directionRepositoryInstance = DefaultMapDirectionRepository(provider: providerInstance) + let directionRepositoryInstance = MapDirectionRepositoryImpl(provider: providerInstance) let mapReactorInstance = MapReactor(useCase: useCaseInstance, directionRepository: directionRepositoryInstance) if let selectedStore = strongSelf.currentCarouselStoreList.first { diff --git a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift index efac523a..77e2659e 100644 --- a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift @@ -196,8 +196,8 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let provider = ProviderImpl() let mapController = MapViewController() - let mapUseCase = DefaultMapUseCase(repository: DefaultMapRepository(provider: provider)) - let directionRepository = DefaultMapDirectionRepository(provider: provider) + let mapUseCase = DefaultMapUseCase(repository: MapRepositoryImpl(provider: provider)) + let directionRepository = MapDirectionRepositoryImpl(provider: provider) mapController.reactor = MapReactor(useCase: mapUseCase, directionRepository: directionRepository) let homeController = HomeController() From 0cc4bf9534d62d7b51a91263b6c87d408c85260d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 21:58:48 +0900 Subject: [PATCH 109/393] =?UTF-8?q?refactor/#112:=20Entity=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=AA=85=20=EC=9E=84=EC=8B=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/{AuthAPI => AuthResponse}/LoginResponse.swift | 0 .../{AuthAPI => AuthResponse}/PostTokenReissueResponse.swift | 0 .../{PopUpAPI => PopUpResponse}/GetPopUpCommentResponse.swift | 0 .../{PopUpAPI => PopUpResponse}/GetPopUpDetailResponse.swift | 0 .../GetSearchBottomPopUpListResponse.swift | 0 .../{PopUpAPI => PopUpResponse}/GetSearchPopUpListResponse.swift | 0 .../{UserAPI => UserResponse}/GetBlockUserListResponse.swift | 0 .../Entity/{UserAPI => UserResponse}/GetMyCommentResponse.swift | 0 .../Entity/{UserAPI => UserResponse}/GetMyPageResponse.swift | 0 .../Entity/{UserAPI => UserResponse}/GetMyProfileResponse.swift | 0 .../{UserAPI => UserResponse}/GetNoticeDetailResponse.swift | 0 .../Entity/{UserAPI => UserResponse}/GetNoticeListResponse.swift | 0 .../GetOtherUserCommentedPopUpListResponse.swift | 0 .../Entity/{UserAPI => UserResponse}/GetRecentPopUpResponse.swift | 0 .../{UserAPI => UserResponse}/GetWithdrawlListResponse.swift | 0 15 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/DomainInterface/Entity/{AuthAPI => AuthResponse}/LoginResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{AuthAPI => AuthResponse}/PostTokenReissueResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{PopUpAPI => PopUpResponse}/GetPopUpCommentResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{PopUpAPI => PopUpResponse}/GetPopUpDetailResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{PopUpAPI => PopUpResponse}/GetSearchBottomPopUpListResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{PopUpAPI => PopUpResponse}/GetSearchPopUpListResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetBlockUserListResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetMyCommentResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetMyPageResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetMyProfileResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetNoticeDetailResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetNoticeListResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetOtherUserCommentedPopUpListResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetRecentPopUpResponse.swift (100%) rename Poppool/Poppool/DomainInterface/Entity/{UserAPI => UserResponse}/GetWithdrawlListResponse.swift (100%) diff --git a/Poppool/Poppool/DomainInterface/Entity/AuthAPI/LoginResponse.swift b/Poppool/Poppool/DomainInterface/Entity/AuthResponse/LoginResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/AuthAPI/LoginResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/AuthResponse/LoginResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/AuthAPI/PostTokenReissueResponse.swift b/Poppool/Poppool/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/AuthAPI/PostTokenReissueResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpCommentResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpCommentResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpDetailResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetPopUpDetailResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchBottomPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchBottomPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpAPI/GetSearchPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetBlockUserListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetBlockUserListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyCommentResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyCommentResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyPageResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyPageResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyProfileResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetMyProfileResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeDetailResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeDetailResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetNoticeListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetOtherUserCommentedPopUpListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetRecentPopUpResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetRecentPopUpResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserAPI/GetWithdrawlListResponse.swift b/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserAPI/GetWithdrawlListResponse.swift rename to Poppool/Poppool/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift From cc48b56bc3b2fa9848915e28a5f334f7038c418a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 22:20:05 +0900 Subject: [PATCH 110/393] =?UTF-8?q?refactor/#112:=20UseCase=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...inUseCase.swift => AdminUseCaseImpl.swift} | 16 ++-------- .../UseCaseImpl/AuthAPIUseCaseImpl.swift | 14 +++------ .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 15 +++------- .../UseCaseImpl/HomeAPIUseCaseImpl.swift | 17 +++++------ .../UseCaseImpl/MapDirectionUseCase.swift | 26 ----------------- ...{MapUseCase.swift => MapUseCaseImpl.swift} | 21 ++------------ .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 13 ++------- .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 16 ++++------ .../UseCaseImpl/UserAPIUseCaseImpl.swift | 14 +++------ .../UseCase/AdminUseCase.swift | 16 ++++++++++ .../UseCase/AuthAPIUseCase.swift | 8 +++++ .../UseCase/CommentAPIUseCase.swift | 9 ++++++ .../UseCase/HomeAPIUseCase.swift | 10 +++++++ .../DomainInterface/UseCase/MapUseCase.swift | 29 +++++++++++++++++++ .../UseCase/PopUpAPIUseCase.swift | 10 +++++++ .../UseCase/SignUpAPIUseCase.swift | 19 ++++++++++++ .../UseCase/UserAPIUseCase.swift | 27 +++++++++++++++++ .../Scene/Admin/AdminViewController.swift | 2 +- .../Scene/Home/List/HomeListReactor.swift | 2 +- .../Scene/Home/Main/HomeReactor.swift | 2 +- .../MapGuideView/MapGuideViewController.swift | 2 +- .../Scene/MyPage/Main/MyPageReactor.swift | 2 +- .../TabbarController/TabbarController.swift | 2 +- 23 files changed, 167 insertions(+), 125 deletions(-) rename Poppool/Poppool/Domain/UseCaseImpl/{AdminUseCase.swift => AdminUseCaseImpl.swift} (74%) delete mode 100644 Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift rename Poppool/Poppool/Domain/UseCaseImpl/{MapUseCase.swift => MapUseCaseImpl.swift} (77%) create mode 100644 Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift create mode 100644 Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCaseImpl.swift similarity index 74% rename from Poppool/Poppool/Domain/UseCaseImpl/AdminUseCase.swift rename to Poppool/Poppool/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 870657d3..77097ed2 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCase.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -1,20 +1,8 @@ import Foundation -import RxSwift - -protocol AdminUseCase { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable - func fetchStoreDetail(id: Int64) -> Observable - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable - func deleteStore(id: Int64) -> Observable - // Notice - func createNotice(request: CreateNoticeRequestDTO) -> Observable - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable - func deleteNotice(id: Int64) -> Observable -} +import RxSwift -final class DefaultAdminUseCase: AdminUseCase { +final class AdminUseCaseImpl: AdminUseCase { private let repository: AdminRepository diff --git a/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift index e15877e6..841a4034 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift @@ -1,18 +1,12 @@ -// -// AuthAPIUseCaseImpl.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation + import RxSwift -final class AuthAPIUseCaseImpl { +final class AuthAPIUseCaseImpl: AuthAPIUseCase { - var repository: AuthAPIRepositoryImpl + private let repository: AuthAPIRepository - init(repository: AuthAPIRepositoryImpl) { + init(repository: AuthAPIRepository) { self.repository = repository } diff --git a/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index a0ab23d7..c7a31242 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -1,19 +1,12 @@ -// -// CommentAPIUseCaseImpl.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import Foundation import RxSwift -final class CommentAPIUseCaseImpl { - - var repository: CommentAPIRepositoryImpl +final class CommentAPIUseCaseImpl: CommentAPIUseCase { + + private let repository: CommentAPIRepository - init(repository: CommentAPIRepositoryImpl) { + init(repository: CommentAPIRepository) { self.repository = repository } diff --git a/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index fe41ddc4..cd665ca5 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -1,15 +1,14 @@ -// -// HomeAPIUseCaseImpl.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation + import RxSwift -final class HomeAPIUseCaseImpl { - var repository = HomeAPIRepositoryImpl(provider: ProviderImpl()) +final class HomeAPIUseCaseImpl: HomeAPIUseCase { + + private let repository: HomeAPIRepository + + init(repository: HomeAPIRepository) { + self.repository = repository + } func fetchHome( page: Int32?, diff --git a/Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift deleted file mode 100644 index 0c7ea395..00000000 --- a/Poppool/Poppool/Domain/UseCaseImpl/MapDirectionUseCase.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// MapDirectionUseCase.swift -// Poppool -// -// Created by 김기현 on 1/23/25. -// - -import Foundation -import RxSwift - -protocol MapDirectionUseCase { - func getPopUpDirection(popUpStoreId: Int64) -> Observable -} - -final class DefaultMapDirectionUseCase: MapDirectionUseCase { - private let repository: MapDirectionRepository - - init(repository: MapDirectionRepository) { - self.repository = repository - } - - func getPopUpDirection(popUpStoreId: Int64) -> Observable { - return repository.getPopUpDirection(popUpStoreId: popUpStoreId) - .map { $0.toDomain() } - } -} diff --git a/Poppool/Poppool/Domain/UseCaseImpl/MapUseCase.swift b/Poppool/Poppool/Domain/UseCaseImpl/MapUseCaseImpl.swift similarity index 77% rename from Poppool/Poppool/Domain/UseCaseImpl/MapUseCase.swift rename to Poppool/Poppool/Domain/UseCaseImpl/MapUseCaseImpl.swift index 660ee9d0..8ee63c44 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/MapUseCase.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -1,26 +1,9 @@ import Foundation -import RxSwift -protocol MapUseCase { - func fetchCategories() -> Observable<[Category]> - func fetchStoresInBounds( - northEastLat: Double, - northEastLon: Double, - southWestLat: Double, - southWestLon: Double, - categories: [Int64] - ) -> Observable<[MapPopUpStore]> - - func searchStores( - query: String, - categories: [Int64] - ) -> Observable<[MapPopUpStore]> - - func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] +import RxSwift -} +final class MapUseCaseImpl: MapUseCase { -class DefaultMapUseCase: MapUseCase { private let repository: MapRepository init(repository: MapRepository) { diff --git a/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index 6c405d4a..8049526a 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -1,19 +1,12 @@ -// -// PopUpAPIUseCaseImpl.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation import RxSwift -final class PopUpAPIUseCaseImpl { +final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { - var repository: PopUpAPIRepositoryImpl + private let repository: PopUpAPIRepository - init(repository: PopUpAPIRepositoryImpl) { + init(repository: PopUpAPIRepository) { self.repository = repository } diff --git a/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index 006d48fe..d739f9ec 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -1,19 +1,14 @@ -// -// SignUpAPIUseCaseImpl.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation + import RxSwift -final class SignUpAPIUseCaseImpl { - var repository: SignUpRepositoryImpl +final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { + private let repository: SignUpRepository - init(repository: SignUpRepositoryImpl) { + init(repository: SignUpRepository) { self.repository = repository } + func trySignUp( nickName: String, gender: String, @@ -33,6 +28,7 @@ final class SignUpAPIUseCaseImpl { appleAuthorizationCode: appleAuthorizationCode ) } + func checkNickName(nickName: String) -> Observable { return repository.checkNickName(nickName: nickName) } diff --git a/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index 1c907205..55a16028 100644 --- a/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -1,17 +1,11 @@ -// -// UserAPIUseCaseImpl.swift -// Poppool -// -// Created by SeoJunYoung on 12/3/24. -// +import Foundation import RxSwift -final class UserAPIUseCaseImpl { +final class UserAPIUseCaseImpl: UserAPIUseCase { + private let repository: UserAPIRepository - var repository: UserAPIRepositoryImpl - - init(repository: UserAPIRepositoryImpl) { + init(repository: UserAPIRepository) { self.repository = repository } diff --git a/Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift new file mode 100644 index 00000000..390c45fb --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift @@ -0,0 +1,16 @@ +import Foundation + +import RxSwift + +protocol AdminUseCase { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable + func fetchStoreDetail(id: Int64) -> Observable + func createStore(request: CreatePopUpStoreRequestDTO) -> Observable + func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable + func deleteStore(id: Int64) -> Observable + + // Notice + func createNotice(request: CreateNoticeRequestDTO) -> Observable + func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable + func deleteNotice(id: Int64) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift new file mode 100644 index 00000000..0ccfaa35 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift @@ -0,0 +1,8 @@ +import Foundation + +import RxSwift + +protocol AuthAPIUseCase { + func postTryLogin(userCredential: Encodable, socialType: String) -> Observable + func postTokenReissue() -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift new file mode 100644 index 00000000..be737e40 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift @@ -0,0 +1,9 @@ +import Foundation + +import RxSwift + +protocol CommentAPIUseCase { + func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable + func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable + func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift new file mode 100644 index 00000000..8bb6b392 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift @@ -0,0 +1,10 @@ +import Foundation + +import RxSwift + +protocol HomeAPIUseCase { + func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift new file mode 100644 index 00000000..954260ad --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift @@ -0,0 +1,29 @@ +// +// MapUseCase.swift +// Poppool +// +// Created by 송영훈 on 4/14/25. +// + + +import Foundation + +import RxSwift + +protocol MapUseCase { + func fetchCategories() -> Observable<[Category]> + func fetchStoresInBounds( + northEastLat: Double, + northEastLon: Double, + southWestLat: Double, + southWestLon: Double, + categories: [Int64] + ) -> Observable<[MapPopUpStore]> + + func searchStores( + query: String, + categories: [Int64] + ) -> Observable<[MapPopUpStore]> + + func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift new file mode 100644 index 00000000..1e122002 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift @@ -0,0 +1,10 @@ +import Foundation + +import RxSwift + +protocol PopUpAPIUseCase { + func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable + func getSearchPopUpList(query: String?) -> Observable + func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool?) -> Observable + func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift new file mode 100644 index 00000000..416194f9 --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift @@ -0,0 +1,19 @@ +import Foundation + +import RxSwift + +protocol SignUpAPIUseCase { + func trySignUp( + nickName: String, + gender: String, + age: Int32, + socialEmail: String, + socialType: String, + interests: [Int64], + appleAuthorizationCode: String? + ) -> Completable + + func checkNickName(nickName: String) -> Observable + + func fetchCategoryList() -> Observable<[Category]> +} diff --git a/Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift b/Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift new file mode 100644 index 00000000..e73f4cdf --- /dev/null +++ b/Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift @@ -0,0 +1,27 @@ +import Foundation + +import RxSwift + +protocol UserAPIUseCase { + func postBookmarkPopUp(popUpID: Int64) -> Completable + func deleteBookmarkPopUp(popUpID: Int64) -> Completable + func postCommentLike(commentId: Int64) -> Completable + func deleteCommentLike(commentId: Int64) -> Completable + func postUserBlock(blockedUserId: String?) -> Completable + func deleteUserBlock(blockedUserId: String?) -> Completable + func getOtherUserCommentedPopUpList(commenterId: String?, commentType: String?, page: Int32?, size: Int32?, sort: String?) -> Observable + func getMyPage() -> Observable + func postLogout() -> Completable + func getWithdrawlList() -> Observable + func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable + func getMyProfile() -> Observable + func putUserTailoredInfo(gender: String?, age: Int32) -> Completable + func putUserCategory(interestCategoriesToAdd: [Int64], interestCategoriesToDelete: [Int64], interestCategoriesToKeep: [Int64]) -> Completable + func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable + func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable + func getNoticeList() -> Observable + func getNoticeDetail(noticeID: Int64) -> Observable + func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable +} diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift index d343a466..b38efef8 100644 --- a/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift @@ -16,7 +16,7 @@ final class AdminViewController: BaseViewController, View { private let adminUseCase: AdminUseCase // MARK: - Init - init(nickname: String, adminUseCase: AdminUseCase = DefaultAdminUseCase(repository: AdminRepositoryImpl(provider: ProviderImpl()))) { + init(nickname: String, adminUseCase: AdminUseCase = AdminUseCaseImpl(repository: AdminRepositoryImpl(provider: ProviderImpl()))) { self.nickname = nickname self.adminUseCase = adminUseCase self.mainView = AdminView(frame: .zero) diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift index 23903e0b..1e3d37b3 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift @@ -43,7 +43,7 @@ final class HomeListReactor: Reactor { var disposeBag = DisposeBag() var popUpType: HomePopUpType - private let homeAPIUseCase = HomeAPIUseCaseImpl() + private let homeAPIUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) private let userDefaultService = UserDefaultService() private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift index aa54ee84..1c4ebf67 100644 --- a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift @@ -39,7 +39,7 @@ final class HomeReactor: Reactor { var disposeBag = DisposeBag() - private let homeApiUseCase = HomeAPIUseCaseImpl() + private let homeApiUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) private let userDefaultService = UserDefaultService() diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 7282142a..6da635ef 100644 --- a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -173,7 +173,7 @@ final class MapGuideViewController: UIViewController, View { let providerInstance = ProviderImpl() let repositoryInstance = MapRepositoryImpl(provider: providerInstance) - let useCaseInstance = DefaultMapUseCase(repository: repositoryInstance) + let useCaseInstance = MapUseCaseImpl(repository: repositoryInstance) let directionRepositoryInstance = MapDirectionRepositoryImpl(provider: providerInstance) let mapReactorInstance = MapReactor(useCase: useCaseInstance, directionRepository: directionRepositoryInstance) diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 503861b4..3777f7e1 100644 --- a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -274,7 +274,7 @@ final class MyPageReactor: Reactor { let nickname = profileSection.inputDataList.first?.nickName ?? "" let adminVC = AdminViewController(nickname: nickname) adminVC.reactor = AdminReactor( - useCase: DefaultAdminUseCase( + useCase: AdminUseCaseImpl( repository: AdminRepositoryImpl(provider: ProviderImpl()) ) ) diff --git a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift index 77e2659e..bd0dc0a1 100644 --- a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift @@ -196,7 +196,7 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let provider = ProviderImpl() let mapController = MapViewController() - let mapUseCase = DefaultMapUseCase(repository: MapRepositoryImpl(provider: provider)) + let mapUseCase = MapUseCaseImpl(repository: MapRepositoryImpl(provider: provider)) let directionRepository = MapDirectionRepositoryImpl(provider: provider) mapController.reactor = MapReactor(useCase: mapUseCase, directionRepository: directionRepository) From 448218ab83aa36545977e714545c5124cd7474d1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 22:35:36 +0900 Subject: [PATCH 111/393] =?UTF-8?q?refactor/#112:=20final,=20private=20let?= =?UTF-8?q?=20=EC=9C=BC=EB=A1=9C=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift | 5 ++--- .../Data/RepositoryImpl/MapDirectionRepositoryImpl.swift | 1 + Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift | 3 ++- .../Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift | 1 + .../Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift index dbb6c689..a0847bd2 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -4,9 +4,8 @@ import RxSwift final class AuthAPIRepositoryImpl: AuthAPIRepository { - var provider: Provider - - var tokenInterceptor = TokenInterceptor() + private let provider: Provider + private let tokenInterceptor = TokenInterceptor() init(provider: Provider) { self.provider = provider diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index b4c3a861..8d0fa37c 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -3,6 +3,7 @@ import Foundation import RxSwift final class MapDirectionRepositoryImpl: MapDirectionRepository { + private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift index c9487f87..d9edb5d5 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -3,7 +3,8 @@ import Foundation import RxSwift // MARK: - Implementation -class MapRepositoryImpl: MapRepository { +final class MapRepositoryImpl: MapRepository { + private let provider: Provider init(provider: Provider) { diff --git a/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift index 735bd339..58f8fb5a 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -3,6 +3,7 @@ import Foundation import RxSwift final class PopUpAPIRepositoryImpl: PopUpAPIRepository { + private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift index 4cdef81e..12d5da59 100644 --- a/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -4,7 +4,7 @@ import RxSwift final class SignUpRepositoryImpl: SignUpRepository { - var provider: Provider + private let provider: Provider init(provider: Provider) { self.provider = provider From 3448cac89ce5780d9bca841eaf3ef8c6772f7b95 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 22:38:30 +0900 Subject: [PATCH 112/393] =?UTF-8?q?refactor/#112:=20DTO=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SignUpAPI/{ => RequestDTO}/CheckNickNameRequestDTO.swift | 0 .../Network/SignUpAPI/{ => RequestDTO}/SignUpRequestDTO.swift | 0 .../SignUpAPI/{ => ResponseDTO}/GetCategoryListResponseDTO.swift | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/Data/Network/SignUpAPI/{ => RequestDTO}/CheckNickNameRequestDTO.swift (100%) rename Poppool/Poppool/Data/Network/SignUpAPI/{ => RequestDTO}/SignUpRequestDTO.swift (100%) rename Poppool/Poppool/Data/Network/SignUpAPI/{ => ResponseDTO}/GetCategoryListResponseDTO.swift (100%) diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/CheckNickNameRequestDTO.swift b/Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/CheckNickNameRequestDTO.swift rename to Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpRequestDTO.swift b/Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/SignUpRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/SignUpRequestDTO.swift rename to Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/SignUpRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/GetCategoryListResponseDTO.swift b/Poppool/Poppool/Data/Network/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/GetCategoryListResponseDTO.swift rename to Poppool/Poppool/Data/Network/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift From 405e9fe9bbc143e3755891a2b8beba8a647caf55 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 14 Apr 2025 23:17:23 +0900 Subject: [PATCH 113/393] =?UTF-8?q?refactor/#112:=20Presigned=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => PreSignedAPI}/PreSignedAPIEndPoint.swift | 0 .../{ => PreSignedAPI/RequestDTO}/PresignedURLRequestDTO.swift | 0 .../{ => PreSignedAPI/ResponseDTO}/PreSignedURLDTO.swift | 0 .../{ => PreSignedAPI/ResponseDTO}/PreSignedURLResponseDTO.swift | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/Infrastructure/PreSignedService/{ => PreSignedAPI}/PreSignedAPIEndPoint.swift (100%) rename Poppool/Poppool/Infrastructure/PreSignedService/{ => PreSignedAPI/RequestDTO}/PresignedURLRequestDTO.swift (100%) rename Poppool/Poppool/Infrastructure/PreSignedService/{ => PreSignedAPI/ResponseDTO}/PreSignedURLDTO.swift (100%) rename Poppool/Poppool/Infrastructure/PreSignedService/{ => PreSignedAPI/ResponseDTO}/PreSignedURLResponseDTO.swift (100%) diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/PreSignedAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPIEndPoint.swift rename to Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/PreSignedAPIEndPoint.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PresignedURLRequestDTO.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PresignedURLRequestDTO.swift rename to Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedURLDTO.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedURLDTO.swift rename to Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedURLResponseDTO.swift b/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedURLResponseDTO.swift rename to Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift From 0e1fad05f1a2c31831ec1f132c09da2ed6c3d6db Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 15 Apr 2025 00:53:10 +0900 Subject: [PATCH 114/393] =?UTF-8?q?refactor/#112:=20=ED=81=B4=EB=A6=B0?= =?UTF-8?q?=EC=95=84=ED=82=A4=ED=85=8D=EC=B2=98=20=EB=B2=A0=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=ED=8F=B4=EB=8D=94=EB=A7=81=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Poppool/{ => CoreLayer}/Infrastructure/Extension/Date?+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/Reactive+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/String?+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIApplication+.swift | 0 .../Infrastructure/Extension/UICollectionReusableView+.swift | 0 .../Infrastructure/Extension/UICollectionViewCell+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIColor+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIFont+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIImage+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIImageView+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UILabel+.swift | 0 .../Infrastructure/Extension/UINavigationController+.swift | 0 .../Infrastructure/Extension/UITableViewCell+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UITextField+.swift | 0 .../{ => CoreLayer}/Infrastructure/Extension/UIView+.swift | 0 .../{ => CoreLayer}/Infrastructure/ImageLoader/DiskStorage.swift | 0 .../{ => CoreLayer}/Infrastructure/ImageLoader/ImageLoader.swift | 0 .../Infrastructure/ImageLoader/MemoryStorage.swift | 0 .../Infrastructure}/IndicatorMaker/IndicatorMaker.swift | 0 .../Poppool/{ => CoreLayer}/Infrastructure/Logger/Logger.swift | 0 .../Poppool/{Resource => CoreLayer/Infrastructure}/Secrets.swift | 0 .../Network/API}/AdminAPI/AdminAPIEndpoint.swift | 0 .../Network/API}/AdminAPI/ResponseDTO/AdminResponseDTO.swift | 0 .../AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift | 0 .../Network/API}/AuthAPI/AuthAPIEndPoint.swift | 0 .../Network/API}/AuthAPI/ResponseDTO/LoginResponseDTO.swift | 0 .../API}/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift | 0 .../Network/API}/CommentAPI/CommentAPIEndPoint.swift | 0 .../API}/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift | 0 .../API}/CommentAPI/RequestDTO/PostCommentRequestDTO.swift | 0 .../Network/API}/CommentAPI/RequestDTO/PutCommentRequestDTO.swift | 0 .../Network/API}/HomeAPI/HomeAPIEndpoint.swift | 0 .../Network/API}/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift | 0 .../Network/API}/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift | 0 .../Network/API}/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift | 0 .../Network/API}/MapAPI/FindDirectionEndPoint.swift | 0 .../Network => DataLayer/Network/API}/MapAPI/MapAPIEndpoint.swift | 0 .../API}/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift | 0 .../Network/API}/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift | 0 .../Network/API}/PopUpAPI/PopUpAPIEndPoint.swift | 0 .../API}/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift | 0 .../API}/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift | 0 .../API}/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift | 0 .../API}/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift | 0 .../API}/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift | 0 .../API}/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift | 0 .../API}/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift | 0 .../API}/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift | 0 .../Network/API}/PreSignedAPI/PreSignedAPIEndPoint.swift | 0 .../API}/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift | 0 .../Network/API}/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift | 0 .../API}/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift | 0 .../API}/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift | 0 .../Network/API}/SignUpAPI/RequestDTO/SignUpRequestDTO.swift | 0 .../API}/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift | 0 .../Network/API}/SignUpAPI/SignUpAPIEndpoint.swift | 0 .../Network => DataLayer/Network/API}/SortedRequestDTO.swift | 0 .../Network/API}/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift | 0 .../Network/API}/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift | 0 .../UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift | 0 .../API}/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift | 0 .../Network/API}/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift | 0 .../ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift | 0 .../API}/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift | 0 .../Network/API}/UserAPI/UserAPIEndPoint.swift | 0 .../NetworkLayer => DataLayer/Network}/Common/NetworkError.swift | 0 .../NetworkLayer => DataLayer/Network}/Common/Requestable.swift | 0 .../NetworkLayer => DataLayer/Network}/Common/Responsable.swift | 0 .../NetworkLayer => DataLayer/Network}/EndPoint/Endpoint.swift | 0 .../Network}/EndPoint/MultipartEndPoint.swift | 0 .../Network}/EndPoint/RequestEndpoint.swift | 0 .../Network}/Interceptor/FormDataInterceptor.swift | 0 .../Network}/Interceptor/TokenInterceptor.swift | 0 .../NetworkLayer => DataLayer/Network}/Provider/Provider.swift | 0 .../Network}/Provider/ProviderImpl.swift | 0 .../Network/Service}/AppleLoginService.swift | 0 .../Network/Service}/AuthServiceable.swift | 0 .../Network/Service}/KakaoLoginService.swift | 0 .../Network/Service}/KeyChainService.swift | 0 .../Network/Service}/PreSignedService.swift | 0 .../Network/Service}/UserDefaultService.swift | 0 .../{Data => DataLayer}/RepositoryImpl/AdminRepositoryImpl.swift | 0 .../RepositoryImpl/AuthAPIRepositoryImpl.swift | 0 .../RepositoryImpl/CommentAPIRepositoryImpl.swift | 0 .../RepositoryImpl/HomeAPIRepositoryImpl.swift | 0 .../RepositoryImpl/MapDirectionRepositoryImpl.swift | 0 .../{Data => DataLayer}/RepositoryImpl/MapRepositoryImpl.swift | 0 .../RepositoryImpl/PopUpAPIRepositoryImpl.swift | 0 .../{Data => DataLayer}/RepositoryImpl/SignUpRepositoryImpl.swift | 0 .../RepositoryImpl/UserAPIRepositoryImpl.swift | 0 .../{ => DomainLayer}/Domain/UseCaseImpl/AdminUseCaseImpl.swift | 0 .../{ => DomainLayer}/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift | 0 .../Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift | 0 .../{ => DomainLayer}/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift | 0 .../{ => DomainLayer}/Domain/UseCaseImpl/MapUseCaseImpl.swift | 0 .../Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift | 0 .../Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift | 0 .../{ => DomainLayer}/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift | 0 .../DomainInterface/Entity/AuthResponse/LoginResponse.swift | 0 .../Entity/AuthResponse/PostTokenReissueResponse.swift | 0 .../DomainInterface/Entity/BannerPopUpStore.swift | 0 .../{ => DomainLayer}/DomainInterface/Entity/Category.swift | 0 .../DomainInterface/Entity/GetHomeInfoResponse.swift | 0 .../{ => DomainLayer}/DomainInterface/Entity/MapPopUpStore.swift | 0 .../Entity/PopUpResponse/GetPopUpCommentResponse.swift | 0 .../Entity/PopUpResponse/GetPopUpDetailResponse.swift | 0 .../Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift | 0 .../Entity/PopUpResponse/GetSearchPopUpListResponse.swift | 0 .../DomainInterface/Entity/PopUpStoreResponse.swift | 0 .../Entity/UserResponse/GetBlockUserListResponse.swift | 0 .../Entity/UserResponse/GetMyCommentResponse.swift | 0 .../DomainInterface/Entity/UserResponse/GetMyPageResponse.swift | 0 .../Entity/UserResponse/GetMyProfileResponse.swift | 0 .../Entity/UserResponse/GetNoticeDetailResponse.swift | 0 .../Entity/UserResponse/GetNoticeListResponse.swift | 0 .../UserResponse/GetOtherUserCommentedPopUpListResponse.swift | 0 .../Entity/UserResponse/GetRecentPopUpResponse.swift | 0 .../Entity/UserResponse/GetWithdrawlListResponse.swift | 0 .../DomainInterface/Repository/AdminRepository.swift | 0 .../DomainInterface/Repository/AuthAPIRepository.swift | 0 .../DomainInterface/Repository/CommentAPIRepository.swift | 0 .../DomainInterface/Repository/HomeAPIRepository.swift | 0 .../DomainInterface/Repository/MapDirectionRepository.swift | 0 .../DomainInterface/Repository/MapRepository.swift | 0 .../DomainInterface/Repository/PopUpAPIRepository.swift | 0 .../DomainInterface/Repository/SignUpRepository.swift | 0 .../DomainInterface/Repository/UserAPIRepository.swift | 0 .../{ => DomainLayer}/DomainInterface/UseCase/AdminUseCase.swift | 0 .../DomainInterface/UseCase/AuthAPIUseCase.swift | 0 .../DomainInterface/UseCase/CommentAPIUseCase.swift | 0 .../DomainInterface/UseCase/HomeAPIUseCase.swift | 0 .../{ => DomainLayer}/DomainInterface/UseCase/MapUseCase.swift | 0 .../DomainInterface/UseCase/PopUpAPIUseCase.swift | 0 .../DomainInterface/UseCase/SignUpAPIUseCase.swift | 0 .../DomainInterface/UseCase/UserAPIUseCase.swift | 0 .../Presentation/Components/PPButton.swift | 0 .../Presentation/Components/PPCancelHeaderView.swift | 0 .../{ => PresentationLayer}/Presentation/Components/PPLabel.swift | 0 .../Presentation/Components/PPPicker.swift | 0 .../Components/PPProgressIndicator/PPProgressIndicator.swift | 0 .../Components/PPProgressIndicator/PPProgressView.swift | 0 .../Presentation/Components/PPReturnHeaderView.swift | 0 .../Presentation/Components/PPSegmentedControl.swift | 0 .../Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift | 0 .../Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift | 0 .../Admin/AdminBottomSheet/AdminBottomSheetViewController.swift | 0 .../Presentation/Scene/Admin/AdminReactor.swift | 0 .../Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift | 0 .../Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift | 0 .../Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift | 0 .../Admin/AdminRegister/PopUpStoreRegisterViewController.swift | 0 .../Presentation/Scene/Admin/AdminStoreCell.swift | 0 .../Presentation/Scene/Admin/AdminView.swift | 0 .../Presentation/Scene/Admin/AdminViewController.swift | 0 .../Presentation/Scene/Admin/ImageCell.swift | 0 .../Scene/Comment/CommentCheck/CommentCheckController.swift | 0 .../Scene/Comment/CommentCheck/CommentCheckReactor.swift | 0 .../Scene/Comment/CommentCheck/CommentCheckView.swift | 0 .../Scene/Comment/CommentDetail/CommentDetailController.swift | 0 .../Scene/Comment/CommentDetail/CommentDetailReactor.swift | 0 .../CommentDetailContentSection/CommentDetailContentSection.swift | 0 .../CommentDetailContentSectionCell.swift | 0 .../Comment/CommentDetail/View/CommentDetailImageSection.swift | 0 .../Scene/Comment/CommentDetail/View/CommentDetailView.swift | 0 .../CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift | 0 .../CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift | 0 .../Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift | 0 .../CommentUserInfo/CommentUserInfoController.swift | 0 .../CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift | 0 .../CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift | 0 .../Scene/Comment/CommentList/CommentListController.swift | 0 .../Scene/Comment/CommentList/CommentListReactor.swift | 0 .../View/CommentListTitleSection/CommentListTitleSection.swift | 0 .../CommentListTitleSection/CommentListTitleSectionCell.swift | 0 .../Scene/Comment/CommentList/View/CommentListView.swift | 0 .../Scene/Comment/CommentSelected/CommentSelectedController.swift | 0 .../Scene/Comment/CommentSelected/CommentSelectedReactor.swift | 0 .../Scene/Comment/CommentSelected/CommentSelectedView.swift | 0 .../Comment/CommentUserBlock/CommentUserBlockController.swift | 0 .../Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift | 0 .../Scene/Comment/CommentUserBlock/CommentUserBlockView.swift | 0 .../Scene/Comment/InstaComment/InstaCommentAddController.swift | 0 .../Scene/Comment/InstaComment/InstaCommentAddReactor.swift | 0 .../Scene/Comment/InstaComment/View/InstaCommentAddView.swift | 0 .../InstaGuideChildSection/InstaGuideChildSection.swift | 0 .../InstaGuideChildSection/InstaGuideChildSectionCell.swift | 0 .../InstaGuideSection/InstaGuideSection/InstaGuideSection.swift | 0 .../InstaGuideSection/InstaGuideSectionCell.swift | 0 .../Comment/NormalCommentAdd/NormalCommentAddController.swift | 0 .../Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift | 0 .../AddCommentDescriptionSection.swift | 0 .../AddCommentDescriptionSectionCell.swift | 0 .../View/AddCommentImageSection/AddCommentImageSection.swift | 0 .../View/AddCommentImageSection/AddCommentImageSectionCell.swift | 0 .../View/AddCommentSection/AddCommentSection.swift | 0 .../View/AddCommentSection/AddCommentSectionCell.swift | 0 .../View/AddCommentTitleSection/AddCommentTitleSection.swift | 0 .../View/AddCommentTitleSection/AddCommentTitleSectionCell.swift | 0 .../Comment/NormalCommentAdd/View/NormalCommentAddView.swift | 0 .../Comment/NormalCommentEdit/NormalCommentEditController.swift | 0 .../Comment/NormalCommentEdit/NormalCommentEditReactor.swift | 0 .../Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift | 0 .../Comment/OtherUserComment/OtherUserCommentController.swift | 0 .../Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift | 0 .../View/OtherUserCommentSection/OtherUserCommentSection.swift | 0 .../OtherUserCommentSection/OtherUserCommentSectionCell.swift | 0 .../Comment/OtherUserComment/View/OtherUserCommentView.swift | 0 .../Presentation/Scene/Detail/DetailController.swift | 0 .../Presentation/Scene/Detail/DetailReactor.swift | 0 .../Detail/View/DetailCommentSection/DetailCommentImageCell.swift | 0 .../View/DetailCommentSection/DetailCommentProfileView.swift | 0 .../Detail/View/DetailCommentSection/DetailCommentSection.swift | 0 .../View/DetailCommentSection/DetailCommentSectionCell.swift | 0 .../DetailCommentTitleSection/DetailCommentTitleSection.swift | 0 .../DetailCommentTitleSection/DetailCommentTitleSectionCell.swift | 0 .../Detail/View/DetailContentSection/DetailContentSection.swift | 0 .../View/DetailContentSection/DetailContentSectionCell.swift | 0 .../View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift | 0 .../DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift | 0 .../Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift | 0 .../Detail/View/DetailInfoSection/DetailInfoSectionCell.swift | 0 .../Detail/View/DetailSimilarSection/DetailSimilarSection.swift | 0 .../View/DetailSimilarSection/DetailSimilarSectionCell.swift | 0 .../Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift | 0 .../Detail/View/DetailTitleSection/DetailTitleSectionCell.swift | 0 .../Presentation/Scene/Detail/View/DetailView.swift | 0 .../Presentation/Scene/Home/List/HomeListController.swift | 0 .../Presentation/Scene/Home/List/HomeListReactor.swift | 0 .../Presentation/Scene/Home/List/HomePopUpType.swift | 0 .../Presentation/Scene/Home/List/View/HomeCardGridSection.swift | 0 .../Presentation/Scene/Home/List/View/HomeListView.swift | 0 .../Presentation/Scene/Home/Main/HomeController.swift | 0 .../Presentation/Scene/Home/Main/HomeReactor.swift | 0 .../Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift | 0 .../Home/Main/View/HomeCardSection/HomeCardSectionCell.swift | 0 .../Presentation/Scene/Home/Main/View/HomeHeaderView.swift | 0 .../Main/View/HomePopularCardSection/HomePopularCardSection.swift | 0 .../View/HomePopularCardSection/HomePopularCardSectionCell.swift | 0 .../Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift | 0 .../Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift | 0 .../Presentation/Scene/Home/Main/View/HomeView.swift | 0 .../ImageBannerChildSection/ImageBannerChildSection.swift | 0 .../ImageBannerChildSection/ImageBannerChildSectionCell.swift | 0 .../ImageBannerSection/ImageBannerSection.swift | 0 .../ImageBannerSection/ImageBannerSectionCell.swift | 0 .../Scene/Home/Main/View/SectionBackGroundDecorationView.swift | 0 .../Scene/Home/Main/View/SpacingSection/SpacingSection.swift | 0 .../Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift | 0 .../Presentation/Scene/ImageDetail/ImageDetailController.swift | 0 .../Presentation/Scene/ImageDetail/ImageDetailReactor.swift | 0 .../Presentation/Scene/ImageDetail/ImageDetailView.swift | 0 .../Presentation/Scene/Login/LastLoginView.swift | 0 .../Presentation/Scene/Login/Main/LoginController.swift | 0 .../Presentation/Scene/Login/Main/LoginReactor.swift | 0 .../Presentation/Scene/Login/Main/LoginView.swift | 0 .../Presentation/Scene/Login/Sub/SubLoginController.swift | 0 .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 0 .../Presentation/Scene/Login/Sub/SubLoginView.swift | 0 .../Scene/Map/FillterSheetView/BalloonBackgroundView.swift | 0 .../Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift | 0 .../Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift | 0 .../Scene/Map/FillterSheetView/FilterBottomSheetView.swift | 0 .../Map/FillterSheetView/FilterBottomSheetViewController.swift | 0 .../Presentation/Scene/Map/FillterSheetView/FilterCell.swift | 0 .../Presentation/Scene/Map/FillterSheetView/FilterChip.swift | 0 .../Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift | 0 .../Map/FindMap/MapGuideView/FullScreenMapViewController.swift | 0 .../Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift | 0 .../Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift | 0 .../Scene/Map/MapPopupCardView/MapPopupCarouselView.swift | 0 .../Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift | 0 .../Presentation/Scene/Map/MapView/MapMarker.swift | 0 .../Presentation/Scene/Map/MapView/MapReactor.swift | 0 .../Presentation/Scene/Map/MapView/MapSearchInput.swift | 0 .../Presentation/Scene/Map/MapView/MapView.swift | 0 .../Presentation/Scene/Map/MapView/MapViewController.swift | 0 .../Presentation/Scene/Map/MapView/MarkerTooltipView.swift | 0 .../Presentation/Scene/Map/StoreListView/StoreListCell.swift | 0 .../Presentation/Scene/Map/StoreListView/StoreListReactor.swift | 0 .../Presentation/Scene/Map/StoreListView/StoreListSection.swift | 0 .../Presentation/Scene/Map/StoreListView/StoreListView.swift | 0 .../Scene/Map/StoreListView/StoreListViewController.swift | 0 .../Scene/MyPage/Block/BlockUserManageController.swift | 0 .../Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift | 0 .../Block/View/BlockUserListSection/BlockUserListSection.swift | 0 .../View/BlockUserListSection/BlockUserListSectionCell.swift | 0 .../Scene/MyPage/Block/View/BlockUserManageView.swift | 0 .../Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift | 0 .../Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift | 0 .../Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift | 0 .../Scene/MyPage/Bookmark/Main/View/CountButtonView.swift | 0 .../Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift | 0 .../Main/View/PopUpCardSection/PopUpCardSectionCell.swift | 0 .../Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift | 0 .../ViewTypeModal/BookMarkPopUpViewTypeModalController.swift | 0 .../ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift | 0 .../Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift | 0 .../Presentation/Scene/MyPage/FAQ/FAQController.swift | 0 .../Presentation/Scene/MyPage/FAQ/FAQReactor.swift | 0 .../MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift | 0 .../FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift | 0 .../Presentation/Scene/MyPage/FAQ/View/FAQView.swift | 0 .../Presentation/Scene/MyPage/Main/MyPageController.swift | 0 .../Presentation/Scene/MyPage/Main/MyPageReactor.swift | 0 .../Main/View/MyPageCommentSection/MyPageCommentSection.swift | 0 .../Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift | 0 .../MyPage/Main/View/MyPageListSection/MyPageListSection.swift | 0 .../Main/View/MyPageListSection/MyPageListSectionCell.swift | 0 .../Main/View/MyPageLogoutSection/MyPageLogoutSection.swift | 0 .../Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift | 0 .../MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift | 0 .../MyPageMyCommentTitleSectionCell.swift | 0 .../Main/View/MyPageProfileSection/MyPageProfileSection.swift | 0 .../Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift | 0 .../Presentation/Scene/MyPage/Main/View/MyPageView.swift | 0 .../Scene/MyPage/MyComment/Main/MyCommentController.swift | 0 .../Scene/MyPage/MyComment/Main/MyCommentReactor.swift | 0 .../Main/View/ListCountButtonSection/ListCountButtonSection.swift | 0 .../View/ListCountButtonSection/ListCountButtonSectionCell.swift | 0 .../Scene/MyPage/MyComment/Main/View/MyCommentView.swift | 0 .../MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift | 0 .../MyCommentedPopUpGridSectionCell.swift | 0 .../MyComment/SortedModal/MyCommentSortedModalController.swift | 0 .../MyComment/SortedModal/MyCommentSortedModalReactor.swift | 0 .../MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift | 0 .../Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift | 0 .../Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift | 0 .../Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift | 0 .../Scene/MyPage/Notice/List/MyPageNoticeController.swift | 0 .../Scene/MyPage/Notice/List/MyPageNoticeReactor.swift | 0 .../Scene/MyPage/Notice/List/View/MyPageNoticeView.swift | 0 .../Notice/List/View/NoticeListSection/NoticeListSection.swift | 0 .../List/View/NoticeListSection/NoticeListSectionCell.swift | 0 .../CategoryEditModal/CategoryEditModalController.swift | 0 .../ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift | 0 .../ProfileEdit/CategoryEditModal/CategoryEditModalView.swift | 0 .../ProfileEdit/InfoEditModal/InfoEditModalController.swift | 0 .../MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift | 0 .../MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift | 0 .../Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift | 0 .../Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift | 0 .../MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift | 0 .../Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift | 0 .../Presentation/Scene/MyPage/Recent/MyPageRecentController.swift | 0 .../Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift | 0 .../Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift | 0 .../Recent/View/RecentPopUpSection/RecentPopUpSection.swift | 0 .../Presentation/Scene/MyPage/Terms/MyPageTermsController.swift | 0 .../Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift | 0 .../Withdrawl/CheckModal/WithdrawlCheckModalController.swift | 0 .../MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift | 0 .../MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift | 0 .../MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift | 0 .../Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift | 0 .../Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift | 0 .../Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift | 0 .../Withdrawl/SelectedReason/View/WithdrawlReasonView.swift | 0 .../Withdrawl/SelectedReason/WithdrawlReasonController.swift | 0 .../MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift | 0 .../Scene/Search/AfterSearch/SearchResultController.swift | 0 .../Scene/Search/AfterSearch/SearchResultReactor.swift | 0 .../View/SearchResultCountSection/SearchResultCountSection.swift | 0 .../SearchResultCountSection/SearchResultCountSectionCell.swift | 0 .../Scene/Search/AfterSearch/View/SearchResultView.swift | 0 .../Presentation/Scene/Search/BeforeSearch/SearchController.swift | 0 .../Presentation/Scene/Search/BeforeSearch/SearchReactor.swift | 0 .../View/CancelableTagSection/CancelableTagSection.swift | 0 .../View/CancelableTagSection/CancelableTagSectionCell.swift | 0 .../View/SearchCountTitleSection/SearchCountTitleSection.swift | 0 .../SearchCountTitleSection/SearchCountTitleSectionCell.swift | 0 .../BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift | 0 .../View/SearchTitleSection/SearchTitleSectionCell.swift | 0 .../Presentation/Scene/Search/BeforeSearch/View/SearchView.swift | 0 .../Search/CategoryController/SearchCategoryController.swift | 0 .../Scene/Search/CategoryController/SearchCategoryReactor.swift | 0 .../Scene/Search/CategoryController/SearchCategoryView.swift | 0 .../Presentation/Scene/Search/Main/SearchMainController.swift | 0 .../Presentation/Scene/Search/Main/SearchMainReactor.swift | 0 .../Presentation/Scene/Search/Main/SearchMainView.swift | 0 .../Scene/Search/SortedController/SearchSortedController.swift | 0 .../Scene/Search/SortedController/SearchSortedReactor.swift | 0 .../Scene/Search/SortedController/SearchSortedView.swift | 0 .../Presentation/Scene/SignUp/Main/SignUpMainController.swift | 0 .../Presentation/Scene/SignUp/Main/SignUpMainReactor.swift | 0 .../Presentation/Scene/SignUp/Main/View/SignUpMainView.swift | 0 .../Scene/SignUp/SignUpComplete/SignUpCompleteController.swift | 0 .../Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift | 0 .../Scene/SignUp/SignUpComplete/SignUpCompleteView.swift | 0 .../Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift | 0 .../Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift | 0 .../Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift | 0 .../Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift | 0 .../Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift | 0 .../Presentation/Scene/SignUp/Step2/IntroState.swift | 0 .../Presentation/Scene/SignUp/Step2/NickNameState.swift | 0 .../Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift | 0 .../Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift | 0 .../Presentation/Scene/SignUp/Step2/SignUpStep2View.swift | 0 .../Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift | 0 .../Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift | 0 .../Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift | 0 .../Scene/SignUp/Step3/View/TagSection/TagSection.swift | 0 .../Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift | 0 .../SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift | 0 .../Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift | 0 .../Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift | 0 .../Scene/SignUp/Step4/Main/SignUpStep4Controller.swift | 0 .../Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift | 0 .../Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift | 0 .../Scene/SignUp/Step4/Main/View/SignUpStep4View.swift | 0 .../Scene/SignUp/TermsDetail/TermsDetailController.swift | 0 .../Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift | 0 .../Presentation/Scene/Splash/SplashController.swift | 0 .../Presentation/Scene/Splash/View/SplashView.swift | 0 .../Presentation/Scene/TabbarController/TabbarController.swift | 0 .../Presentation/Utills/Common/ClusteringManager.swift | 0 .../Presentation/Utills/Common/ClusteringModels.swift | 0 .../Presentation/Utills/Common/DateTimePickerManager.swift | 0 .../Presentation/Utills/Common/ExtendedImage.swift | 0 .../Presentation/Utills/Common/FilterType.swift | 0 .../Utills/Common/LocationPermissionBottomSheet.swift | 0 .../Presentation/Utills/Common/MapFilterChips.swift | 0 .../Presentation/Utills/Common/MapUtilities.swift | 0 .../Presentation/Utills/Common/NMFMapViewDelegateProxy.swift | 0 .../Presentation/Utills/Common/RegionDefinitions.swift | 0 .../Presentation/Utills/Common/ViewportBounds.swift | 0 .../Presentation/Utills/Controllers/BaseTabmanController.swift | 0 .../Presentation/Utills/Controllers/BaseViewController.swift | 0 .../Presentation/Utills/Interfaces/InOutputable.swift | 0 .../Utills/Interfaces/Sectionable/SectionDecorationItem.swift | 0 .../Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift | 0 .../Presentation/Utills/Interfaces/Sectionable/Sectionable.swift | 0 .../Presentation/Utills/ToastMaker/BookMarkToastView.swift | 0 .../Presentation/Utills/ToastMaker/ToastMaker.swift | 0 .../Presentation/Utills/ToastMaker/ToastView.swift | 0 446 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/Date?+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/Reactive+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/String?+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIApplication+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UICollectionReusableView+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UICollectionViewCell+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIColor+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIFont+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIImage+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIImageView+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UILabel+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UINavigationController+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UITableViewCell+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UITextField+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Extension/UIView+.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/ImageLoader/DiskStorage.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/ImageLoader/ImageLoader.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/ImageLoader/MemoryStorage.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => CoreLayer/Infrastructure}/IndicatorMaker/IndicatorMaker.swift (100%) rename Poppool/Poppool/{ => CoreLayer}/Infrastructure/Logger/Logger.swift (100%) rename Poppool/Poppool/{Resource => CoreLayer/Infrastructure}/Secrets.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AdminAPI/AdminAPIEndpoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AdminAPI/ResponseDTO/AdminResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AuthAPI/AuthAPIEndPoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AuthAPI/ResponseDTO/LoginResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/CommentAPI/CommentAPIEndPoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/CommentAPI/RequestDTO/PostCommentRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/CommentAPI/RequestDTO/PutCommentRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/HomeAPI/HomeAPIEndpoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/MapAPI/FindDirectionEndPoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/MapAPI/MapAPIEndpoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/PopUpAPIEndPoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift (100%) rename Poppool/Poppool/{Infrastructure/PreSignedService => DataLayer/Network/API}/PreSignedAPI/PreSignedAPIEndPoint.swift (100%) rename Poppool/Poppool/{Infrastructure/PreSignedService => DataLayer/Network/API}/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift (100%) rename Poppool/Poppool/{Infrastructure/PreSignedService => DataLayer/Network/API}/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift (100%) rename Poppool/Poppool/{Infrastructure/PreSignedService => DataLayer/Network/API}/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/SignUpAPI/RequestDTO/SignUpRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/SignUpAPI/SignUpAPIEndpoint.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/SortedRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift (100%) rename Poppool/Poppool/{Data/Network => DataLayer/Network/API}/UserAPI/UserAPIEndPoint.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Common/NetworkError.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Common/Requestable.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Common/Responsable.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/EndPoint/Endpoint.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/EndPoint/MultipartEndPoint.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/EndPoint/RequestEndpoint.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Interceptor/FormDataInterceptor.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Interceptor/TokenInterceptor.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Provider/Provider.swift (100%) rename Poppool/Poppool/{Infrastructure/NetworkLayer => DataLayer/Network}/Provider/ProviderImpl.swift (100%) rename Poppool/Poppool/{Infrastructure => DataLayer/Network/Service}/AppleLoginService.swift (100%) rename Poppool/Poppool/{Infrastructure => DataLayer/Network/Service}/AuthServiceable.swift (100%) rename Poppool/Poppool/{Infrastructure => DataLayer/Network/Service}/KakaoLoginService.swift (100%) rename Poppool/Poppool/{Infrastructure => DataLayer/Network/Service}/KeyChainService.swift (100%) rename Poppool/Poppool/{Infrastructure/PreSignedService => DataLayer/Network/Service}/PreSignedService.swift (100%) rename Poppool/Poppool/{Infrastructure => DataLayer/Network/Service}/UserDefaultService.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/AdminRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/AuthAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/CommentAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/HomeAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/MapDirectionRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/MapRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/PopUpAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/SignUpRepositoryImpl.swift (100%) rename Poppool/Poppool/{Data => DataLayer}/RepositoryImpl/UserAPIRepositoryImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/AdminUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/MapUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/AuthResponse/LoginResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/BannerPopUpStore.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/Category.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/GetHomeInfoResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/MapPopUpStore.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/PopUpStoreResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/AdminRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/AuthAPIRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/CommentAPIRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/HomeAPIRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/MapDirectionRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/MapRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/PopUpAPIRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/SignUpRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/Repository/UserAPIRepository.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/AdminUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/AuthAPIUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/CommentAPIUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/HomeAPIUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/MapUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/PopUpAPIUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/SignUpAPIUseCase.swift (100%) rename Poppool/Poppool/{ => DomainLayer}/DomainInterface/UseCase/UserAPIUseCase.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPButton.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPCancelHeaderView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPLabel.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPPicker.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPProgressIndicator/PPProgressView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPReturnHeaderView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Components/PPSegmentedControl.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminStoreCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/AdminViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Admin/ImageCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentList/CommentListController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentList/CommentListReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentList/View/CommentListView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/DetailController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/DetailReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Detail/View/DetailView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/List/HomeListController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/List/HomeListReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/List/HomePopUpType.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/List/View/HomeCardGridSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/List/View/HomeListView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/HomeController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/HomeReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeHeaderView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/HomeView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/ImageDetail/ImageDetailController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/ImageDetail/ImageDetailReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/ImageDetail/ImageDetailView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/LastLoginView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Main/LoginController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Main/LoginReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Main/LoginView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Sub/SubLoginController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Sub/SubLoginReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Login/Sub/SubLoginView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterChip.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MapMarker.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MapReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MapSearchInput.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MapView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MapViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/MapView/MarkerTooltipView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/StoreListView/StoreListCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/StoreListView/StoreListReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/StoreListView/StoreListSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/StoreListView/StoreListView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Map/StoreListView/StoreListViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Block/BlockUserManageController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/FAQ/FAQController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/FAQ/FAQReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/FAQ/View/FAQView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/MyPageController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/MyPageReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Main/View/MyPageView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/AfterSearch/SearchResultController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/SearchController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/Main/SearchMainController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/Main/SearchMainReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/Main/SearchMainView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/SortedController/SearchSortedController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Search/SortedController/SearchSortedView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Main/SignUpMainController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step2/IntroState.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step2/NickNameState.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Splash/SplashController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/Splash/View/SplashView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Scene/TabbarController/TabbarController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/ClusteringManager.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/ClusteringModels.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/DateTimePickerManager.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/ExtendedImage.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/FilterType.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/LocationPermissionBottomSheet.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/MapFilterChips.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/MapUtilities.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/RegionDefinitions.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Common/ViewportBounds.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Controllers/BaseTabmanController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Controllers/BaseViewController.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Interfaces/InOutputable.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/ToastMaker/BookMarkToastView.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/ToastMaker/ToastMaker.swift (100%) rename Poppool/Poppool/{ => PresentationLayer}/Presentation/Utills/ToastMaker/ToastView.swift (100%) diff --git a/Poppool/Poppool/Infrastructure/Extension/Date?+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/Date?+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/Date?+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/Date?+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/Reactive+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/Reactive+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/Reactive+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/Reactive+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/String?+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/String?+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/String?+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/String?+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIApplication+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIApplication+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIApplication+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIApplication+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UICollectionReusableView+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionReusableView+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UICollectionReusableView+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionReusableView+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UICollectionViewCell+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionViewCell+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UICollectionViewCell+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionViewCell+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIColor+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIColor+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIColor+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIColor+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIFont+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIFont+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIImage+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImage+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIImage+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImage+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIImageView+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImageView+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIImageView+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImageView+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UILabel+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UILabel+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UILabel+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UILabel+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UINavigationController+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UINavigationController+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UINavigationController+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UINavigationController+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UITableViewCell+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITableViewCell+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UITableViewCell+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITableViewCell+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UITextField+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITextField+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UITextField+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITextField+.swift diff --git a/Poppool/Poppool/Infrastructure/Extension/UIView+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIView+.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Extension/UIView+.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIView+.swift diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift b/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/DiskStorage.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/ImageLoader/DiskStorage.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/DiskStorage.swift diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/ImageLoader.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/ImageLoader/ImageLoader.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/ImageLoader.swift diff --git a/Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift b/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/MemoryStorage.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/ImageLoader/MemoryStorage.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/MemoryStorage.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift b/Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/IndicatorMaker/IndicatorMaker.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift diff --git a/Poppool/Poppool/Infrastructure/Logger/Logger.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Logger/Logger.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/Logger/Logger.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Logger/Logger.swift diff --git a/Poppool/Poppool/Resource/Secrets.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Secrets.swift similarity index 100% rename from Poppool/Poppool/Resource/Secrets.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Secrets.swift diff --git a/Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/AdminAPI/AdminAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AdminAPI/AdminAPIEndpoint.swift rename to Poppool/Poppool/DataLayer/Network/API/AdminAPI/AdminAPIEndpoint.swift diff --git a/Poppool/Poppool/Data/Network/AdminAPI/ResponseDTO/AdminResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AdminAPI/ResponseDTO/AdminResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/AuthAPI/AuthAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AuthAPI/AuthAPIEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/AuthAPI/AuthAPIEndPoint.swift diff --git a/Poppool/Poppool/Data/Network/AuthAPI/ResponseDTO/LoginResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AuthAPI/ResponseDTO/LoginResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/CommentAPI/CommentAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/CommentAPI/CommentAPIEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/CommentAPI/CommentAPIEndPoint.swift diff --git a/Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/PostCommentRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/PostCommentRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/PutCommentRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/CommentAPI/RequestDTO/PutCommentRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/HomeAPI/HomeAPIEndpoint.swift rename to Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift diff --git a/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift diff --git a/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/FindDirectionEndPoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/MapAPI/FindDirectionEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/MapAPI/FindDirectionEndPoint.swift diff --git a/Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/MapAPI/MapAPIEndpoint.swift rename to Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift diff --git a/Poppool/Poppool/Data/Network/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/PopUpAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/PopUpAPIEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/PopUpAPIEndPoint.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/PreSignedAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/PreSignedAPIEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/SignUpRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/RequestDTO/SignUpRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/SignUpAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SignUpAPI/SignUpAPIEndpoint.swift rename to Poppool/Poppool/DataLayer/Network/API/SignUpAPI/SignUpAPIEndpoint.swift diff --git a/Poppool/Poppool/Data/Network/SortedRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/SortedRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift diff --git a/Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/Data/Network/UserAPI/UserAPIEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Common/NetworkError.swift b/Poppool/Poppool/DataLayer/Network/Common/NetworkError.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Common/NetworkError.swift rename to Poppool/Poppool/DataLayer/Network/Common/NetworkError.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift b/Poppool/Poppool/DataLayer/Network/Common/Requestable.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Common/Requestable.swift rename to Poppool/Poppool/DataLayer/Network/Common/Requestable.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Common/Responsable.swift b/Poppool/Poppool/DataLayer/Network/Common/Responsable.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Common/Responsable.swift rename to Poppool/Poppool/DataLayer/Network/Common/Responsable.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift b/Poppool/Poppool/DataLayer/Network/EndPoint/Endpoint.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/Endpoint.swift rename to Poppool/Poppool/DataLayer/Network/EndPoint/Endpoint.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift b/Poppool/Poppool/DataLayer/Network/EndPoint/MultipartEndPoint.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/MultipartEndPoint.swift rename to Poppool/Poppool/DataLayer/Network/EndPoint/MultipartEndPoint.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/RequestEndpoint.swift b/Poppool/Poppool/DataLayer/Network/EndPoint/RequestEndpoint.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/EndPoint/RequestEndpoint.swift rename to Poppool/Poppool/DataLayer/Network/EndPoint/RequestEndpoint.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift b/Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/FormDataInterceptor.swift rename to Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift b/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Interceptor/TokenInterceptor.swift rename to Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift b/Poppool/Poppool/DataLayer/Network/Provider/Provider.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Provider/Provider.swift rename to Poppool/Poppool/DataLayer/Network/Provider/Provider.swift diff --git a/Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift b/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/NetworkLayer/Provider/ProviderImpl.swift rename to Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift diff --git a/Poppool/Poppool/Infrastructure/AppleLoginService.swift b/Poppool/Poppool/DataLayer/Network/Service/AppleLoginService.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/AppleLoginService.swift rename to Poppool/Poppool/DataLayer/Network/Service/AppleLoginService.swift diff --git a/Poppool/Poppool/Infrastructure/AuthServiceable.swift b/Poppool/Poppool/DataLayer/Network/Service/AuthServiceable.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/AuthServiceable.swift rename to Poppool/Poppool/DataLayer/Network/Service/AuthServiceable.swift diff --git a/Poppool/Poppool/Infrastructure/KakaoLoginService.swift b/Poppool/Poppool/DataLayer/Network/Service/KakaoLoginService.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/KakaoLoginService.swift rename to Poppool/Poppool/DataLayer/Network/Service/KakaoLoginService.swift diff --git a/Poppool/Poppool/Infrastructure/KeyChainService.swift b/Poppool/Poppool/DataLayer/Network/Service/KeyChainService.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/KeyChainService.swift rename to Poppool/Poppool/DataLayer/Network/Service/KeyChainService.swift diff --git a/Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift b/Poppool/Poppool/DataLayer/Network/Service/PreSignedService.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/PreSignedService/PreSignedService.swift rename to Poppool/Poppool/DataLayer/Network/Service/PreSignedService.swift diff --git a/Poppool/Poppool/Infrastructure/UserDefaultService.swift b/Poppool/Poppool/DataLayer/Network/Service/UserDefaultService.swift similarity index 100% rename from Poppool/Poppool/Infrastructure/UserDefaultService.swift rename to Poppool/Poppool/DataLayer/Network/Service/UserDefaultService.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/AdminRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/MapRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/SignUpRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift diff --git a/Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/Data/RepositoryImpl/UserAPIRepositoryImpl.swift rename to Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/AdminUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/MapUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift rename to Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/AuthResponse/LoginResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/LoginResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/AuthResponse/LoginResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/LoginResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/BannerPopUpStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/BannerPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/BannerPopUpStore.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/BannerPopUpStore.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/Category.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/Category.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/Category.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/Category.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/GetHomeInfoResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/GetHomeInfoResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/GetHomeInfoResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/GetHomeInfoResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/MapPopUpStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/MapPopUpStore.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapPopUpStore.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/PopUpStoreResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpStoreResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/PopUpStoreResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpStoreResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/AdminRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/AuthAPIRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/CommentAPIRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/HomeAPIRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapDirectionRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/MapDirectionRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapDirectionRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/MapRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/MapRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/PopUpAPIRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/SignUpRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift diff --git a/Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/Repository/UserAPIRepository.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/AdminUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AuthAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/AuthAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AuthAPIUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/CommentAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/HomeAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/HomeAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/HomeAPIUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/MapUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/PopUpAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/PopUpAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/PopUpAPIUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/SignUpAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift diff --git a/Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/UserAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainInterface/UseCase/UserAPIUseCase.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/UseCase/UserAPIUseCase.swift diff --git a/Poppool/Poppool/Presentation/Components/PPButton.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPButton.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPButton.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPButton.swift diff --git a/Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPCancelHeaderView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPCancelHeaderView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPCancelHeaderView.swift diff --git a/Poppool/Poppool/Presentation/Components/PPLabel.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPLabel.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPLabel.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPLabel.swift diff --git a/Poppool/Poppool/Presentation/Components/PPPicker.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPPicker.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPPicker.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPPicker.swift diff --git a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift diff --git a/Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPProgressIndicator/PPProgressView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressView.swift diff --git a/Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPReturnHeaderView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPReturnHeaderView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPReturnHeaderView.swift diff --git a/Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift b/Poppool/Poppool/PresentationLayer/Presentation/Components/PPSegmentedControl.swift similarity index 100% rename from Poppool/Poppool/Presentation/Components/PPSegmentedControl.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Components/PPSegmentedControl.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminStoreCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/AdminViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Admin/ImageCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/ImageCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Admin/ImageCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/ImageCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentList/CommentListReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentList/View/CommentListView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/DetailController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/DetailReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Detail/View/DetailView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/List/HomeListController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/List/HomeListReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomePopUpType.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/List/HomePopUpType.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomePopUpType.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeCardGridSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/List/View/HomeCardGridSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeCardGridSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeListView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/List/View/HomeListView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeListView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/HomeController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/HomeReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeHeaderView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeHeaderView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeHeaderView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/HomeView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailController.swift diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/ImageDetail/ImageDetailView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/LastLoginView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/LastLoginView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/LastLoginView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Main/LoginController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Main/LoginReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Main/LoginView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Login/Sub/SubLoginView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterChip.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChip.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterChip.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChip.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapMarker.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapMarker.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MapMarker.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapMarker.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MapReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapSearchInput.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MapSearchInput.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapSearchInput.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MapView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MapViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MarkerTooltipView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/MapView/MarkerTooltipView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MarkerTooltipView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Map/StoreListView/StoreListViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/FAQ/FAQReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/FAQ/View/FAQView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/MyPageReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Main/View/MyPageView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift diff --git a/Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/Main/SearchMainView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Search/SortedController/SearchSortedView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedView.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/IntroState.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step2/IntroState.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/IntroState.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/NickNameState.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step2/NickNameState.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/NickNameState.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift diff --git a/Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift diff --git a/Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Splash/SplashController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift diff --git a/Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/View/SplashView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/Splash/View/SplashView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/View/SplashView.swift diff --git a/Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Scene/TabbarController/TabbarController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/ClusteringManager.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringManager.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/ClusteringManager.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringManager.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/ClusteringModels.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringModels.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/ClusteringModels.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringModels.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/DateTimePickerManager.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/DateTimePickerManager.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/DateTimePickerManager.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/DateTimePickerManager.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/ExtendedImage.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ExtendedImage.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/ExtendedImage.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ExtendedImage.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/FilterType.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/FilterType.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/LocationPermissionBottomSheet.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/LocationPermissionBottomSheet.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/LocationPermissionBottomSheet.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/LocationPermissionBottomSheet.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapFilterChips.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/MapFilterChips.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapFilterChips.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/MapUtilities.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapUtilities.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/MapUtilities.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapUtilities.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/RegionDefinitions.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/RegionDefinitions.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/RegionDefinitions.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/RegionDefinitions.swift diff --git a/Poppool/Poppool/Presentation/Utills/Common/ViewportBounds.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Common/ViewportBounds.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift diff --git a/Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseTabmanController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Controllers/BaseTabmanController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseTabmanController.swift diff --git a/Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseViewController.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Controllers/BaseViewController.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseViewController.swift diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/InOutputable.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/InOutputable.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Interfaces/InOutputable.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/InOutputable.swift diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift diff --git a/Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/BookMarkToastView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/ToastMaker/BookMarkToastView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/BookMarkToastView.swift diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/ToastMaker/ToastMaker.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift diff --git a/Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastView.swift similarity index 100% rename from Poppool/Poppool/Presentation/Utills/ToastMaker/ToastView.swift rename to Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastView.swift From e4230f23f4ee5b77767204b6fb90d39ab05ad3b7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 10:12:24 +0900 Subject: [PATCH 115/393] =?UTF-8?q?refactor/#112:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=ED=8C=8C=EC=9D=BC=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20Layer=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=ED=8F=B4?= =?UTF-8?q?=EB=8D=94=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Extension/UIFont+.swift | 1 + .../IndicatorMaker/IndicatorMaker.swift | 56 ------------------- .../Service/AppleLoginService.swift | 0 .../Service/AuthServiceable.swift | 0 .../Service/KakaoLoginService.swift | 0 .../Service/KeyChainService.swift | 0 .../Service/PreSignedService.swift | 0 .../Service/UserDefaultService.swift | 0 .../GetCategoryListResponseDTO.swift | 4 +- .../Network/API/SortedRequestDTO.swift | 2 + .../Interceptor/FormDataInterceptor.swift | 30 ---------- .../RepositoryImpl/MapRepositoryImpl.swift | 2 +- .../PopUpAPIRepositoryImpl.swift | 2 +- .../RepositoryImpl/SignUpRepositoryImpl.swift | 2 +- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 2 +- .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 2 +- .../CategoryResponse.swift} | 2 +- .../{ => HomeResponse}/BannerPopUpStore.swift | 0 .../GetHomeInfoResponse.swift | 0 .../{ => MapResponse}/MapPopUpStore.swift | 0 .../PopUpStoreResponse.swift | 0 .../UserResponse/GetMyProfileResponse.swift | 2 +- .../Repository/MapRepository.swift | 4 +- .../Repository/SignUpRepository.swift | 2 +- .../DomainInterface/UseCase/MapUseCase.swift | 2 +- .../UseCase/SignUpAPIUseCase.swift | 2 +- .../Utills/Common/FilterType.swift | 1 - .../Utills/Common/ViewportBounds.swift | 6 -- 28 files changed, 17 insertions(+), 107 deletions(-) delete mode 100644 Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/AppleLoginService.swift (100%) rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/AuthServiceable.swift (100%) rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/KakaoLoginService.swift (100%) rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/KeyChainService.swift (100%) rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/PreSignedService.swift (100%) rename Poppool/Poppool/{DataLayer/Network => CoreLayer/Infrastructure}/Service/UserDefaultService.swift (100%) delete mode 100644 Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/{Category.swift => AuthResponse/CategoryResponse.swift} (85%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/{ => HomeResponse}/BannerPopUpStore.swift (100%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/{ => HomeResponse}/GetHomeInfoResponse.swift (100%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/{ => MapResponse}/MapPopUpStore.swift (100%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/{ => PopUpResponse}/PopUpStoreResponse.swift (100%) delete mode 100644 Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift index 75bdb25b..f8eaab2d 100644 --- a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift +++ b/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift @@ -1,4 +1,5 @@ import Foundation + import UIKit extension UIFont { diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift b/Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift deleted file mode 100644 index 997125a1..00000000 --- a/Poppool/Poppool/CoreLayer/Infrastructure/IndicatorMaker/IndicatorMaker.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// IndicatorMaker.swift -// MomsVillage -// -// Created by SeoJunYoung on 10/14/24. -// - -import UIKit - -import Lottie -import SnapKit - -struct IndicatorMaker { - - static let indicatorImageView: LottieAnimationView = { - let view = LottieAnimationView(name: "indicator") - view.loopMode = .loop - return view - }() - - static let overlayView: UIView = { - let view = UIView() - view.backgroundColor = .black.withAlphaComponent(0.1) - return view - }() - - static func showIndicator() { - DispatchQueue.main.async { - guard let topVC = UIApplication.topViewController() else { - print("Error: Cannot find top view controller") - return - } - - topVC.view.addSubview(overlayView) - overlayView.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - overlayView.addSubview(indicatorImageView) - indicatorImageView.snp.makeConstraints { make in - make.size.equalTo(200) - make.center.equalToSuperview() - } - indicatorImageView.play() - topVC.view.isUserInteractionEnabled = false - } - } - - static func hideIndicator() { - DispatchQueue.main.async { - indicatorImageView.stop() - overlayView.removeFromSuperview() - indicatorImageView.removeFromSuperview() - UIApplication.topViewController()?.view.isUserInteractionEnabled = true - } - } -} diff --git a/Poppool/Poppool/DataLayer/Network/Service/AppleLoginService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/AppleLoginService.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/AppleLoginService.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/AppleLoginService.swift diff --git a/Poppool/Poppool/DataLayer/Network/Service/AuthServiceable.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/AuthServiceable.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/AuthServiceable.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/AuthServiceable.swift diff --git a/Poppool/Poppool/DataLayer/Network/Service/KakaoLoginService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/KakaoLoginService.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/KakaoLoginService.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/KakaoLoginService.swift diff --git a/Poppool/Poppool/DataLayer/Network/Service/KeyChainService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/KeyChainService.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/KeyChainService.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/KeyChainService.swift diff --git a/Poppool/Poppool/DataLayer/Network/Service/PreSignedService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/PreSignedService.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift diff --git a/Poppool/Poppool/DataLayer/Network/Service/UserDefaultService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/UserDefaultService.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Service/UserDefaultService.swift rename to Poppool/Poppool/CoreLayer/Infrastructure/Service/UserDefaultService.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift index b437e186..bf58db7d 100644 --- a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift +++ b/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift @@ -19,7 +19,7 @@ struct CategoryResponseDTO: Codable { } extension CategoryResponseDTO { - func toDomain() -> Category { - return Category(categoryId: categoryId, category: categoryName) + func toDomain() -> CategoryResponse { + return CategoryResponse(categoryId: categoryId, category: categoryName) } } diff --git a/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift index 70752653..14ecf6d6 100644 --- a/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift +++ b/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift @@ -5,6 +5,8 @@ // Created by SeoJunYoung on 11/28/24. // +// TODO: SortedRequestDTO를 HOME, User로 나눠서 폴더에 넣어주기 + import Foundation struct SortedRequestDTO: Encodable { diff --git a/Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift b/Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift deleted file mode 100644 index f057df7d..00000000 --- a/Poppool/Poppool/DataLayer/Network/Interceptor/FormDataInterceptor.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// FormDataInterceptor.swift -// MomsVillage -// -// Created by SeoJunYoung on 10/25/24. -// - -import Alamofire -import Foundation -import RxSwift - -final class FormDataInterceptor: RequestInterceptor { - - private var disposeBag = DisposeBag() - - func adapt( - _ urlRequest: URLRequest, - for session: Session, - completion: @escaping (Result) -> Void) { - } - - func retry( - _ request: Request, - for session: Session, - dueTo error: any Error, - completion: @escaping (RetryResult) -> Void - ) { - Logger.log(message: "FormDataInterceptor Retry Start", category: .network) - } -} diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift index d9edb5d5..c78a6af6 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift @@ -45,7 +45,7 @@ final class MapRepositoryImpl: MapRepository { .map { $0.popUpStoreList } } - func fetchCategories() -> Observable<[Category]> { + func fetchCategories() -> Observable<[CategoryResponse]> { Logger.log(message: "카테고리 매핑 요청을 시작합니다.", category: .network) return provider.requestData( diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift index 58f8fb5a..b20a108a 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift final class PopUpAPIRepositoryImpl: PopUpAPIRepository { - + private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift index 12d5da59..d6b071d0 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift @@ -15,7 +15,7 @@ final class SignUpRepositoryImpl: SignUpRepository { return provider.requestData(with: endPoint, interceptor: TokenInterceptor()) } - func fetchCategoryList() -> Observable<[Category]> { + func fetchCategoryList() -> Observable<[CategoryResponse]> { let endPoint = SignUpAPIEndpoint.signUp_getCategoryList() return provider.requestData(with: endPoint, interceptor: TokenInterceptor()).map { responseDTO in return responseDTO.categoryResponseList.map({ $0.toDomain() }) diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift index 8ee63c44..f06968c0 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -10,7 +10,7 @@ final class MapUseCaseImpl: MapUseCase { self.repository = repository } - func fetchCategories() -> Observable<[Category]> { + func fetchCategories() -> Observable<[CategoryResponse]> { return repository.fetchCategories() } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index d739f9ec..9f91f723 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -33,7 +33,7 @@ final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { return repository.checkNickName(nickName: nickName) } - func fetchCategoryList() -> Observable<[Category]> { + func fetchCategoryList() -> Observable<[CategoryResponse]> { return repository.fetchCategoryList() } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/Category.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/CategoryResponse.swift similarity index 85% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/Category.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index 9f7da1cd..20972e3c 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/Category.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -7,7 +7,7 @@ import Foundation -struct Category { +struct CategoryResponse { let categoryId: Int64 let category: String } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/BannerPopUpStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/BannerPopUpStore.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/GetHomeInfoResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/GetHomeInfoResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapPopUpStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapPopUpStore.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpStoreResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpStoreResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift index 081da110..6c71ce4b 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift @@ -15,5 +15,5 @@ struct GetMyProfileResponse { var intro: String? var gender: String? var age: Int32 - var interestCategoryList: [Category] + var interestCategoryList: [CategoryResponse] } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift index b50ef36b..6f613828 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift @@ -24,5 +24,5 @@ protocol MapRepository { categories: [Int64] ) -> Observable<[MapPopUpStoreDTO]> - func fetchCategories() -> Observable<[Category]> -} \ No newline at end of file + func fetchCategories() -> Observable<[CategoryResponse]> +} diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift index d403fe94..6e7ef1b0 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift @@ -4,7 +4,7 @@ import RxSwift protocol SignUpRepository { func checkNickName(nickName: String) -> Observable - func fetchCategoryList() -> Observable<[Category]> + func fetchCategoryList() -> Observable<[CategoryResponse]> func trySignUp( nickName: String, gender: String, diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift index 954260ad..9eb687da 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift @@ -11,7 +11,7 @@ import Foundation import RxSwift protocol MapUseCase { - func fetchCategories() -> Observable<[Category]> + func fetchCategories() -> Observable<[CategoryResponse]> func fetchStoresInBounds( northEastLat: Double, northEastLon: Double, diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift index 416194f9..e0dfeb36 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift @@ -15,5 +15,5 @@ protocol SignUpAPIUseCase { func checkNickName(nickName: String) -> Observable - func fetchCategoryList() -> Observable<[Category]> + func fetchCategoryList() -> Observable<[CategoryResponse]> } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift index b19e7c64..c181a940 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift @@ -1,5 +1,4 @@ import Foundation -import UIKit /// 맵과 리스트에서 공통으로 사용하는 필터 타입 enum FilterType { diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift deleted file mode 100644 index 9c1fc360..00000000 --- a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ViewportBounds.swift +++ /dev/null @@ -1,6 +0,0 @@ -import CoreLocation - -struct ViewportBounds { - let northEast: CLLocationCoordinate2D - let southWest: CLLocationCoordinate2D -} From f76fb706556892a4bd4117d412aba8c48e5057ea Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 17:31:30 +0900 Subject: [PATCH 116/393] =?UTF-8?q?feat/#113:=20DIContainer=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 --- .../DIContainer/DIContainer.swift | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift new file mode 100644 index 00000000..7db26b38 --- /dev/null +++ b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift @@ -0,0 +1,68 @@ +import Foundation + +/// 의존성 주입 컨테이너 +/// +/// 이 컨테이너는 타입 기반으로 의존성을 등록하고 어디서든 안전하게 꺼내 쓸 수 있도록 도와줍니다. +/// +/// 앱 시작 시점에 필요한 구현체를 `register(_:_: )`를 통해 등록하고, +/// 이후에는 `resolve(_:)` 메서드를 통해 원하는 타입의 인스턴스를 꺼낼 수 있습니다. +/// +/// ## 등록 예시 +/// ```swift +/// DIContainer.register(SampleProtocol.self) { +/// SampleImpl() +/// } +/// ``` +/// +/// ## 사용 예시 +/// ```swift +/// // DIContainer의 resolve 메서드를 사용하는 방식 +/// let sample: SampleProtocol = DIContainer.resolve(SampleProtocol.self) +/// ``` +public final class DIContainer { + private static let container = DIContainer() + + private var registrations: [ObjectIdentifier: () -> Any] = [:] + + private let resolveQueue = DispatchQueue(label: "resolveQueue") + + private init() {} + + /// 의존성을 등록합니다. + /// - Parameters: + /// - type: 등록할 프로토콜 또는 클래스 타입 + /// - implementation: 해당 타입에 대응되는 구현체를 생성하는 클로저 + public static func register( + _ type: T.Type, + _ implementation: @escaping () -> T + ) { + container.register(type, implementation) + } + + /// 의존성을 꺼내옵니다. + /// - Parameter type: 요청할 타입 + /// - Returns: 등록된 타입의 인스턴스 + public static func resolve(_ type: T.Type) -> T { + return container.resolve(type) + } + + private func register( + _ type: T.Type, + _ implementation: @escaping () -> T + ) { + let key = ObjectIdentifier(type) + registrations[key] = { implementation() } + } + + private func resolve(_ type: T.Type) -> T { + let key = ObjectIdentifier(type) + + return resolveQueue.sync { + guard let registration = registrations[key], + let instance = registration() as? T + else { fatalError("\(type) does not registered") } + + return instance + } + } +} From d62546d0b68722e9ee5807addcb1052b5492188d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 17:32:03 +0900 Subject: [PATCH 117/393] =?UTF-8?q?feat/#113:=20=EA=B0=84=EB=8B=A8?= =?UTF-8?q?=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A3=BC=EC=9E=85?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20PropertyWrapper=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 --- .../DIContainer/DependencyWrapper.swift | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift new file mode 100644 index 00000000..cbfee35a --- /dev/null +++ b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift @@ -0,0 +1,28 @@ +import Foundation + +/// 의존성 자동 주입을 위한 프로퍼티 래퍼 +/// +/// 사용하는 곳에서 `@Dependency`만 붙이면 등록된 구현체가 자동으로 주입됩니다. +/// +/// Swift의 프로퍼티 래퍼 특성상 `var`로 선언해야 하지만, 실제 인스턴스는 외부에서 변경할 수 없도록 `private(set)`으로 보호되어 불변성을 유지합니다. +/// +/// 사용 예시: +/// ```swift +/// class MyViewModel { +/// @Dependency var sample: SampleProtocol +/// +/// func run() { +/// sample.doSomething() +/// } +/// } +/// ``` +@propertyWrapper +public final class Dependency { + /// DIContainer에서 꺼내온 실제 인스턴스 + public private(set) var wrappedValue: T + + /// DIContainer로부터 자동으로 인스턴스를 꺼내와 초기화합니다. + public init() { + self.wrappedValue = DIContainer.resolve(T.self) + } +} From b99d07c69799928e5e9fb9d9fcc1c55b9f06774c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 18:25:16 +0900 Subject: [PATCH 118/393] =?UTF-8?q?refactor/#113:=20KeyChainService=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EA=B4=80=EB=A6=AC=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=B4=20DIContainer=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 11 +++++++++++ .../Network/Interceptor/TokenInterceptor.swift | 2 +- .../DataLayer/Network/Provider/ProviderImpl.swift | 2 +- .../Presentation/Scene/Login/Main/LoginReactor.swift | 2 +- .../Scene/Login/Sub/SubLoginReactor.swift | 2 +- .../Scene/MyPage/Main/MyPageReactor.swift | 6 +++--- .../SelectedReason/WithdrawlReasonReactor.swift | 2 +- .../Presentation/Scene/Splash/SplashController.swift | 2 +- 8 files changed, 20 insertions(+), 9 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index bb8a380f..489957ce 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -13,6 +13,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate { let locationManager = CLLocationManager() locationManager.requestWhenInUseAuthorization() + self.registerDependencies() + return true } @@ -21,3 +23,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } } + +// MARK: - Dependency +extension AppDelegate { + /// 의존성 등록을 위한 메서드 + private func registerDependencies() { + // MARK: Register Service + DIContainer.register(KeyChainService.self) { return KeyChainService() } + } +} diff --git a/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift b/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift index 9ad8e40f..c6cd1ec3 100644 --- a/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift +++ b/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift @@ -16,7 +16,7 @@ final class TokenInterceptor: RequestInterceptor { for session: Session, completion: @escaping (Result) -> Void) { Logger.log(message: "TokenInterceptor Adapt Token", category: .network) - let keyChainService = KeyChainService() + @Dependency var keyChainService: KeyChainService var urlRequest = urlRequest let accessTokenResult = keyChainService.fetchToken(type: .accessToken) switch accessTokenResult { diff --git a/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift b/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift index a7fa9517..0a14662f 100644 --- a/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift +++ b/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift @@ -118,7 +118,7 @@ final class ProviderImpl: Provider { accessToken = accessToken.replacingOccurrences(of: "Bearer ", with: "") refreshToken = refreshToken.replacingOccurrences(of: "Bearer ", with: "") - let keyChainService = KeyChainService() + @Dependency var keyChainService: KeyChainService keyChainService.saveToken(type: .accessToken, value: accessToken) keyChainService.saveToken(type: .refreshToken, value: refreshToken) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift index 1d3425a0..d7963143 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift @@ -41,7 +41,7 @@ final class LoginReactor: Reactor { private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) - private let keyChainService = KeyChainService() + @Dependency private var keyChainService: KeyChainService let userDefaultService = UserDefaultService() // MARK: - init diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 24b6d961..b51ec276 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -41,7 +41,7 @@ final class SubLoginReactor: Reactor { private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) - private let keyChainService = KeyChainService() + @Dependency private var keyChainService: KeyChainService let userDefaultService = UserDefaultService() // MARK: - init diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 3777f7e1..927375f9 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -184,9 +184,9 @@ final class MyPageReactor: Reactor { controller.navigationController?.pushViewController(nextController, animated: true) case .logout: - let service = KeyChainService() - _ = service.deleteToken(type: .accessToken) - _ = service.deleteToken(type: .refreshToken) + @Dependency var keyChainService: KeyChainService + _ = 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/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift index ca08cbf5..4e0a67de 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -58,7 +58,7 @@ final class WithdrawlReasonReactor: Reactor { private var reasonSection = WithdrawlCheckSection(inputDataList: []) private var spacing156Section = SpacingSection(inputDataList: [.init(spacing: 156)]) private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) - private let keyChainService = KeyChainService() + @Dependency private var keyChainService: KeyChainService private let userDefaultService = UserDefaultService() // MARK: - init init() { diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift index c4fb9af4..24ac8954 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift @@ -19,7 +19,7 @@ final class SplashController: BaseViewController { private var mainView = SplashView() private let authAPIUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) - private let keyChainService = KeyChainService() + @Dependency private var keyChainService: KeyChainService private var rootViewController: UIViewController? } From aec9f97040602b2f137a2ff73477580836d2b1ce Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 18:29:15 +0900 Subject: [PATCH 119/393] =?UTF-8?q?refactor/#113:=20Provider=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 9 +++++++++ .../Infrastructure/Service/PreSignedService.swift | 4 +--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 489957ce..0e2cd87d 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -29,6 +29,15 @@ extension AppDelegate { /// 의존성 등록을 위한 메서드 private func registerDependencies() { // MARK: Register Service + DIContainer.register(Provider.self) { return ProviderImpl() } DIContainer.register(KeyChainService.self) { return KeyChainService() } + + // MARK: Resolve service + + // MARK: Register repository + + // MARK: Resolve repository + + // MARK: Register UseCase } } diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift b/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift index 0aaa3410..ad1ef9a2 100644 --- a/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift +++ b/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift @@ -25,7 +25,7 @@ class PreSignedService { let tokenInterceptor = TokenInterceptor() - let provider = ProviderImpl() + @Dependency private var provider: Provider let disposeBag = DisposeBag() @@ -204,7 +204,6 @@ private extension PreSignedService { func getUploadLinks(request: PresignedURLRequestDTO) -> Observable { Logger.log(message: "Presigned URL 생성 요청 데이터: \(request)", category: .debug) - let provider = ProviderImpl() let endPoint = PreSignedAPIEndPoint.presigned_upload(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) .do(onNext: { response in @@ -215,7 +214,6 @@ private extension PreSignedService { } func getDownloadLinks(request: PresignedURLRequestDTO) -> Observable { - let provider = ProviderImpl() let endPoint = PreSignedAPIEndPoint.presigned_download(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) } From 54de56d4a8f2d14df2d98ac3174ebd4fdd4d20f5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 20:58:22 +0900 Subject: [PATCH 120/393] =?UTF-8?q?refactor/#113:=20MapUseCase=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=8C=80=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 4 ++++ .../Map/FindMap/MapGuideView/MapGuideViewController.swift | 6 +++--- .../Scene/TabbarController/TabbarController.swift | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 0e2cd87d..d359a281 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -33,11 +33,15 @@ extension AppDelegate { DIContainer.register(KeyChainService.self) { return KeyChainService() } // MARK: Resolve service + @Dependency var provider: Provider // MARK: Register repository + DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } // MARK: Resolve repository + @Dependency var mapRepository: MapRepository // MARK: Register UseCase + DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 6da635ef..3cec2853 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -172,10 +172,10 @@ final class MapGuideViewController: UIViewController, View { guard let strongSelf = self else { return } let providerInstance = ProviderImpl() - let repositoryInstance = MapRepositoryImpl(provider: providerInstance) - let useCaseInstance = MapUseCaseImpl(repository: repositoryInstance) + @Dependency var mapUseCase: MapUseCase + let directionRepositoryInstance = MapDirectionRepositoryImpl(provider: providerInstance) - let mapReactorInstance = MapReactor(useCase: useCaseInstance, directionRepository: directionRepositoryInstance) + let mapReactorInstance = MapReactor(useCase: mapUseCase, directionRepository: directionRepositoryInstance) if let selectedStore = strongSelf.currentCarouselStoreList.first { mapReactorInstance.action.onNext(.didSelectItem(selectedStore)) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index bd0dc0a1..18b8735e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -196,7 +196,7 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let provider = ProviderImpl() let mapController = MapViewController() - let mapUseCase = MapUseCaseImpl(repository: MapRepositoryImpl(provider: provider)) + @Dependency var mapUseCase: MapUseCase let directionRepository = MapDirectionRepositoryImpl(provider: provider) mapController.reactor = MapReactor(useCase: mapUseCase, directionRepository: directionRepository) From c25bc34e2e6544bb2e4fde60041b24a0d186dfe1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 21:36:49 +0900 Subject: [PATCH 121/393] =?UTF-8?q?style/#113:=20DIContainer=20lint=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/DIContainer/DIContainer.swift | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift index 7db26b38..0ad4234e 100644 --- a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift +++ b/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift @@ -21,13 +21,13 @@ import Foundation /// ``` public final class DIContainer { private static let container = DIContainer() - + private var registrations: [ObjectIdentifier: () -> Any] = [:] - + private let resolveQueue = DispatchQueue(label: "resolveQueue") - + private init() {} - + /// 의존성을 등록합니다. /// - Parameters: /// - type: 등록할 프로토콜 또는 클래스 타입 @@ -38,14 +38,14 @@ public final class DIContainer { ) { container.register(type, implementation) } - + /// 의존성을 꺼내옵니다. /// - Parameter type: 요청할 타입 /// - Returns: 등록된 타입의 인스턴스 public static func resolve(_ type: T.Type) -> T { return container.resolve(type) } - + private func register( _ type: T.Type, _ implementation: @escaping () -> T @@ -53,7 +53,7 @@ public final class DIContainer { let key = ObjectIdentifier(type) registrations[key] = { implementation() } } - + private func resolve(_ type: T.Type) -> T { let key = ObjectIdentifier(type) @@ -61,7 +61,7 @@ public final class DIContainer { guard let registration = registrations[key], let instance = registration() as? T else { fatalError("\(type) does not registered") } - + return instance } } From de9e70b4e07b2f63e835771c7f6f82e3e0b96e06 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 21:52:52 +0900 Subject: [PATCH 122/393] =?UTF-8?q?refactor/#113:=20AdminUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../Presentation/Scene/Admin/AdminReactor.swift | 10 +++++----- .../AdminRegister/PopUpStoreRegisterReactor.swift | 6 +++++- .../PopUpStoreRegisterViewController.swift | 6 +++++- .../Presentation/Scene/Admin/AdminViewController.swift | 5 ++++- .../Presentation/Scene/MyPage/Main/MyPageReactor.swift | 9 +++++---- 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index d359a281..e0b3e405 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -37,11 +37,14 @@ extension AppDelegate { // MARK: Register repository DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } + DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository + @Dependency var adminRepository: AdminRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } + DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift index e258e6df..c8984af1 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift @@ -31,10 +31,10 @@ final class AdminReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let useCase: AdminUseCase + private let adminUseCase: AdminUseCase - init(useCase: AdminUseCase) { - self.useCase = useCase + init(adminUseCase: AdminUseCase) { + self.adminUseCase = adminUseCase self.initialState = State() } @@ -43,7 +43,7 @@ final class AdminReactor: Reactor { case .viewDidLoad, .reloadData: return .concat([ .just(.setIsLoading(true)), - useCase.fetchStoreList(query: nil, page: 0, size: 100) + adminUseCase.fetchStoreList(query: nil, page: 0, size: 100) .map { .setStores($0.popUpStoreList ?? []) }, // ✅ nil 방지 .just(.setIsLoading(false)) ]) @@ -51,7 +51,7 @@ final class AdminReactor: Reactor { case let .updateSearchQuery(query): return .concat([ .just(.setIsLoading(true)), - useCase.fetchStoreList(query: query, page: 0, size: 100) + adminUseCase.fetchStoreList(query: query, page: 0, size: 100) .do(onNext: { response in Logger.log(message: "조회 성공 - 응답 데이터: \(response)", category: .info) }, onError: { error in diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index e2c6cb67..3321184e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -16,7 +16,11 @@ final class PopUpStoreRegisterReactor: Reactor { private var disposeBag = DisposeBag() - init(adminUseCase: AdminUseCase, presignedService: PreSignedService, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { + init( + adminUseCase: AdminUseCase, + presignedService: PreSignedService, + editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil + ) { self.adminUseCase = adminUseCase self.presignedService = presignedService self.isEditMode = editingStore != nil diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 15e6855b..4f7c9c83 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -19,7 +19,11 @@ final class PopUpStoreRegisterViewController: BaseViewController { private var mainView: PopUpRegisterView // MARK: - Initializer - init(nickname: String, adminUseCase: AdminUseCase, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil) { + init( + nickname: String, + adminUseCase: AdminUseCase, + editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil + ) { self.nickname = nickname self.adminUseCase = adminUseCase self.mainView = PopUpRegisterView() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift index b38efef8..a3a21e31 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift @@ -16,7 +16,10 @@ final class AdminViewController: BaseViewController, View { private let adminUseCase: AdminUseCase // MARK: - Init - init(nickname: String, adminUseCase: AdminUseCase = AdminUseCaseImpl(repository: AdminRepositoryImpl(provider: ProviderImpl()))) { + init( + nickname: String, + adminUseCase: AdminUseCase + ) { self.nickname = nickname self.adminUseCase = adminUseCase self.mainView = AdminView(frame: .zero) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 927375f9..68a57447 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -272,11 +272,12 @@ final class MyPageReactor: Reactor { case .moveToAdminScene(let controller): // 관리자 VC let nickname = profileSection.inputDataList.first?.nickName ?? "" - let adminVC = AdminViewController(nickname: nickname) + let adminVC = AdminViewController( + nickname: nickname, + adminUseCase: DIContainer.resolve(AdminUseCase.self) + ) adminVC.reactor = AdminReactor( - useCase: AdminUseCaseImpl( - repository: AdminRepositoryImpl(provider: ProviderImpl()) - ) + adminUseCase: DIContainer.resolve(AdminUseCase.self) ) controller.navigationController?.pushViewController(adminVC, animated: true) } From c34ff2e56e5af67aaa091272251ff612037829f1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 21:55:38 +0900 Subject: [PATCH 123/393] =?UTF-8?q?refactor/#113:=20MapUseCase=20=EC=9D=98?= =?UTF-8?q?=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 --- .../MapGuideView/MapGuideViewController.swift | 6 +++++- .../Scene/Map/MapView/MapReactor.swift | 16 ++++++++-------- .../TabbarController/TabbarController.swift | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 3cec2853..9e86e795 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -174,8 +174,12 @@ final class MapGuideViewController: UIViewController, View { let providerInstance = ProviderImpl() @Dependency var mapUseCase: MapUseCase + // FIXME: Respository를 UseCase를 태우도록 변경 필요!! let directionRepositoryInstance = MapDirectionRepositoryImpl(provider: providerInstance) - let mapReactorInstance = MapReactor(useCase: mapUseCase, directionRepository: directionRepositoryInstance) + let mapReactorInstance = MapReactor( + mapUseCase: mapUseCase, + directionRepository: directionRepositoryInstance + ) if let selectedStore = strongSelf.currentCarouselStoreList.first { mapReactorInstance.action.onNext(.didSelectItem(selectedStore)) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift index b0e82322..f1b599ac 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift @@ -64,11 +64,11 @@ final class MapReactor: Reactor { } let initialState: State - private let useCase: MapUseCase + private let mapUseCase: MapUseCase private let directionRepository: MapDirectionRepository - init(useCase: MapUseCase, directionRepository: MapDirectionRepository) { - self.useCase = useCase + init(mapUseCase: MapUseCase, directionRepository: MapDirectionRepository) { + self.mapUseCase = mapUseCase self.directionRepository = directionRepository self.initialState = State() } @@ -90,7 +90,7 @@ final class MapReactor: Reactor { func mutate(action: Action) -> Observable { switch action { case .fetchCategories: - return useCase.fetchCategories() + return mapUseCase.fetchCategories() .map { categories in let mapping = categories.reduce(into: [String: Int64]()) { dict, category in dict[category.category] = category.categoryId @@ -107,7 +107,7 @@ final class MapReactor: Reactor { return .concat([ .just(.setSearchResults([])), .just(.setLoading(true)), - useCase.searchStores(query: query, categories: categoryIDs) + mapUseCase.searchStores(query: query, categories: categoryIDs) .flatMap { results -> Observable in if results.isEmpty { return .just(.setToastMessage("검색 결과가 없습니다.")) @@ -124,7 +124,7 @@ final class MapReactor: Reactor { return .concat([ .just(.setLoading(true)), - useCase.fetchStoresInBounds( + mapUseCase.fetchStoresInBounds( northEastLat: northEastLat, northEastLon: northEastLon, southWestLat: southWestLat, @@ -185,7 +185,7 @@ final class MapReactor: Reactor { .compactMap { currentState.categoryMapping[$0] } return Observable.concat([ Observable.just(.setLoading(true)), - useCase.fetchStoresInBounds( + mapUseCase.fetchStoresInBounds( northEastLat: northEastLat, northEastLon: northEastLon, southWestLat: southWestLat, @@ -260,7 +260,7 @@ final class MapReactor: Reactor { return .concat([ .just(.setLoading(true)), - useCase.fetchStoresInBounds( + mapUseCase.fetchStoresInBounds( northEastLat: koreaRegion.northEast.lat, northEastLon: koreaRegion.northEast.lon, southWestLat: koreaRegion.southWest.lat, diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index 18b8735e..cca64663 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -198,7 +198,7 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let mapController = MapViewController() @Dependency var mapUseCase: MapUseCase let directionRepository = MapDirectionRepositoryImpl(provider: provider) - mapController.reactor = MapReactor(useCase: mapUseCase, directionRepository: directionRepository) + mapController.reactor = MapReactor(mapUseCase: mapUseCase, directionRepository: directionRepository) let homeController = HomeController() homeController.reactor = HomeReactor() From e67aec78695704542265093e2a95491684c9d4f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:09:39 +0900 Subject: [PATCH 124/393] =?UTF-8?q?refactor/#113:=20UserAPIUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 + .../CommentDetail/CommentDetailReactor.swift | 8 ++- .../CommentList/CommentListReactor.swift | 19 ++++-- .../OtherUserCommentReactor.swift | 13 +++- .../Scene/Detail/DetailReactor.swift | 29 +++++++-- .../Scene/Home/List/HomeListReactor.swift | 13 +++- .../Scene/Home/Main/HomeReactor.swift | 65 +++++++++++++++---- .../Scene/Map/MapView/MapViewController.swift | 11 +++- .../Map/StoreListView/StoreListReactor.swift | 4 +- .../StoreListViewController.swift | 5 +- .../MyPage/Block/BlockUserManageReactor.swift | 5 +- .../Bookmark/Main/MyPageBookmarkReactor.swift | 15 +++-- .../Scene/MyPage/Main/MyPageReactor.swift | 24 ++++--- .../MyComment/Main/MyCommentReactor.swift | 10 ++- .../Detail/MyPageNoticeDetailReactor.swift | 8 ++- .../Notice/List/MyPageNoticeReactor.swift | 10 ++- .../CategoryEditModalReactor.swift | 8 ++- .../InfoEditModal/InfoEditModalReactor.swift | 9 ++- .../ProfileEdit/Main/ProfileEditReactor.swift | 16 +++-- .../MyPage/Recent/MyPageRecentReactor.swift | 10 ++- .../WithdrawlReasonReactor.swift | 5 +- .../AfterSearch/SearchResultReactor.swift | 10 ++- .../Search/BeforeSearch/SearchReactor.swift | 10 ++- .../Search/Main/SearchMainController.swift | 4 +- .../TabbarController/TabbarController.swift | 4 +- .../Utills/ToastMaker/ToastMaker.swift | 4 +- 26 files changed, 236 insertions(+), 86 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index e0b3e405..4e661809 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -38,13 +38,16 @@ extension AppDelegate { // MARK: Register repository DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } + DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @Dependency var adminRepository: AdminRepository + @Dependency var userAPIRepository: UserAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } + DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 8b7d28f3..8641f50d 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -36,7 +36,7 @@ final class CommentDetailReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -58,8 +58,12 @@ final class CommentDetailReactor: Reactor { private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) // MARK: - init - init(comment: DetailCommentSection.CellType.Input) { + init( + comment: DetailCommentSection.CellType.Input, + userAPIUseCase: UserAPIUseCase + ) { self.initialState = State(commentData: comment) + self.userAPIUseCase = userAPIUseCase } // MARK: - Reactor Methods diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index af0fc685..3310f1b3 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -50,7 +50,7 @@ final class CommentListReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -73,10 +73,15 @@ final class CommentListReactor: Reactor { private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) private let spacing28Section = SpacingSection(inputDataList: [.init(spacing: 28)]) // MARK: - init - init(popUpID: Int64, popUpName: String?) { + init( + popUpID: Int64, + popUpName: String?, + userAPIUseCase: UserAPIUseCase + ) { self.initialState = State() self.popUpID = popUpID self.popUpName = popUpName + self.userAPIUseCase = userAPIUseCase } // MARK: - Reactor Methods @@ -188,7 +193,10 @@ final class CommentListReactor: Reactor { case .presentDetailScene(let controller, let row): let comment = commentSection.inputDataList[row] let nextController = CommentDetailController() - nextController.reactor = CommentDetailReactor(comment: comment) + nextController.reactor = CommentDetailReactor( + comment: comment, + userAPIUseCase: userAPIUseCase + ) nextController.mainView.likeButton.rx.tap .map { Action.detailSceneLikeButtonTapped(row: row)} .bind(to: action) @@ -233,7 +241,10 @@ final class CommentListReactor: Reactor { case .normal: owner.dismiss(animated: true) { [weak controller] in let otherUserCommentController = OtherUserCommentController() - otherUserCommentController.reactor = OtherUserCommentReactor(commenterID: comment.creator) + otherUserCommentController.reactor = OtherUserCommentReactor( + commenterID: comment.creator, + userAPIUseCase: self.userAPIUseCase + ) controller?.navigationController?.pushViewController(otherUserCommentController, animated: true) } case .block: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index bc2de562..5f4e5167 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -38,7 +38,7 @@ final class OtherUserCommentReactor: Reactor { var disposeBag = DisposeBag() private let commenterID: String? - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -58,9 +58,13 @@ final class OtherUserCommentReactor: Reactor { private var popUpSection = MyCommentedPopUpGridSection(inputDataList: []) // MARK: - init - init(commenterID: String?) { + init( + commenterID: String?, + userAPIUseCase: UserAPIUseCase + ) { self.initialState = State() self.commenterID = commenterID + self.userAPIUseCase = userAPIUseCase } // MARK: - Reactor Methods @@ -103,7 +107,10 @@ final class OtherUserCommentReactor: Reactor { case .moveToDetailScene(let controller, let row): let id = popUpSection.inputDataList[row].popUpID let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: id) + nextController.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } return newState diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift index ced7d370..d4abab15 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift @@ -59,7 +59,7 @@ final class DetailReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -94,8 +94,12 @@ final class DetailReactor: Reactor { private var spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private var spacing16GraySection = SpacingSection(inputDataList: [.init(spacing: 16, backgroundColor: .g50)]) // MARK: - init - init(popUpID: Int64) { + init( + popUpID: Int64, + userAPIUseCase: UserAPIUseCase + ) { self.popUpID = popUpID + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -171,7 +175,11 @@ final class DetailReactor: Reactor { case .moveToCommentTotalScene(let controller): if isLogin { let nextController = CommentListController() - nextController.reactor = CommentListReactor(popUpID: popUpID, popUpName: popUpName) + nextController.reactor = CommentListReactor( + popUpID: popUpID, + popUpName: popUpName, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } else { let loginController = SubLoginController() @@ -190,12 +198,18 @@ final class DetailReactor: Reactor { case .showCommentDetailScene(let controller, let indexPath): let comment = commentSection.inputDataList[indexPath.row] let nextController = CommentDetailController() - nextController.reactor = CommentDetailReactor(comment: comment) + nextController.reactor = CommentDetailReactor( + comment: comment, + userAPIUseCase: userAPIUseCase + ) controller.presentPanModal(nextController) case .moveToDetailScene(let controller, let indexPath): let id = similarSection.inputDataList[indexPath.row].id let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: id) + nextController.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): controller.navigationController?.popViewController(animated: true) @@ -436,7 +450,10 @@ extension DetailReactor { case .normal: owner.dismiss(animated: true) { [weak controller] in let otherUserCommentController = OtherUserCommentController() - otherUserCommentController.reactor = OtherUserCommentReactor(commenterID: comment.creator) + otherUserCommentController.reactor = OtherUserCommentReactor( + commenterID: comment.creator, + userAPIUseCase: self.userAPIUseCase + ) controller?.navigationController?.pushViewController(otherUserCommentController, animated: true) } case .block: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift index 1e3d37b3..c454c132 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift @@ -45,7 +45,7 @@ final class HomeListReactor: Reactor { private let homeAPIUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) private let userDefaultService = UserDefaultService() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase private var isLoading: Bool = false private var totalPage: Int32 = 0 @@ -70,9 +70,13 @@ final class HomeListReactor: Reactor { private var cardSections = HomeCardGridSection(inputDataList: []) // MARK: - init - init(popUpType: HomePopUpType) { + init( + popUpType: HomePopUpType, + userAPIUseCase: UserAPIUseCase + ) { self.initialState = State(popUpType: popUpType) self.popUpType = popUpType + self.userAPIUseCase = userAPIUseCase } // MARK: - Reactor Methods @@ -144,7 +148,10 @@ final class HomeListReactor: Reactor { isLoading = false case .moveToDetailScene(let controller, let row): let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: cardSections.inputDataList[row].id) + nextController.reactor = DetailReactor( + popUpID: cardSections.inputDataList[row].id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } return newState diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift index 1c4ebf67..a34eba5c 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift @@ -40,7 +40,7 @@ final class HomeReactor: Reactor { var disposeBag = DisposeBag() private let homeApiUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase private let userDefaultService = UserDefaultService() lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -84,7 +84,8 @@ final class HomeReactor: Reactor { private var spaceGray24Section = SpacingSection(inputDataList: [.init(spacing: 24, backgroundColor: .g700)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -272,35 +273,56 @@ final class HomeReactor: Reactor { case 0: if let id = loginImageBannerSection.inputDataList.first?.idList[indexPath.row - 1] { let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) } case 2: let controller = HomeListController() - controller.reactor = HomeListReactor(popUpType: .curation) + controller.reactor = HomeListReactor( + popUpType: .curation, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 4: let id = curationSection.inputDataList[indexPath.row].id let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: let controller = HomeListController() - controller.reactor = HomeListReactor(popUpType: .popular) + controller.reactor = HomeListReactor( + popUpType: .popular, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 9: let id = popularSection.inputDataList[indexPath.row].id let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 12: let controller = HomeListController() - controller.reactor = HomeListReactor(popUpType: .new) + controller.reactor = HomeListReactor( + popUpType: .new, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 14: let id = newSection.inputDataList[indexPath.row].id let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) default: break @@ -310,26 +332,41 @@ final class HomeReactor: Reactor { case 0: if let id = loginImageBannerSection.inputDataList.first?.idList[indexPath.row - 1] { let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) } case 2: let controller = HomeListController() - controller.reactor = HomeListReactor(popUpType: .popular) + controller.reactor = HomeListReactor( + popUpType: .popular, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 4: let id = popularSection.inputDataList[indexPath.row].id let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: let controller = HomeListController() - controller.reactor = HomeListReactor(popUpType: .new) + controller.reactor = HomeListReactor( + popUpType: .new, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) case 9: let id = newSection.inputDataList[indexPath.row].id let controller = DetailController() - controller.reactor = DetailReactor(popUpID: id) + controller.reactor = DetailReactor( + popUpID: id, + userAPIUseCase: userAPIUseCase + ) currentController.navigationController?.pushViewController(controller, animated: true) default: break diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift index e327b463..17b6f1ec 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift @@ -41,8 +41,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let carouselView = MapPopupCarouselView() private let locationManager = CLLocationManager() var currentMarker: NMFMarker? - private let storeListReactor = StoreListReactor() - private let storeListViewController = StoreListViewController(reactor: StoreListReactor()) + private let storeListReactor = StoreListReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + private let storeListViewController = StoreListViewController( + reactor: StoreListReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + ) private var listViewTopConstraint: Constraint? private var currentFilterBottomSheet: FilterBottomSheetViewController? private var filterChipsTopY: CGFloat = 0 @@ -115,7 +117,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.onCardTapped = { [weak self] store in let detailController = DetailController() - detailController.reactor = DetailReactor(popUpID: Int64(store.id)) + detailController.reactor = DetailReactor( + popUpID: Int64(store.id), + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self) + ) self?.navigationController?.isNavigationBarHidden = false self?.navigationController?.tabBarController?.tabBar.isHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index 21e54ec8..dba652c8 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -5,7 +5,7 @@ import RxSwift final class StoreListReactor: Reactor { // MARK: - Reactor - private let userAPIUseCase: UserAPIUseCaseImpl + private let userAPIUseCase: UserAPIUseCase private let popUpAPIUseCase: PopUpAPIUseCaseImpl private let bookmarkStateRelay = PublishRelay<(Int64, Bool)>() @@ -48,7 +48,7 @@ final class StoreListReactor: Reactor { var initialState: State init( - userAPIUseCase: UserAPIUseCaseImpl = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())), + userAPIUseCase: UserAPIUseCase, popUpAPIUseCase: PopUpAPIUseCaseImpl = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) ) { self.userAPIUseCase = userAPIUseCase diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift index 3a358ce8..5f421b12 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift @@ -116,7 +116,10 @@ final class StoreListViewController: UIViewController, View { let store = owner.reactor?.currentState.stores[indexPath.item] else { return } let detailController = DetailController() - detailController.reactor = DetailReactor(popUpID: Int64(store.id)) + detailController.reactor = DetailReactor( + popUpID: Int64(store.id), + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self) + ) owner.navigationController?.isNavigationBarHidden = false owner.navigationController?.tabBarController?.tabBar.isHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index 99c16ac2..e0439e64 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -35,7 +35,7 @@ final class BlockUserManageReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -56,7 +56,8 @@ final class BlockUserManageReactor: Reactor { private var spcing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 077b4506..f8ac3593 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -52,7 +52,7 @@ final class MyPageBookmarkReactor: Reactor { private var size: Int32 = 10 private var viewType: String = "크게보기" - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -74,7 +74,8 @@ final class MyPageBookmarkReactor: Reactor { private var spacing150Section = SpacingSection(inputDataList: [.init(spacing: 150)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -178,7 +179,10 @@ final class MyPageBookmarkReactor: Reactor { controller.navigationController?.popViewController(animated: true) case .moveToDetailScene(let controller, let row): let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: listSection.inputDataList[row].id) + nextController.reactor = DetailReactor( + popUpID: listSection.inputDataList[row].id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .presentModal(let controller): let nextController = BookMarkPopUpViewTypeModalController() @@ -195,7 +199,10 @@ final class MyPageBookmarkReactor: Reactor { .disposed(by: nextController.disposeBag) case .moveToSuggestScene(let controller): let nextController = HomeListController() - nextController.reactor = HomeListReactor(popUpType: .curation) + nextController.reactor = HomeListReactor( + popUpType: .curation, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } newState.sections = getSection() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 68a57447..60bfe676 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -45,7 +45,7 @@ final class MyPageReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -103,7 +103,8 @@ final class MyPageReactor: Reactor { var isAdmin: Bool = false // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -180,7 +181,7 @@ final class MyPageReactor: Reactor { case .moveToProfileEditScene(let controller): let nextController = ProfileEditController() - nextController.reactor = ProfileEditReactor() + nextController.reactor = ProfileEditReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case .logout: @@ -207,7 +208,7 @@ final class MyPageReactor: Reactor { case .apply: nextController.dismiss(animated: true) { let reasonController = WithdrawlReasonController() - reasonController.reactor = WithdrawlReasonReactor() + reasonController.reactor = WithdrawlReasonReactor(userAPIUseCase: self.userAPIUseCase) controller?.navigationController?.pushViewController(reasonController, animated: true) } case .cancel: @@ -220,12 +221,12 @@ final class MyPageReactor: Reactor { case "차단한 사용자 관리": let nextController = BlockUserManageController() - nextController.reactor = BlockUserManageReactor() + nextController.reactor = BlockUserManageReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case "공지사항": let nextController = MyPageNoticeController() - nextController.reactor = MyPageNoticeReactor() + nextController.reactor = MyPageNoticeReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case "고객문의": @@ -235,12 +236,12 @@ final class MyPageReactor: Reactor { case "찜한 팝업": let nextController = MyPageBookmarkController() - nextController.reactor = MyPageBookmarkReactor() + nextController.reactor = MyPageBookmarkReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case "최근 본 팝업": let nextController = MyPageRecentController() - nextController.reactor = MyPageRecentReactor() + nextController.reactor = MyPageRecentReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case "약관": @@ -254,7 +255,10 @@ final class MyPageReactor: Reactor { case .moveToPopUpDetailScene(let controller, let row): let nextController = DetailController() let popUpID = commentSection.inputDataList[row].popUpID - nextController.reactor = DetailReactor(popUpID: popUpID) + nextController.reactor = DetailReactor( + popUpID: popUpID, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToLoginScene(let controller): @@ -266,7 +270,7 @@ final class MyPageReactor: Reactor { case .moveToMyCommentScene(let controller): let nextController = MyCommentController() - nextController.reactor = MyCommentReactor() + nextController.reactor = MyCommentReactor(userAPIUseCase: userAPIUseCase) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToAdminScene(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index 83445aac..f4688687 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -37,7 +37,7 @@ final class MyCommentReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -59,7 +59,8 @@ final class MyCommentReactor: Reactor { private var spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -106,7 +107,10 @@ final class MyCommentReactor: Reactor { case .moveToDetailScene(let controller, let row): let popUpID = listSection.inputDataList[row].popUpID let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: popUpID) + nextController.reactor = DetailReactor( + popUpID: popUpID, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): controller.navigationController?.popViewController(animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift index 02bb5176..f868b9ed 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift @@ -37,10 +37,14 @@ final class MyPageNoticeDetailReactor: Reactor { var content: String? var noticeID: Int64 - let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + let userAPIUseCase: UserAPIUseCase // MARK: - init - init(noticeID: Int64) { + init( + noticeID: Int64, + userAPIUseCase: UserAPIUseCase + ) { self.noticeID = noticeID + self.userAPIUseCase = userAPIUseCase self.initialState = State() } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index ab4da90c..c5620e42 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -34,7 +34,7 @@ final class MyPageNoticeReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -54,7 +54,8 @@ final class MyPageNoticeReactor: Reactor { private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -85,7 +86,10 @@ final class MyPageNoticeReactor: Reactor { newState.sections = getSection() case .moveToDetailScene(let controller, let row): let nextController = MyPageNoticeDetailController() - nextController.reactor = MyPageNoticeDetailReactor(noticeID: listSection.inputDataList[row].noticeID) + nextController.reactor = MyPageNoticeDetailReactor( + noticeID: listSection.inputDataList[row].noticeID, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): controller.navigationController?.popViewController(animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index 2fb1bac2..518e80bf 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -53,12 +53,16 @@ final class CategoryEditModalReactor: Reactor { }() private var signUpUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) - private var userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private var userAPIUseCase: UserAPIUseCase private var tagSection = TagSection(inputDataList: []) // MARK: - init - init(selectedID: [Int64]) { + init( + selectedID: [Int64], + userAPIUseCase: UserAPIUseCase + ) { self.originSelectedID = selectedID + self.userAPIUseCase = userAPIUseCase self.initialState = State(originSelectedID: selectedID) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift index df0ae566..45e48e9b 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift @@ -48,11 +48,16 @@ final class InfoEditModalReactor: Reactor { var currentAge: Int32 = 0 var currentGender: String? - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase // MARK: - init - init(age: Int32, gender: String?) { + init( + age: Int32, + gender: String?, + userAPIUseCase: UserAPIUseCase + ) { self.originAge = age self.originGender = gender + self.userAPIUseCase = userAPIUseCase self.initialState = State(age: age, gender: gender) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 5719a881..9aa9f5e3 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -64,12 +64,13 @@ final class ProfileEditReactor: Reactor { var currentIntro: String? var introIsActive: Bool = false - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) private let imageService = PreSignedService() // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -133,11 +134,18 @@ final class ProfileEditReactor: Reactor { newState.introState = checkIntroState(text: currentIntro, isActive: introIsActive) case .moveToCategoryEditScene(let controller): let nextController = CategoryEditModalController() - nextController.reactor = CategoryEditModalReactor(selectedID: newState.originProfileData?.interestCategoryList.map { $0.categoryId } ?? []) + nextController.reactor = CategoryEditModalReactor( + selectedID: newState.originProfileData?.interestCategoryList.map { $0.categoryId } ?? [], + userAPIUseCase: userAPIUseCase + ) controller.presentPanModal(nextController) case .moveToInfoEditScene(let controller): let nextController = InfoEditModalController() - nextController.reactor = InfoEditModalReactor(age: originProfileData?.age ?? 0, gender: originProfileData?.gender) + nextController.reactor = InfoEditModalReactor( + age: originProfileData?.age ?? 0, + gender: originProfileData?.gender, + userAPIUseCase: userAPIUseCase + ) controller.presentPanModal(nextController) case .isValidateNickName(let isValidate): if isValidate { diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 300b6bcb..7bd27e39 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -42,7 +42,7 @@ final class MyPageRecentReactor: Reactor { private var currentPage: Int32 = 0 private var size: Int32 = 100 - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -63,7 +63,8 @@ final class MyPageRecentReactor: Reactor { private var spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -125,7 +126,10 @@ final class MyPageRecentReactor: Reactor { controller.navigationController?.popViewController(animated: true) case .moveToDetailScene(let controller, let row): let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: listSection.inputDataList[row].id) + nextController.reactor = DetailReactor( + popUpID: listSection.inputDataList[row].id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } return newState diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift index 4e0a67de..4748166b 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -57,11 +57,12 @@ final class WithdrawlReasonReactor: Reactor { private var reasonSection = WithdrawlCheckSection(inputDataList: []) private var spacing156Section = SpacingSection(inputDataList: [.init(spacing: 156)]) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase @Dependency private var keyChainService: KeyChainService private let userDefaultService = UserDefaultService() // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index f4601985..f61829cd 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -36,7 +36,7 @@ final class SearchResultReactor: Reactor { var initialState: State var disposeBag = DisposeBag() private var popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -59,7 +59,8 @@ final class SearchResultReactor: Reactor { private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -123,7 +124,10 @@ final class SearchResultReactor: Reactor { newState.isEmptyResult = true case .moveToDetailScene(let controller, let indexPath): let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: searchListSection.inputDataList[indexPath.row].id) + nextController.reactor = DetailReactor( + popUpID: searchListSection.inputDataList[indexPath.row].id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) } return newState diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index a8fa3122..df6ee726 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -59,7 +59,7 @@ final class SearchReactor: Reactor { let userDefaultService = UserDefaultService() private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) - private let userAPIUseCase = UserAPIUseCaseImpl(repository: UserAPIRepositoryImpl(provider: ProviderImpl())) + private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -91,7 +91,8 @@ final class SearchReactor: Reactor { private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) // MARK: - init - init() { + init(userAPIUseCase: UserAPIUseCase) { + self.userAPIUseCase = userAPIUseCase self.initialState = State() } @@ -219,7 +220,10 @@ final class SearchReactor: Reactor { .disposed(by: nextController.disposeBag) case .moveToDetailScene(let controller, let indexPath): let nextController = DetailController() - nextController.reactor = DetailReactor(popUpID: searchListSection.inputDataList[indexPath.row].id) + nextController.reactor = DetailReactor( + popUpID: searchListSection.inputDataList[indexPath.row].id, + userAPIUseCase: userAPIUseCase + ) controller.navigationController?.pushViewController(nextController, animated: true) case .setSearchKeyWord(let text): newState.searchKeyWord = text diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift index c9b9fe6f..4da3edf1 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift @@ -25,13 +25,13 @@ final class SearchMainController: BaseTabmanController, View { var beforeController: SearchController = { let controller = SearchController() - controller.reactor = SearchReactor() + controller.reactor = SearchReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) return controller }() var afterController: SearchResultController = { let controller = SearchResultController() - controller.reactor = SearchResultReactor() + controller.reactor = SearchResultReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) return controller }() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index cca64663..2c6f55eb 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -201,10 +201,10 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { mapController.reactor = MapReactor(mapUseCase: mapUseCase, directionRepository: directionRepository) let homeController = HomeController() - homeController.reactor = HomeReactor() + homeController.reactor = HomeReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) let myPageController = MyPageController() - myPageController.reactor = MyPageReactor() + myPageController.reactor = MyPageReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) let iconSize = CGSize(width: 32, height: 32) // 탭바 아이템 생성 diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift index 54f6fdf7..924c7b0e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift @@ -92,7 +92,9 @@ extension ToastMaker { .withUnretained(currentVC) .subscribe(onNext: { (owner, _) in let nextController = MyPageBookmarkController() - nextController.reactor = MyPageBookmarkReactor() + nextController.reactor = MyPageBookmarkReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self) + ) owner.navigationController?.pushViewController(nextController, animated: true) }) .disposed(by: disposeBag) From 8c0e3ebf5d05434fc4b6d34794678c217dc129f7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:21:20 +0900 Subject: [PATCH 125/393] =?UTF-8?q?refactor/#113:=20PopUpAPIUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../CommentList/CommentListReactor.swift | 6 ++++-- .../OtherUserCommentReactor.swift | 3 ++- .../Scene/Detail/DetailReactor.swift | 12 +++++++---- .../Scene/Home/List/HomeListReactor.swift | 3 ++- .../Scene/Home/Main/HomeReactor.swift | 21 ++++++++++++------- .../Scene/Map/MapView/MapViewController.swift | 7 +++++-- .../StoreListViewController.swift | 3 ++- .../Bookmark/Main/MyPageBookmarkReactor.swift | 3 ++- .../Scene/MyPage/Main/MyPageReactor.swift | 3 ++- .../MyComment/Main/MyCommentReactor.swift | 3 ++- .../MyPage/Recent/MyPageRecentReactor.swift | 3 ++- .../AfterSearch/SearchResultReactor.swift | 11 +++++++--- .../Search/BeforeSearch/SearchReactor.swift | 11 +++++++--- .../Search/Main/SearchMainController.swift | 10 +++++++-- 15 files changed, 72 insertions(+), 30 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 4e661809..3128fc07 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -39,15 +39,18 @@ extension AppDelegate { DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } + DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @Dependency var adminRepository: AdminRepository @Dependency var userAPIRepository: UserAPIRepository + @Dependency var popUpAPIRepository: PopUpAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } + DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index 3310f1b3..c98139f0 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -49,7 +49,7 @@ final class CommentListReactor: Reactor { private var appendDataIsEmpty: Bool = false private var imageService = PreSignedService() - private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) @@ -76,12 +76,14 @@ final class CommentListReactor: Reactor { init( popUpID: Int64, popUpName: String?, - userAPIUseCase: UserAPIUseCase + userAPIUseCase: UserAPIUseCase, + popUpAPIUseCase: PopUpAPIUseCase ) { self.initialState = State() self.popUpID = popUpID self.popUpName = popUpName self.userAPIUseCase = userAPIUseCase + self.popUpAPIUseCase = popUpAPIUseCase } // MARK: - Reactor Methods diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index 5f4e5167..c18f26d4 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -109,7 +109,8 @@ final class OtherUserCommentReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift index d4abab15..c127a559 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift @@ -58,7 +58,7 @@ final class DetailReactor: Reactor { private var isFirstRequest: Bool = true private var imageService = PreSignedService() - private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -96,10 +96,12 @@ final class DetailReactor: Reactor { // MARK: - init init( popUpID: Int64, - userAPIUseCase: UserAPIUseCase + userAPIUseCase: UserAPIUseCase, + popUpAPIUseCase: PopUpAPIUseCase ) { self.popUpID = popUpID self.userAPIUseCase = userAPIUseCase + self.popUpAPIUseCase = popUpAPIUseCase self.initialState = State() } @@ -178,7 +180,8 @@ final class DetailReactor: Reactor { nextController.reactor = CommentListReactor( popUpID: popUpID, popUpName: popUpName, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: popUpAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) } else { @@ -208,7 +211,8 @@ final class DetailReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: popUpAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift index c454c132..52b0ee7f 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift @@ -150,7 +150,8 @@ final class HomeListReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: cardSections.inputDataList[row].id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift index a34eba5c..4406df63 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift @@ -275,7 +275,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -291,7 +292,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -306,7 +308,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 12: @@ -321,7 +324,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: @@ -334,7 +338,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -350,7 +355,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -365,7 +371,8 @@ final class HomeReactor: Reactor { let controller = DetailController() controller.reactor = DetailReactor( popUpID: id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift index 17b6f1ec..67c5b4bc 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift @@ -32,8 +32,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var markerDictionary: [Int64: NMFMarker] = [:] private var individualMarkerDictionary: [Int64: NMFMarker] = [:] private var clusterMarkerDictionary: [String: NMFMarker] = [:] + // FIXME: Reactor 이용해서 처리하도록 수정 private let popUpAPIUseCase = PopUpAPIUseCaseImpl( - repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + repository: PopUpAPIRepositoryImpl(provider: ProviderImpl()) + ) private let clusteringManager = ClusteringManager() var currentStores: [MapPopUpStore] = [] var disposeBag = DisposeBag() @@ -119,7 +121,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let detailController = DetailController() detailController.reactor = DetailReactor( popUpID: Int64(store.id), - userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self) + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: self?.popUpAPIUseCase ?? DIContainer.resolve(PopUpAPIUseCase.self) ) self?.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift index 5f421b12..8239c1a4 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift @@ -118,7 +118,8 @@ final class StoreListViewController: UIViewController, View { let detailController = DetailController() detailController.reactor = DetailReactor( popUpID: Int64(store.id), - userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self) + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) owner.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index f8ac3593..b3e0660d 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -181,7 +181,8 @@ final class MyPageBookmarkReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: listSection.inputDataList[row].id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .presentModal(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 60bfe676..274d1294 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -257,7 +257,8 @@ final class MyPageReactor: Reactor { let popUpID = commentSection.inputDataList[row].popUpID nextController.reactor = DetailReactor( popUpID: popUpID, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index f4688687..bfb9275d 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -109,7 +109,8 @@ final class MyCommentReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: popUpID, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 7bd27e39..7a48683f 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -128,7 +128,8 @@ final class MyPageRecentReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: listSection.inputDataList[row].id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index f61829cd..b05c1ddb 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -35,7 +35,7 @@ final class SearchResultReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private var popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + private var popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -59,8 +59,12 @@ final class SearchResultReactor: Reactor { private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) // MARK: - init - init(userAPIUseCase: UserAPIUseCase) { + init( + userAPIUseCase: UserAPIUseCase, + popUpAPIUseCase: PopUpAPIUseCase + ) { self.userAPIUseCase = userAPIUseCase + self.popUpAPIUseCase = popUpAPIUseCase self.initialState = State() } @@ -126,7 +130,8 @@ final class SearchResultReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: searchListSection.inputDataList[indexPath.row].id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: popUpAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index df6ee726..5027add3 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -58,7 +58,7 @@ final class SearchReactor: Reactor { private var isLoading: Bool = false let userDefaultService = UserDefaultService() - private let popUpAPIUseCase = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -91,8 +91,12 @@ final class SearchReactor: Reactor { private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) // MARK: - init - init(userAPIUseCase: UserAPIUseCase) { + init( + userAPIUseCase: UserAPIUseCase, + popUpAPIUseCase: PopUpAPIUseCase + ) { self.userAPIUseCase = userAPIUseCase + self.popUpAPIUseCase = popUpAPIUseCase self.initialState = State() } @@ -222,7 +226,8 @@ final class SearchReactor: Reactor { let nextController = DetailController() nextController.reactor = DetailReactor( popUpID: searchListSection.inputDataList[indexPath.row].id, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + popUpAPIUseCase: popUpAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) case .setSearchKeyWord(let text): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift index 4da3edf1..91840c9c 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift @@ -25,13 +25,19 @@ final class SearchMainController: BaseTabmanController, View { var beforeController: SearchController = { let controller = SearchController() - controller.reactor = SearchReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + controller.reactor = SearchReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + ) return controller }() var afterController: SearchResultController = { let controller = SearchResultController() - controller.reactor = SearchResultReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + controller.reactor = SearchResultReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + ) return controller }() From e489b14b63807255c6a481a0e640a2b64a54144f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:36:23 +0900 Subject: [PATCH 126/393] =?UTF-8?q?refactor/#113:=20CommentAPIUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../CommentList/CommentListReactor.swift | 13 +++++++--- .../NormalCommentAddReactor.swift | 9 +++++-- .../NormalCommentEditReactor.swift | 10 ++++++-- .../OtherUserCommentReactor.swift | 3 ++- .../Scene/Detail/DetailReactor.swift | 25 ++++++++++++++----- .../Scene/Home/List/HomeListReactor.swift | 3 ++- .../Scene/Home/Main/HomeReactor.swift | 21 ++++++++++------ .../Scene/Map/MapView/MapViewController.swift | 3 ++- .../StoreListViewController.swift | 3 ++- .../Bookmark/Main/MyPageBookmarkReactor.swift | 3 ++- .../Scene/MyPage/Main/MyPageReactor.swift | 3 ++- .../MyComment/Main/MyCommentReactor.swift | 3 ++- .../MyPage/Recent/MyPageRecentReactor.swift | 3 ++- .../AfterSearch/SearchResultReactor.swift | 3 ++- .../Search/BeforeSearch/SearchReactor.swift | 3 ++- 16 files changed, 81 insertions(+), 30 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 3128fc07..04bcfb3a 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -40,17 +40,20 @@ extension AppDelegate { DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } + DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @Dependency var adminRepository: AdminRepository @Dependency var userAPIRepository: UserAPIRepository @Dependency var popUpAPIRepository: PopUpAPIRepository + @Dependency var commentAPIRepository: CommentAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } + DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index c98139f0..7bdd33c5 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -51,7 +51,7 @@ final class CommentListReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) + private let commentAPIUseCase: CommentAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -77,13 +77,15 @@ final class CommentListReactor: Reactor { popUpID: Int64, popUpName: String?, userAPIUseCase: UserAPIUseCase, - popUpAPIUseCase: PopUpAPIUseCase + popUpAPIUseCase: PopUpAPIUseCase, + commentAPIUseCase: CommentAPIUseCase ) { self.initialState = State() self.popUpID = popUpID self.popUpName = popUpName self.userAPIUseCase = userAPIUseCase self.popUpAPIUseCase = popUpAPIUseCase + self.commentAPIUseCase = commentAPIUseCase } // MARK: - Reactor Methods @@ -311,7 +313,12 @@ final class CommentListReactor: Reactor { owner.dismiss(animated: true) { [weak controller] in guard let popUpName = self.popUpName else { return } let editController = NormalCommentEditController() - editController.reactor = NormalCommentEditReactor(popUpID: self.popUpID, popUpName: popUpName, comment: comment) + editController.reactor = NormalCommentEditReactor( + popUpID: self.popUpID, + popUpName: popUpName, + comment: comment, + commentAPIUseCase: self.commentAPIUseCase + ) controller?.navigationController?.pushViewController(editController, animated: true) } case .cancel: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index 8e7d528b..8e00f247 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -47,7 +47,7 @@ final class NormalCommentAddReactor: Reactor { private var popUpID: Int64 private var popUpName: String - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) + private let commentAPIUseCase: CommentAPIUseCase private let imageService = PreSignedService() lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -74,10 +74,15 @@ final class NormalCommentAddReactor: Reactor { private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) private let spacing32Section = SpacingSection(inputDataList: [.init(spacing: 32)]) // MARK: - init - init(popUpID: Int64, popUpName: String) { + init( + popUpID: Int64, + popUpName: String, + commentAPIUseCase: CommentAPIUseCase + ) { self.initialState = State() self.popUpID = popUpID self.popUpName = popUpName + self.commentAPIUseCase = commentAPIUseCase } // MARK: - Reactor Methods diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 73d67b40..a13c4ca9 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -48,7 +48,7 @@ final class NormalCommentEditReactor: Reactor { private var popUpName: String private var originComment: DetailCommentSection.CellType.Input - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) + private let commentAPIUseCase: CommentAPIUseCase private let imageService = PreSignedService() lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -76,11 +76,17 @@ final class NormalCommentEditReactor: Reactor { private let spacing32Section = SpacingSection(inputDataList: [.init(spacing: 32)]) // MARK: - init - init(popUpID: Int64, popUpName: String, comment: DetailCommentSection.CellType.Input) { + init( + popUpID: Int64, + popUpName: String, + comment: DetailCommentSection.CellType.Input, + commentAPIUseCase: CommentAPIUseCase + ) { self.initialState = State(text: comment.comment) self.popUpID = popUpID self.popUpName = popUpName self.originComment = comment + self.commentAPIUseCase = commentAPIUseCase let imageList = zip(comment.imageList, comment.imageIDList) imageSection.inputDataList.append(contentsOf: imageList.map({ url, id in .init(image: nil, isFirstCell: false, isEditCase: true, imageURL: url, imageID: id) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index c18f26d4..ed2ebb51 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -110,7 +110,8 @@ final class OtherUserCommentReactor: Reactor { nextController.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift index c127a559..7e938419 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift @@ -60,7 +60,7 @@ final class DetailReactor: Reactor { private var imageService = PreSignedService() private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase - private let commentAPIUseCase = CommentAPIUseCaseImpl(repository: CommentAPIRepositoryImpl(provider: ProviderImpl())) + private let commentAPIUseCase: CommentAPIUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in guard let self = self else { @@ -97,11 +97,13 @@ final class DetailReactor: Reactor { init( popUpID: Int64, userAPIUseCase: UserAPIUseCase, - popUpAPIUseCase: PopUpAPIUseCase + popUpAPIUseCase: PopUpAPIUseCase, + commentAPIUseCase: CommentAPIUseCase ) { self.popUpID = popUpID self.userAPIUseCase = userAPIUseCase self.popUpAPIUseCase = popUpAPIUseCase + self.commentAPIUseCase = commentAPIUseCase self.initialState = State() } @@ -151,7 +153,11 @@ final class DetailReactor: Reactor { case .moveToCommentTypeSelectedScene(let controller): if isLogin { let commentController = NormalCommentAddController() - commentController.reactor = NormalCommentAddReactor(popUpID: popUpID, popUpName: popUpName ?? "") + commentController.reactor = NormalCommentAddReactor( + popUpID: popUpID, + popUpName: popUpName ?? "", + commentAPIUseCase: commentAPIUseCase + ) controller.navigationController?.pushViewController(commentController, animated: true) } else { let loginController = SubLoginController() @@ -181,7 +187,8 @@ final class DetailReactor: Reactor { popUpID: popUpID, popUpName: popUpName, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase + popUpAPIUseCase: popUpAPIUseCase, + commentAPIUseCase: commentAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) } else { @@ -212,7 +219,8 @@ final class DetailReactor: Reactor { nextController.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase + popUpAPIUseCase: popUpAPIUseCase, + commentAPIUseCase: commentAPIUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): @@ -523,7 +531,12 @@ extension DetailReactor { owner.dismiss(animated: true) { [weak controller] in guard let popUpName = self.popUpName else { return } let editController = NormalCommentEditController() - editController.reactor = NormalCommentEditReactor(popUpID: self.popUpID, popUpName: popUpName, comment: comment) + editController.reactor = NormalCommentEditReactor( + popUpID: self.popUpID, + popUpName: popUpName, + comment: comment, + commentAPIUseCase: self.commentAPIUseCase + ) controller?.navigationController?.pushViewController(editController, animated: true) } case .cancel: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift index 52b0ee7f..54045b52 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift @@ -151,7 +151,8 @@ final class HomeListReactor: Reactor { nextController.reactor = DetailReactor( popUpID: cardSections.inputDataList[row].id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift index 4406df63..9897ffc0 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift @@ -276,7 +276,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -293,7 +294,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -309,7 +311,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 12: @@ -325,7 +328,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: @@ -339,7 +343,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -356,7 +361,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -372,7 +378,8 @@ final class HomeReactor: Reactor { controller.reactor = DetailReactor( popUpID: id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift index 67c5b4bc..762ef97f 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift @@ -122,7 +122,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM detailController.reactor = DetailReactor( popUpID: Int64(store.id), userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), - popUpAPIUseCase: self?.popUpAPIUseCase ?? DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: self?.popUpAPIUseCase ?? DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) self?.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift index 8239c1a4..9ab19b90 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift @@ -119,7 +119,8 @@ final class StoreListViewController: UIViewController, View { detailController.reactor = DetailReactor( popUpID: Int64(store.id), userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) owner.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index b3e0660d..23f39005 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -182,7 +182,8 @@ final class MyPageBookmarkReactor: Reactor { nextController.reactor = DetailReactor( popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .presentModal(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 274d1294..5edd3778 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -258,7 +258,8 @@ final class MyPageReactor: Reactor { nextController.reactor = DetailReactor( popUpID: popUpID, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index bfb9275d..a41eb076 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -110,7 +110,8 @@ final class MyCommentReactor: Reactor { nextController.reactor = DetailReactor( popUpID: popUpID, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 7a48683f..06318f0a 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -129,7 +129,8 @@ final class MyPageRecentReactor: Reactor { nextController.reactor = DetailReactor( popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index b05c1ddb..0122bc4d 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -131,7 +131,8 @@ final class SearchResultReactor: Reactor { nextController.reactor = DetailReactor( popUpID: searchListSection.inputDataList[indexPath.row].id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase + popUpAPIUseCase: popUpAPIUseCase, + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 5027add3..960feda8 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -227,7 +227,8 @@ final class SearchReactor: Reactor { nextController.reactor = DetailReactor( popUpID: searchListSection.inputDataList[indexPath.row].id, userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase + popUpAPIUseCase: popUpAPIUseCase, + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .setSearchKeyWord(let text): From d13c2b24b0a76cc48c37e775f99aa55b0f9d02fb Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:41:51 +0900 Subject: [PATCH 127/393] =?UTF-8?q?refactor/#113:=20HomeAPIUseCase=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../Scene/Home/List/HomeListReactor.swift | 6 +++-- .../Scene/Home/Main/HomeReactor.swift | 25 +++++++++++++------ .../Bookmark/Main/MyPageBookmarkReactor.swift | 3 ++- .../TabbarController/TabbarController.swift | 5 +++- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 04bcfb3a..0eff7fb3 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -41,6 +41,7 @@ extension AppDelegate { DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } + DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -48,6 +49,7 @@ extension AppDelegate { @Dependency var userAPIRepository: UserAPIRepository @Dependency var popUpAPIRepository: PopUpAPIRepository @Dependency var commentAPIRepository: CommentAPIRepository + @Dependency var homeAPIRepository: HomeAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -55,5 +57,6 @@ extension AppDelegate { DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } + DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift index 54045b52..776ded93 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift @@ -43,7 +43,7 @@ final class HomeListReactor: Reactor { var disposeBag = DisposeBag() var popUpType: HomePopUpType - private let homeAPIUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) + private let homeAPIUseCase: HomeAPIUseCase private let userDefaultService = UserDefaultService() private let userAPIUseCase: UserAPIUseCase @@ -72,11 +72,13 @@ final class HomeListReactor: Reactor { // MARK: - init init( popUpType: HomePopUpType, - userAPIUseCase: UserAPIUseCase + userAPIUseCase: UserAPIUseCase, + homeAPIUseCase: HomeAPIUseCase ) { self.initialState = State(popUpType: popUpType) self.popUpType = popUpType self.userAPIUseCase = userAPIUseCase + self.homeAPIUseCase = homeAPIUseCase } // MARK: - Reactor Methods diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift index 9897ffc0..228abf0a 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift @@ -39,7 +39,7 @@ final class HomeReactor: Reactor { var disposeBag = DisposeBag() - private let homeApiUseCase = HomeAPIUseCaseImpl(repository: HomeAPIRepositoryImpl(provider: ProviderImpl())) + private let homeAPIUseCase: HomeAPIUseCase private let userAPIUseCase: UserAPIUseCase private let userDefaultService = UserDefaultService() @@ -84,8 +84,12 @@ final class HomeReactor: Reactor { private var spaceGray24Section = SpacingSection(inputDataList: [.init(spacing: 24, backgroundColor: .g700)]) // MARK: - init - init(userAPIUseCase: UserAPIUseCase) { + init( + userAPIUseCase: UserAPIUseCase, + homeAPIUseCase: HomeAPIUseCase + ) { self.userAPIUseCase = userAPIUseCase + self.homeAPIUseCase = homeAPIUseCase self.initialState = State() } @@ -95,7 +99,7 @@ final class HomeReactor: Reactor { case .changeIndicatorColor(let controller, let row): return Observable.just(.skip) case .viewWillAppear: - return homeApiUseCase.fetchHome(page: 0, size: 6, sort: "viewCount,desc") + return homeAPIUseCase.fetchHome(page: 0, size: 6, sort: "viewCount,desc") .withUnretained(self) .map { (owner, response) in owner.setBannerSection(response: response) @@ -285,7 +289,8 @@ final class HomeReactor: Reactor { let controller = HomeListController() controller.reactor = HomeListReactor( popUpType: .curation, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: homeAPIUseCase ) currentController.navigationController?.pushViewController(controller, animated: true) case 4: @@ -302,7 +307,8 @@ final class HomeReactor: Reactor { let controller = HomeListController() controller.reactor = HomeListReactor( popUpType: .popular, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: homeAPIUseCase ) currentController.navigationController?.pushViewController(controller, animated: true) case 9: @@ -319,7 +325,8 @@ final class HomeReactor: Reactor { let controller = HomeListController() controller.reactor = HomeListReactor( popUpType: .new, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: homeAPIUseCase ) currentController.navigationController?.pushViewController(controller, animated: true) case 14: @@ -352,7 +359,8 @@ final class HomeReactor: Reactor { let controller = HomeListController() controller.reactor = HomeListReactor( popUpType: .popular, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: homeAPIUseCase ) currentController.navigationController?.pushViewController(controller, animated: true) case 4: @@ -369,7 +377,8 @@ final class HomeReactor: Reactor { let controller = HomeListController() controller.reactor = HomeListReactor( popUpType: .new, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: homeAPIUseCase ) currentController.navigationController?.pushViewController(controller, animated: true) case 9: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 23f39005..084782dc 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -203,7 +203,8 @@ final class MyPageBookmarkReactor: Reactor { let nextController = HomeListController() nextController.reactor = HomeListReactor( popUpType: .curation, - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + homeAPIUseCase: DIContainer.resolve(HomeAPIUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index 2c6f55eb..e3210dbf 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -201,7 +201,10 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { mapController.reactor = MapReactor(mapUseCase: mapUseCase, directionRepository: directionRepository) let homeController = HomeController() - homeController.reactor = HomeReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + homeController.reactor = HomeReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + homeAPIUseCase: DIContainer.resolve(HomeAPIUseCase.self) + ) let myPageController = MyPageController() myPageController.reactor = MyPageReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) From 955d84840e124a052c006db606063b557361f618 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:50:04 +0900 Subject: [PATCH 128/393] =?UTF-8?q?refactor/#113:=20AuthAPIUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../Presentation/Scene/Detail/DetailReactor.swift | 12 +++++++++--- .../Scene/Login/Main/LoginReactor.swift | 11 +++++++---- .../Scene/Login/Sub/SubLoginReactor.swift | 11 +++++++---- .../Scene/MyPage/Main/MyPageReactor.swift | 4 +++- .../Scene/Splash/SplashController.swift | 14 +++++--------- 6 files changed, 34 insertions(+), 21 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 0eff7fb3..e3276914 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -42,6 +42,7 @@ extension AppDelegate { DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } + DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -50,6 +51,7 @@ extension AppDelegate { @Dependency var popUpAPIRepository: PopUpAPIRepository @Dependency var commentAPIRepository: CommentAPIRepository @Dependency var homeAPIRepository: HomeAPIRepository + @Dependency var authAPIRepository: AuthAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -58,5 +60,6 @@ extension AppDelegate { DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } + DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift index 7e938419..edbe9b36 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift @@ -161,7 +161,9 @@ final class DetailReactor: Reactor { controller.navigationController?.pushViewController(commentController, animated: true) } else { let loginController = SubLoginController() - loginController.reactor = SubLoginReactor() + loginController.reactor = SubLoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) @@ -193,7 +195,9 @@ final class DetailReactor: Reactor { controller.navigationController?.pushViewController(nextController, animated: true) } else { let loginController = SubLoginController() - loginController.reactor = SubLoginReactor() + loginController.reactor = SubLoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) @@ -227,7 +231,9 @@ final class DetailReactor: Reactor { controller.navigationController?.popViewController(animated: true) case .moveToLoginScene(let controller): let loginController = SubLoginController() - loginController.reactor = SubLoginReactor() + loginController.reactor = SubLoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen controller.present(nextController, animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift index d7963143..f972d93f 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift @@ -40,12 +40,15 @@ final class LoginReactor: Reactor { private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() - private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) + private let authAPIUseCase: AuthAPIUseCase @Dependency private var keyChainService: KeyChainService let userDefaultService = UserDefaultService() // MARK: - init - init() { + init( + authAPIUseCase: AuthAPIUseCase + ) { + self.authAPIUseCase = authAPIUseCase self.initialState = State() } @@ -93,7 +96,7 @@ final class LoginReactor: Reactor { return kakaoLoginService.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in - return owner.authApiUseCase.postTryLogin(userCredential: response, socialType: "kakao") + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") } .withUnretained(self) .map { [weak controller] (owner, loginResponse) in @@ -121,7 +124,7 @@ final class LoginReactor: Reactor { .withUnretained(self) .flatMap { owner, response in owner.authrizationCode = response.authorizationCode - return owner.authApiUseCase.postTryLogin(userCredential: response, socialType: "apple") + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) .map { [weak controller] (owner, loginResponse) in diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift index b51ec276..ca603d43 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -40,12 +40,15 @@ final class SubLoginReactor: Reactor { private let kakaoLoginService = KakaoLoginService() private var appleLoginService = AppleLoginService() - private let authApiUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) + private let authAPIUseCase: AuthAPIUseCase @Dependency private var keyChainService: KeyChainService let userDefaultService = UserDefaultService() // MARK: - init - init() { + init( + authAPIUseCase: AuthAPIUseCase + ) { + self.authAPIUseCase = authAPIUseCase self.initialState = State() } @@ -90,7 +93,7 @@ final class SubLoginReactor: Reactor { return kakaoLoginService.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in - owner.authApiUseCase.postTryLogin(userCredential: response, socialType: "kakao") + owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") } .withUnretained(self) .map { [weak controller] (owner, loginResponse) in @@ -118,7 +121,7 @@ final class SubLoginReactor: Reactor { .withUnretained(self) .flatMap { owner, response in owner.authrizationCode = response.authorizationCode - return owner.authApiUseCase.postTryLogin(userCredential: response, socialType: "apple") + return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) .map { [weak controller] (owner, loginResponse) in diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 5edd3778..92d62266 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -265,7 +265,9 @@ final class MyPageReactor: Reactor { case .moveToLoginScene(let controller): let nextController = SubLoginController() - nextController.reactor = SubLoginReactor() + nextController.reactor = SubLoginReactor( + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + ) let navigationController = UINavigationController(rootViewController: nextController) navigationController.modalPresentationStyle = .fullScreen controller.present(navigationController, animated: true) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift index 24ac8954..80cc2877 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift @@ -1,10 +1,3 @@ -// -// SplashController.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import UIKit import ReactorKit @@ -18,7 +11,8 @@ final class SplashController: BaseViewController { var disposeBag = DisposeBag() private var mainView = SplashView() - private let authAPIUseCase = AuthAPIUseCaseImpl(repository: AuthAPIRepositoryImpl(provider: ProviderImpl())) + // //FIXME: Reactor 태워서 UseCase 처리하도록 수정 + @Dependency private var authAPIUseCase: AuthAPIUseCase @Dependency private var keyChainService: KeyChainService private var rootViewController: UIViewController? @@ -65,7 +59,9 @@ private extension SplashController { }, onError: { [weak self] _ in guard let self = self else { return } let loginViewController = LoginController() - loginViewController.reactor = LoginReactor() + loginViewController.reactor = LoginReactor( + authAPIUseCase: authAPIUseCase + ) let loginNavigationController = UINavigationController(rootViewController: loginViewController) rootViewController = loginNavigationController }) From 514cad7fe92296dc2fffeda0d975173d2feb3469 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 16 Apr 2025 23:57:20 +0900 Subject: [PATCH 129/393] =?UTF-8?q?refactor/#113:=20PopUpAPIUseCase=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 --- .../Scene/Map/MapView/MapViewController.swift | 18 +++++++++++------- .../Map/StoreListView/StoreListReactor.swift | 7 ++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift index 762ef97f..e6d07180 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift @@ -32,10 +32,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var markerDictionary: [Int64: NMFMarker] = [:] private var individualMarkerDictionary: [Int64: NMFMarker] = [:] private var clusterMarkerDictionary: [String: NMFMarker] = [:] - // FIXME: Reactor 이용해서 처리하도록 수정 - private let popUpAPIUseCase = PopUpAPIUseCaseImpl( - repository: PopUpAPIRepositoryImpl(provider: ProviderImpl()) - ) + @Dependency private var popUpAPIUseCase: PopUpAPIUseCase private let clusteringManager = ClusteringManager() var currentStores: [MapPopUpStore] = [] var disposeBag = DisposeBag() @@ -43,9 +40,15 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let carouselView = MapPopupCarouselView() private let locationManager = CLLocationManager() var currentMarker: NMFMarker? - private let storeListReactor = StoreListReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + private let storeListReactor = StoreListReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + ) private let storeListViewController = StoreListViewController( - reactor: StoreListReactor(userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self)) + reactor: StoreListReactor( + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) + ) ) private var listViewTopConstraint: Constraint? private var currentFilterBottomSheet: FilterBottomSheetViewController? @@ -1235,7 +1238,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM stores.forEach { store in self.popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", - popUpStoredId: store.id + popUpStoredId: store.id, + isViewCount: true ) .asObservable() .observe(on: MainScheduler.instance) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index dba652c8..5204520c 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -6,7 +6,7 @@ import RxSwift final class StoreListReactor: Reactor { // MARK: - Reactor private let userAPIUseCase: UserAPIUseCase - private let popUpAPIUseCase: PopUpAPIUseCaseImpl + private let popUpAPIUseCase: PopUpAPIUseCase private let bookmarkStateRelay = PublishRelay<(Int64, Bool)>() // private var currentPage = 0 @@ -49,7 +49,7 @@ final class StoreListReactor: Reactor { init( userAPIUseCase: UserAPIUseCase, - popUpAPIUseCase: PopUpAPIUseCaseImpl = PopUpAPIUseCaseImpl(repository: PopUpAPIRepositoryImpl(provider: ProviderImpl())) + popUpAPIUseCase: PopUpAPIUseCase ) { self.userAPIUseCase = userAPIUseCase self.popUpAPIUseCase = popUpAPIUseCase @@ -82,7 +82,8 @@ final class StoreListReactor: Reactor { return popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", - popUpStoredId: Int64(idInt32) // Int32 → Int64 변환 + popUpStoredId: Int64(idInt32), // Int32 → Int64 변환 + isViewCount: true ) .flatMap { detail -> Observable in if detail.bookmarkYn != store.isBookmarked { From 588b5feec602230787c50531532e14dfa277c3a7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Apr 2025 00:06:19 +0900 Subject: [PATCH 130/393] =?UTF-8?q?refactor/#113:=20SignUpAPIUseCase=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 --- Poppool/Poppool/Application/AppDelegate.swift | 3 +++ .../Presentation/Scene/Login/Main/LoginReactor.swift | 6 +++++- .../Scene/Login/Sub/SubLoginReactor.swift | 6 +++++- .../Scene/MyPage/Main/MyPageReactor.swift | 5 ++++- .../CategoryEditModal/CategoryEditModalReactor.swift | 6 ++++-- .../MyPage/ProfileEdit/Main/ProfileEditReactor.swift | 11 ++++++++--- .../Scene/Search/BeforeSearch/SearchReactor.swift | 5 ++++- .../CategoryController/SearchCategoryReactor.swift | 8 ++++++-- .../Scene/SignUp/Main/SignUpMainController.swift | 8 ++++++-- .../Scene/SignUp/Main/SignUpMainReactor.swift | 11 +++++++---- .../Scene/SignUp/Step2/SignUpStep2Reactor.swift | 7 +++++-- .../Scene/SignUp/Step3/SignUpStep3Reactor.swift | 7 +++++-- 12 files changed, 62 insertions(+), 21 deletions(-) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index e3276914..71d4ab96 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -43,6 +43,7 @@ extension AppDelegate { DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } + DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -52,6 +53,7 @@ extension AppDelegate { @Dependency var commentAPIRepository: CommentAPIRepository @Dependency var homeAPIRepository: HomeAPIRepository @Dependency var authAPIRepository: AuthAPIRepository + @Dependency var signUpRepository: SignUpRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -61,5 +63,6 @@ extension AppDelegate { DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } + DIContainer.register(SignUpAPIUseCase.self) { return SignUpAPIUseCaseImpl(repository: signUpRepository) } } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift index f972d93f..6065f763 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift @@ -74,7 +74,11 @@ final class LoginReactor: Reactor { switch mutation { case .moveToSignUpScene(let controller): let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor(isFirstResponderCase: true, authrizationCode: authrizationCode) + 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() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift index ca603d43..71711306 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -72,7 +72,11 @@ final class SubLoginReactor: Reactor { switch mutation { case .moveToSignUpScene(let controller): let signUpController = SignUpMainController() - signUpController.reactor = SignUpMainReactor(isFirstResponderCase: false, authrizationCode: authrizationCode) + 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) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 92d62266..1b32e806 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -181,7 +181,10 @@ final class MyPageReactor: Reactor { case .moveToProfileEditScene(let controller): let nextController = ProfileEditController() - nextController.reactor = ProfileEditReactor(userAPIUseCase: userAPIUseCase) + nextController.reactor = ProfileEditReactor( + userAPIUseCase: userAPIUseCase, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) controller.navigationController?.pushViewController(nextController, animated: true) case .logout: diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index 518e80bf..5e1efdcb 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -52,17 +52,19 @@ final class CategoryEditModalReactor: Reactor { } }() - private var signUpUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private var signUpUseCase: SignUpAPIUseCase private var userAPIUseCase: UserAPIUseCase private var tagSection = TagSection(inputDataList: []) // MARK: - init init( selectedID: [Int64], - userAPIUseCase: UserAPIUseCase + userAPIUseCase: UserAPIUseCase, + signUpAPIUseCase: SignUpAPIUseCase ) { self.originSelectedID = selectedID self.userAPIUseCase = userAPIUseCase + self.signUpUseCase = signUpAPIUseCase self.initialState = State(originSelectedID: selectedID) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 9aa9f5e3..00b17647 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -65,12 +65,16 @@ final class ProfileEditReactor: Reactor { var introIsActive: Bool = false private let userAPIUseCase: UserAPIUseCase - private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private let signUpAPIUseCase: SignUpAPIUseCase private let imageService = PreSignedService() // MARK: - init - init(userAPIUseCase: UserAPIUseCase) { + init( + userAPIUseCase: UserAPIUseCase, + signUpAPIUseCase: SignUpAPIUseCase + ) { self.userAPIUseCase = userAPIUseCase + self.signUpAPIUseCase = signUpAPIUseCase self.initialState = State() } @@ -136,7 +140,8 @@ final class ProfileEditReactor: Reactor { let nextController = CategoryEditModalController() nextController.reactor = CategoryEditModalReactor( selectedID: newState.originProfileData?.interestCategoryList.map { $0.categoryId } ?? [], - userAPIUseCase: userAPIUseCase + userAPIUseCase: userAPIUseCase, + signUpAPIUseCase: signUpAPIUseCase ) controller.presentPanModal(nextController) case .moveToInfoEditScene(let controller): diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 960feda8..9e90a637 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -193,7 +193,10 @@ final class SearchReactor: Reactor { case .moveToCategoryScene(let controller): let categoryIDList = searchCategorySection.inputDataList.compactMap { $0.id } let nextController = SearchCategoryController() - nextController.reactor = SearchCategoryReactor(originCategoryList: categoryIDList) + nextController.reactor = SearchCategoryReactor( + originCategoryList: categoryIDList, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) controller.presentPanModal(nextController) nextController.reactor?.state .withUnretained(self) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift index 383d676f..66554ab7 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift @@ -43,7 +43,7 @@ final class SearchCategoryReactor: Reactor { var initialState: State var disposeBag = DisposeBag() var originCategoryList: [Int64] - private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private let signUpAPIUseCase: SignUpAPIUseCase private var tagSection = TagSection(inputDataList: []) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -60,9 +60,13 @@ final class SearchCategoryReactor: Reactor { }() // MARK: - init - init(originCategoryList: [Int64]) { + init( + originCategoryList: [Int64], + signUpAPIUseCase: SignUpAPIUseCase + ) { self.initialState = State() self.originCategoryList = originCategoryList + self.signUpAPIUseCase = signUpAPIUseCase } // MARK: - Reactor Methods diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift index 6fd26f5c..9e31c30e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift @@ -31,13 +31,17 @@ final class SignUpMainController: BaseTabmanController, View { var step2Controller: SignUpStep2Controller = { let controller = SignUpStep2Controller() - controller.reactor = SignUpStep2Reactor() + controller.reactor = SignUpStep2Reactor( + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) return controller }() var step3Controller: SignUpStep3Controller = { let controller = SignUpStep3Controller() - controller.reactor = SignUpStep3Reactor() + controller.reactor = SignUpStep3Reactor( + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) return controller }() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index 363a7ec0..c98f891e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -60,15 +60,20 @@ final class SignUpMainReactor: Reactor { private var authrizationCode: String? - private var signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private var signUpAPIUseCase: SignUpAPIUseCase private let userDefaultService = UserDefaultService() var isFirstResponderCase: Bool // MARK: - init - init(isFirstResponderCase: Bool, authrizationCode: String?) { + init( + isFirstResponderCase: Bool, + authrizationCode: String?, + signUpAPIUseCase: SignUpAPIUseCase + ) { self.initialState = State() self.authrizationCode = authrizationCode self.isFirstResponderCase = isFirstResponderCase + self.signUpAPIUseCase = signUpAPIUseCase } // MARK: - Reactor Methods @@ -122,8 +127,6 @@ final class SignUpMainReactor: Reactor { let nickName = newState.nickName, let gender = newState.gender else { return newState } - signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) - signUpAPIUseCase.trySignUp( nickName: nickName, gender: gender, diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift index 9604a80d..24f21cfb 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift @@ -39,11 +39,14 @@ final class SignUpStep2Reactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private let signUpAPIUseCase: SignUpAPIUseCase private var nickName: String? // MARK: - init - init() { + init( + signUpAPIUseCase: SignUpAPIUseCase + ) { + self.signUpAPIUseCase = signUpAPIUseCase self.initialState = State() } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 30fbc30b..142a0124 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -34,7 +34,7 @@ final class SignUpStep3Reactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let signUpAPIUseCase = SignUpAPIUseCaseImpl(repository: SignUpRepositoryImpl(provider: ProviderImpl())) + private let signUpAPIUseCase: SignUpAPIUseCase private var cetegoryIDList: [Int64] = [] lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -54,7 +54,10 @@ final class SignUpStep3Reactor: Reactor { private var categorySection: TagSection = TagSection(inputDataList: []) // MARK: - init - init() { + init( + signUpAPIUseCase: SignUpAPIUseCase + ) { + self.signUpAPIUseCase = signUpAPIUseCase self.initialState = State() } From 5a75bcfee913bea7439f064e3647ae40f3fc0529 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Apr 2025 00:15:19 +0900 Subject: [PATCH 131/393] =?UTF-8?q?refactor/#113:=20MapDirectionRepository?= =?UTF-8?q?=20=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 - repository의 메서드를 바로 끌어다쓰는 경우로 보임 - 제거되어야 되는 부분 - 반드시 UseCase를 태우도록 변경 필요 --- .../Presentation/Scene/Detail/DetailReactor.swift | 5 ++++- .../Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift | 8 ++++---- .../Map/FindMap/MapGuideView/MapGuideViewController.swift | 4 +--- .../Presentation/Scene/Map/MapView/MapReactor.swift | 8 ++++---- .../Scene/TabbarController/TabbarController.swift | 7 +++++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift index edbe9b36..181667ed 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift @@ -175,7 +175,10 @@ final class DetailReactor: Reactor { ToastMaker.createToast(message: "주소를 복사했어요") case .moveToAddressScene(let controller): let mapGuideController = MapGuideViewController(popUpStoreId: popUpID) - let reactor = MapGuideReactor(popUpStoreId: popUpID) + let reactor = MapGuideReactor( + popUpStoreId: popUpID, + mapDirectionRepository: DIContainer.resolve(MapDirectionRepository.self) + ) mapGuideController.reactor = reactor mapGuideController.modalPresentationStyle = .overCurrentContext diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index 551a396c..085787de 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -42,15 +42,15 @@ final class MapGuideReactor: Reactor { let initialState: State private let popUpStoreId: Int64 - private let directionRepository: MapDirectionRepository + private let mapDirectionRepository: MapDirectionRepository // MARK: - Init init( popUpStoreId: Int64, - repository: MapDirectionRepository = MapDirectionRepositoryImpl(provider: ProviderImpl()) + mapDirectionRepository: MapDirectionRepository ) { self.popUpStoreId = popUpStoreId - self.directionRepository = repository + self.mapDirectionRepository = mapDirectionRepository self.initialState = State() } @@ -70,7 +70,7 @@ final class MapGuideReactor: Reactor { return Observable.just(.navigateBack) case .viewDidLoad(let id): - return directionRepository.getPopUpDirection(popUpStoreId: id) + return mapDirectionRepository.getPopUpDirection(popUpStoreId: id) .map { response -> [Mutation] in return [ .setMap(CLLocationCoordinate2D(latitude: response.latitude, longitude: response.longitude)), diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 9e86e795..7c247580 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -174,11 +174,9 @@ final class MapGuideViewController: UIViewController, View { let providerInstance = ProviderImpl() @Dependency var mapUseCase: MapUseCase - // FIXME: Respository를 UseCase를 태우도록 변경 필요!! - let directionRepositoryInstance = MapDirectionRepositoryImpl(provider: providerInstance) let mapReactorInstance = MapReactor( mapUseCase: mapUseCase, - directionRepository: directionRepositoryInstance + mapDirectionRepository: DIContainer.resolve(MapDirectionRepository.self) ) if let selectedStore = strongSelf.currentCarouselStoreList.first { diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift index f1b599ac..d5d5d9b8 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift @@ -65,11 +65,11 @@ final class MapReactor: Reactor { let initialState: State private let mapUseCase: MapUseCase - private let directionRepository: MapDirectionRepository + private let mapDirectionRepository: MapDirectionRepository - init(mapUseCase: MapUseCase, directionRepository: MapDirectionRepository) { + init(mapUseCase: MapUseCase, mapDirectionRepository: MapDirectionRepository) { self.mapUseCase = mapUseCase - self.directionRepository = directionRepository + self.mapDirectionRepository = mapDirectionRepository self.initialState = State() } private func store(_ store: MapPopUpStore, matches filter: String) -> Bool { @@ -215,7 +215,7 @@ final class MapReactor: Reactor { } case .viewDidLoad(let id): - return directionRepository.getPopUpDirection(popUpStoreId: id) + return mapDirectionRepository.getPopUpDirection(popUpStoreId: id) .do( onNext: { _ in }, diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index e3210dbf..00e8ab50 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -197,8 +197,11 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { let mapController = MapViewController() @Dependency var mapUseCase: MapUseCase - let directionRepository = MapDirectionRepositoryImpl(provider: provider) - mapController.reactor = MapReactor(mapUseCase: mapUseCase, directionRepository: directionRepository) + @Dependency var mapDirectionRepository: MapDirectionRepository + mapController.reactor = MapReactor( + mapUseCase: mapUseCase, + mapDirectionRepository: mapDirectionRepository + ) let homeController = HomeController() homeController.reactor = HomeReactor( From 235a2272800c00fb72e3541dfd5f09f14a1d9bd6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Apr 2025 00:35:58 +0900 Subject: [PATCH 132/393] =?UTF-8?q?remove/#113:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=9D=B8?= =?UTF-8?q?=EC=8A=A4=ED=84=B4=EC=8A=A4=20=EC=83=9D=EC=84=B1=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 - UseCase를 불필요하게 ViewController에서 사용할 경우를 방지 - 불필요한 인스턴스를 제거하여 메모리 확보 --- .../AdminRegister/PopUpStoreRegisterViewController.swift | 5 +---- .../Presentation/Scene/Admin/AdminViewController.swift | 6 +----- .../FindMap/MapGuideView/MapGuideViewController.swift | 5 +---- .../Scene/SignUp/Main/SignUpMainReactor.swift | 2 +- .../Scene/TabbarController/TabbarController.swift | 9 +++------ 5 files changed, 7 insertions(+), 20 deletions(-) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 4f7c9c83..ed6fd4d0 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -11,7 +11,6 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Properties private var pickerViewController: PHPickerViewController? - private let adminUseCase: AdminUseCase private let nickname: String var completionHandler: (() -> Void)? var disposeBag = DisposeBag() @@ -21,18 +20,16 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Initializer init( nickname: String, - adminUseCase: AdminUseCase, editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil ) { self.nickname = nickname - self.adminUseCase = adminUseCase self.mainView = PopUpRegisterView() super.init() let presignedService = PreSignedService() let reactor = PopUpStoreRegisterReactor( - adminUseCase: adminUseCase, + adminUseCase: DIContainer.resolve(AdminUseCase.self), presignedService: presignedService, editingStore: editingStore ) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift index a3a21e31..45de4fb4 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift @@ -172,7 +172,6 @@ final class AdminViewController: BaseViewController, View { private func editStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { let registerVC = PopUpStoreRegisterViewController( nickname: nickname, - adminUseCase: adminUseCase, editingStore: store ) @@ -248,10 +247,7 @@ final class AdminViewController: BaseViewController, View { mainView.registerButton.rx.tap .subscribe(onNext: { [weak self] _ in guard let self = self else { return } - let registerVC = PopUpStoreRegisterViewController( - nickname: self.nickname, - adminUseCase: self.adminUseCase - ) + let registerVC = PopUpStoreRegisterViewController(nickname: self.nickname) registerVC.completionHandler = { [weak self] in self?.reactor?.action.onNext(.reloadData) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 7c247580..9f45b079 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -171,11 +171,8 @@ final class MapGuideViewController: UIViewController, View { .subscribe(onNext: { [weak self] in guard let strongSelf = self else { return } - let providerInstance = ProviderImpl() - @Dependency var mapUseCase: MapUseCase - let mapReactorInstance = MapReactor( - mapUseCase: mapUseCase, + mapUseCase: DIContainer.resolve(MapUseCase.self), mapDirectionRepository: DIContainer.resolve(MapDirectionRepository.self) ) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index c98f891e..adeaa99e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -60,7 +60,7 @@ final class SignUpMainReactor: Reactor { private var authrizationCode: String? - private var signUpAPIUseCase: SignUpAPIUseCase + private let signUpAPIUseCase: SignUpAPIUseCase private let userDefaultService = UserDefaultService() var isFirstResponderCase: Bool diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift index 00e8ab50..4be350da 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift @@ -193,14 +193,11 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { } func addSomeTabItems() { - let provider = ProviderImpl() - let mapController = MapViewController() - @Dependency var mapUseCase: MapUseCase - @Dependency var mapDirectionRepository: MapDirectionRepository + mapController.reactor = MapReactor( - mapUseCase: mapUseCase, - mapDirectionRepository: mapDirectionRepository + mapUseCase: DIContainer.resolve(MapUseCase.self), + mapDirectionRepository: DIContainer.resolve(MapDirectionRepository.self) ) let homeController = HomeController() From add928b105c1eba89c72644db44189495a1b1285 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 17 Apr 2025 16:43:41 +0900 Subject: [PATCH 133/393] =?UTF-8?q?fix/#113:=20MapDirectionRepository=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Application/AppDelegate.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 71d4ab96..471f00c9 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -44,6 +44,7 @@ extension AppDelegate { DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } + DIContainer.register(MapDirectionRepository.self) { return MapDirectionRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository From 71dd5ac1a3e7eda0e9eb3ce060504a5b620ef6cd Mon Sep 17 00:00:00 2001 From: JunYoung Date: Thu, 17 Apr 2025 21:15:14 +0900 Subject: [PATCH 134/393] =?UTF-8?q?refactor/#116:=20HomeAPI=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20Domain=20Layer=20DTO=20=EC=A0=9C=EA=B1=B0=20-=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryImpl/HomeAPIRepositoryImpl.swift | 12 ++++++++---- .../Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift | 8 ++++---- .../Repository/HomeAPIRepository.swift | 8 ++++---- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift index 89ef1e10..2e432b88 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -11,22 +11,26 @@ final class HomeAPIRepositoryImpl: HomeAPIRepository { self.provider = provider } - func fetchHome(request: SortedRequestDTO) -> Observable { + func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchHome(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchCustomPopUp(request: SortedRequestDTO) -> Observable { + func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchCustomPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchNewPopUp(request: SortedRequestDTO) -> Observable { + func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchNewPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchPopularPopUp(request: SortedRequestDTO) -> Observable { + func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchPopularPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index cd665ca5..2251ba97 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -15,7 +15,7 @@ final class HomeAPIUseCaseImpl: HomeAPIUseCase { size: Int32?, sort: String? ) -> Observable { - return repository.fetchHome(request: .init(page: page, size: size, sort: sort)) + return repository.fetchHome(page: page, size: size, sort: sort) } func fetchCustomPopUp( @@ -23,7 +23,7 @@ final class HomeAPIUseCaseImpl: HomeAPIUseCase { size: Int32?, sort: String? ) -> Observable { - return repository.fetchCustomPopUp(request: .init(page: page, size: size, sort: sort)) + return repository.fetchCustomPopUp(page: page, size: size, sort: sort) } func fetchNewPopUp( @@ -31,7 +31,7 @@ final class HomeAPIUseCaseImpl: HomeAPIUseCase { size: Int32?, sort: String? ) -> Observable { - return repository.fetchNewPopUp(request: .init(page: page, size: size, sort: sort)) + return repository.fetchNewPopUp(page: page, size: size, sort: sort) } func fetchPopularPopUp( @@ -39,6 +39,6 @@ final class HomeAPIUseCaseImpl: HomeAPIUseCase { size: Int32?, sort: String? ) -> Observable { - return repository.fetchPopularPopUp(request: .init(page: page, size: size, sort: sort)) + return repository.fetchPopularPopUp(page: page, size: size, sort: sort) } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift index 2c6082ed..4b7f0bb1 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift @@ -3,8 +3,8 @@ import Foundation import RxSwift protocol HomeAPIRepository { - func fetchHome(request: SortedRequestDTO) -> Observable - func fetchCustomPopUp(request: SortedRequestDTO) -> Observable - func fetchNewPopUp(request: SortedRequestDTO) -> Observable - func fetchPopularPopUp(request: SortedRequestDTO) -> Observable + func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable } From fa9db31f7b2c124a010d0029c20f5bef796ccdc1 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Thu, 17 Apr 2025 22:23:49 +0900 Subject: [PATCH 135/393] =?UTF-8?q?refactor/#116:=20PopUpAPI=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20Domain=20Layer=20DTO=20=EC=A0=9C=EA=B1=B0=20-=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopUpAPIRepositoryImpl.swift | 49 ++++++++++++++----- .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 11 ++--- .../Repository/PopUpAPIRepository.swift | 44 ++++++++++++++--- 3 files changed, 81 insertions(+), 23 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift index b20a108a..ab8e22d6 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -11,33 +11,60 @@ final class PopUpAPIRepositoryImpl: PopUpAPIRepository { self.provider = provider } - func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { + func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { + let request = PostBookmarkPopUpRequestDTO(popUpStoreId: popUpStoreId) let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { + func getClosePopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable { + let request = GetSearchPopUpListRequestDTO(categories: categories, page: page, size: size, sort: sort, query: query, sortCode: sortCode) let endPoint = PopUpAPIEndPoint.getClosePopUpList(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { + func getOpenPopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable { + let request = GetSearchPopUpListRequestDTO(categories: categories, page: page, size: size, sort: sort, query: query, sortCode: sortCode) let endPoint = PopUpAPIEndPoint.getOpenPopUpList(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable { + func getSearchPopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable { + let request = GetSearchPopUpListRequestDTO(categories: categories, page: page, size: size, sort: sort, query: query, sortCode: sortCode) let endPoint = PopUpAPIEndPoint.getSearchPopUpList(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Observable { + func getPopUpDetail(commentType: String?, popUpStoreId: Int64, viewCountYn: Bool?) -> Observable { + let request = GetPopUpDetailRequestDTO(commentType: commentType, popUpStoreId: popUpStoreId, viewCountYn: viewCountYn) let endPoint = PopUpAPIEndPoint.getPopUpDetail(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Observable { + func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { + let request = GetPopUpCommentRequestDTO(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) let endPoint = PopUpAPIEndPoint.getPopUpComment(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index 8049526a..5bd701ce 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -15,24 +15,23 @@ final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { if !categories.isEmpty { categoryString = categories.map { String($0) + "," }.reduce("", +) } - let request = GetSearchPopUpListRequestDTO(categories: categoryString, page: page, size: size, sortCode: sort) if isOpen { - return repository.getOpenPopUpList(request: request).map { $0.toDomain() } + return repository.getOpenPopUpList(categories: categoryString, page: page, size: size, sort: nil, query: nil, sortCode: sort) } else { - return repository.getClosePopUpList(request: request).map { $0.toDomain() } + return repository.getClosePopUpList(categories: categoryString, page: page, size: size, sort: nil, query: nil, sortCode: sort) } } func getSearchPopUpList(query: String?) -> Observable { - return repository.getSearchPopUpList(request: .init(query: query)).map { $0.toDomain() } + return repository.getSearchPopUpList(categories: nil, page: nil, size: nil, sort: nil, query: query, sortCode: nil) } func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool? = true) -> Observable { - return repository.getPopUpDetail(request: .init(commentType: commentType, popUpStoreId: popUpStoredId, viewCountYn: isViewCount)).map { $0.toDomain() } + return repository.getPopUpDetail(commentType: commentType, popUpStoreId: popUpStoredId, viewCountYn: isViewCount) } func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { let request: GetPopUpCommentRequestDTO = .init(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) - return repository.getPopUpComment(request: request).map { $0.toDomain() } + return repository.getPopUpComment(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift index 4cec5864..d03a9666 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift @@ -3,10 +3,42 @@ import Foundation import RxSwift protocol PopUpAPIRepository { - func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable - func getClosePopUpList(request: GetSearchPopUpListRequestDTO) -> Observable - func getOpenPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable - func getSearchPopUpList(request: GetSearchPopUpListRequestDTO) -> Observable - func getPopUpDetail(request: GetPopUpDetailRequestDTO) -> Observable - func getPopUpComment(request: GetPopUpCommentRequestDTO) -> Observable + func postBookmarkPopUp(popUpStoreId: Int64) -> Completable + + func getClosePopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable + + func getOpenPopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable + + func getSearchPopUpList( + categories: String?, + page: Int32?, + size: Int32?, + sort: String?, + query: String?, + sortCode: String? + ) -> Observable + + func getPopUpDetail(commentType: String?, popUpStoreId: Int64, viewCountYn: Bool?) -> Observable + + func getPopUpComment( + commentType: String?, + page: Int32?, + size: Int32?, + sort: String?, + popUpStoreId: Int64 + ) -> Observable } From c34382d55bfb2826584be670420668171d9bdb7a Mon Sep 17 00:00:00 2001 From: JunYoung Date: Thu, 17 Apr 2025 23:05:29 +0900 Subject: [PATCH 136/393] =?UTF-8?q?refactor/#116:=20HomeAPI=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20Domain=20Layer=20DTO=20=EC=A0=9C=EA=B1=B0=20-=20?= =?UTF-8?q?=EB=B9=8C=EB=93=9C=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../GetBlockUserListRequestDTO.swift | 14 --- .../Network/API/UserAPI/UserAPIEndPoint.swift | 2 +- .../UserAPIRepositoryImpl.swift | 107 +++++++++++------- .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 2 +- .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/UserAPIUseCaseImpl.swift | 56 +++++---- .../Repository/UserAPIRepository.swift | 62 ++++++---- 7 files changed, 141 insertions(+), 106 deletions(-) delete mode 100644 Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift deleted file mode 100644 index a06c73c1..00000000 --- a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetBlockUserListRequestDTO.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// GetBlockUserListRequestDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - -import Foundation - -struct GetBlockUserListRequestDTO: Encodable { - var page: Int32? - var size: Int32? - var sort: String? -} diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift index 032c756e..3ddc45e1 100644 --- a/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift @@ -151,7 +151,7 @@ struct UserAPIEndPoint { ) } - static func getBlockUserList(request: GetBlockUserListRequestDTO) -> Endpoint { + static func getBlockUserList(request: SortedRequestDTO) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/users/blocked", diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift index a146c460..d52ddf0b 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -11,44 +11,51 @@ final class UserAPIRepositoryImpl: UserAPIRepository { self.provider = provider } - func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: request) + func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { + let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: .init(popUpStoreId: popUpStoreId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.deleteBookmarkPopUp(request: request) + func deleteBookmarkPopUp(popUpStoreId: Int64) -> Completable { + let endPoint = UserAPIEndPoint.deleteBookmarkPopUp(request: .init(popUpStoreId: popUpStoreId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func postCommentLike(request: CommentLikeRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.postCommentLike(request: request) + func postCommentLike(commentId: Int64) -> Completable { + let endPoint = UserAPIEndPoint.postCommentLike(request: .init(commentId: commentId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteCommentLike(request: CommentLikeRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.deleteCommentLike(request: request) + func deleteCommentLike(commentId: Int64) -> Completable { + let endPoint = UserAPIEndPoint.deleteCommentLike(request: .init(commentId: commentId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func postUserBlock(request: PostUserBlockRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.postUserBlock(request: request) + func postUserBlock(blockedUserId: String?) -> Completable { + let endPoint = UserAPIEndPoint.postUserBlock(request: .init(blockedUserId: blockedUserId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteUserBlock(request: PostUserBlockRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.deleteUserBlock(request: request) + func deleteUserBlock(blockedUserId: String?) -> Completable { + let endPoint = UserAPIEndPoint.deleteUserBlock(request: .init(blockedUserId: blockedUserId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getOtherUserCommentList(request: GetOtherUserCommentListRequestDTO) -> Observable { + func getOtherUserCommentList( + commenterId: String?, + commentType: String?, + page: Int32?, + size: Int32?, + sort: String? + ) -> Observable { + let request = GetOtherUserCommentListRequestDTO(commenterId: commenterId, commentType: commentType, page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getOtherUserCommentPopUpList(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getMyPage() -> Observable { + func getMyPage() -> Observable { let endPoint = UserAPIEndPoint.getMyPage() - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } func postLogout() -> Completable { @@ -56,63 +63,87 @@ final class UserAPIRepositoryImpl: UserAPIRepository { return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getWithdrawlList() -> Observable { + func getWithdrawlList() -> Observable { let endPoint = UserAPIEndPoint.getWithdrawlList() - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func postWithdrawl(request: PostWithdrawlListRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.postWithdrawl(request: request) + func postWithdrawl(list: [(Int64, String?)]) -> Completable { + let endPoint = UserAPIEndPoint.postWithdrawl(request: .init(checkedSurveyList: list.map { .init(id: $0.0, survey: $0.1)})) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getMyProfile() -> Observable { + func getMyProfile() -> Observable { let endPoint = UserAPIEndPoint.getMyProfile() - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> Completable { - let endPoint = UserAPIEndPoint.putUserTailoredInfo(request: request) + func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { + let endPoint = UserAPIEndPoint.putUserTailoredInfo(request: .init(gender: gender, age: age)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func putUserCategory(request: PutUserCategoryRequestDTO) -> Completable { + func putUserCategory( + interestCategoriesToAdd: [Int64], + interestCategoriesToDelete: [Int64], + interestCategoriesToKeep: [Int64] + ) -> Completable { + let request = PutUserCategoryRequestDTO( + interestCategoriesToAdd: interestCategoriesToAdd, + interestCategoriesToDelete: interestCategoriesToDelete, + interestCategoriesToKeep: interestCategoriesToKeep + ) let endPoint = UserAPIEndPoint.putUserCategory(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func putUserProfile(request: PutUserProfileRequestDTO) -> Completable { + func putUserProfile( + profileImageUrl: String?, + nickname: String?, + email: String?, + instagramId: String?, + intro: String? + ) -> Completable { + let request = PutUserProfileRequestDTO(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro) let endPoint = UserAPIEndPoint.putUserProfile(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getMyCommentedPopUp(request: SortedRequestDTO) -> Observable { + func getMyCommentedPopUp( + page: Int32?, + size: Int32?, + sort: String? + ) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getMyCommentedPopUp(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getBlockUserList(request: GetBlockUserListRequestDTO) -> Observable { + func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBlockUserList(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getNoticeList() -> Observable { + func getNoticeList() -> Observable { let endPoint = UserAPIEndPoint.getNoticeList() - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getNoticeDetail(noticeID: Int64) -> Observable { + func getNoticeDetail(noticeID: Int64) -> Observable { let endPoint = UserAPIEndPoint.getNoticeDetail(noticeID: noticeID) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getRecentPopUp(request: SortedRequestDTO) -> Observable { + func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getRecentPopUp(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getBookmarkPopUp(request: SortedRequestDTO) -> Observable { + func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + let request = SortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBookmarkPopUp(request: request) - return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index 5bd701ce..4e53f095 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -31,7 +31,7 @@ final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { } func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { - let request: GetPopUpCommentRequestDTO = .init(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) + let request = GetPopUpCommentRequestDTO(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) return repository.getPopUpComment(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index 9f91f723..78c69f75 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -8,7 +8,7 @@ final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { init(repository: SignUpRepository) { self.repository = repository } - + func trySignUp( nickName: String, gender: String, @@ -28,7 +28,7 @@ final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { appleAuthorizationCode: appleAuthorizationCode ) } - + func checkNickName(nickName: String) -> Observable { return repository.checkNickName(nickName: nickName) } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index 55a16028..d13ea254 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -10,27 +10,27 @@ final class UserAPIUseCaseImpl: UserAPIUseCase { } func postBookmarkPopUp(popUpID: Int64) -> Completable { - return repository.postBookmarkPopUp(request: .init(popUpStoreId: popUpID)) + return repository.postBookmarkPopUp(popUpStoreId: popUpID) } func deleteBookmarkPopUp(popUpID: Int64) -> Completable { - return repository.deleteBookmarkPopUp(request: .init(popUpStoreId: popUpID)) + return repository.deleteBookmarkPopUp(popUpStoreId: popUpID) } func postCommentLike(commentId: Int64) -> Completable { - return repository.postCommentLike(request: .init(commentId: commentId)) + return repository.postCommentLike(commentId: commentId) } func deleteCommentLike(commentId: Int64) -> Completable { - return repository.deleteCommentLike(request: .init(commentId: commentId)) + return repository.deleteCommentLike(commentId: commentId) } func postUserBlock(blockedUserId: String?) -> Completable { - return repository.postUserBlock(request: .init(blockedUserId: blockedUserId)) + return repository.postUserBlock(blockedUserId: blockedUserId) } func deleteUserBlock(blockedUserId: String?) -> Completable { - return repository.deleteUserBlock(request: .init(blockedUserId: blockedUserId)) + return repository.deleteUserBlock(blockedUserId: blockedUserId) } func getOtherUserCommentedPopUpList( @@ -41,18 +41,16 @@ final class UserAPIUseCaseImpl: UserAPIUseCase { sort: String? ) -> Observable { return repository.getOtherUserCommentList( - request: .init( - commenterId: commenterId, - commentType: commentType, - page: page, - size: size, - sort: sort) + commenterId: commenterId, + commentType: commentType, + page: page, + size: size, + sort: sort ) - .map { $0.toDomain() } } func getMyPage() -> Observable { - return repository.getMyPage().map { $0.toDomain() } + return repository.getMyPage() } func postLogout() -> Completable { @@ -60,19 +58,19 @@ final class UserAPIUseCaseImpl: UserAPIUseCase { } func getWithdrawlList() -> Observable { - return repository.getWithdrawlList().map { $0.toDomain() } + return repository.getWithdrawlList() } func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable { - return repository.postWithdrawl(request: .init(checkedSurveyList: surveyList.map { .init(id: $0.id, survey: $0.survey)})) + return repository.postWithdrawl(list: surveyList.map { ($0.id, $0.survey)}) } func getMyProfile() -> Observable { - return repository.getMyProfile().map { $0.toDomain() } + return repository.getMyProfile() } func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { - return repository.putUserTailoredInfo(request: .init(gender: gender, age: age)) + return repository.putUserTailoredInfo(gender: gender, age: age) } func putUserCategory( @@ -81,39 +79,37 @@ final class UserAPIUseCaseImpl: UserAPIUseCase { interestCategoriesToKeep: [Int64] ) -> Completable { return repository.putUserCategory( - request: .init( - interestCategoriesToAdd: interestCategoriesToAdd, - interestCategoriesToDelete: interestCategoriesToDelete, - interestCategoriesToKeep: interestCategoriesToKeep - ) + interestCategoriesToAdd: interestCategoriesToAdd, + interestCategoriesToDelete: interestCategoriesToDelete, + interestCategoriesToKeep: interestCategoriesToKeep ) } func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable { - return repository.putUserProfile(request: .init(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro)) + return repository.putUserProfile(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro) } func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - return repository.getMyCommentedPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } + return repository.getMyCommentedPopUp(page: page, size: size, sort: sort) } func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { - return repository.getBlockUserList(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } + return repository.getBlockUserList(page: page, size: size, sort: sort) } func getNoticeList() -> Observable { - return repository.getNoticeList().map { $0.toDomain() } + return repository.getNoticeList() } func getNoticeDetail(noticeID: Int64) -> Observable { - return repository.getNoticeDetail(noticeID: noticeID).map { $0.toDomain() } + return repository.getNoticeDetail(noticeID: noticeID) } func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - return repository.getRecentPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } + return repository.getRecentPopUp(page: page, size: size, sort: sort) } func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - return repository.getBookmarkPopUp(request: .init(page: page, size: size, sort: sort)).map { $0.toDomain() } + return repository.getBookmarkPopUp(page: page, size: size, sort: sort) } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift index 5d37ef5f..3012787d 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift @@ -3,25 +3,47 @@ import Foundation import RxSwift protocol UserAPIRepository { - func postBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable - func deleteBookmarkPopUp(request: PostBookmarkPopUpRequestDTO) -> Completable - func postCommentLike(request: CommentLikeRequestDTO) -> Completable - func deleteCommentLike(request: CommentLikeRequestDTO) -> Completable - func postUserBlock(request: PostUserBlockRequestDTO) -> Completable - func deleteUserBlock(request: PostUserBlockRequestDTO) -> Completable - func getOtherUserCommentList(request: GetOtherUserCommentListRequestDTO) -> Observable - func getMyPage() -> Observable + func postBookmarkPopUp(popUpStoreId: Int64) -> Completable + func deleteBookmarkPopUp(popUpStoreId: Int64) -> Completable + func postCommentLike(commentId: Int64) -> Completable + func deleteCommentLike(commentId: Int64) -> Completable + func postUserBlock(blockedUserId: String?) -> Completable + func deleteUserBlock(blockedUserId: String?) -> Completable + + func getOtherUserCommentList( + commenterId: String?, + commentType: String?, + page: Int32?, + size: Int32?, + sort: String? + ) -> Observable + + func getMyPage() -> Observable func postLogout() -> Completable - func getWithdrawlList() -> Observable - func postWithdrawl(request: PostWithdrawlListRequestDTO) -> Completable - func getMyProfile() -> Observable - func putUserTailoredInfo(request: PutUserTailoredInfoRequestDTO) -> Completable - func putUserCategory(request: PutUserCategoryRequestDTO) -> Completable - func putUserProfile(request: PutUserProfileRequestDTO) -> Completable - func getMyCommentedPopUp(request: SortedRequestDTO) -> Observable - func getBlockUserList(request: GetBlockUserListRequestDTO) -> Observable - func getNoticeList() -> Observable - func getNoticeDetail(noticeID: Int64) -> Observable - func getRecentPopUp(request: SortedRequestDTO) -> Observable - func getBookmarkPopUp(request: SortedRequestDTO) -> Observable + func getWithdrawlList() -> Observable + func postWithdrawl(list: [(Int64, String?)]) -> Completable + + func getMyProfile() -> Observable + func putUserTailoredInfo(gender: String?, age: Int32) -> Completable + + func putUserCategory( + interestCategoriesToAdd: [Int64], + interestCategoriesToDelete: [Int64], + interestCategoriesToKeep: [Int64] + ) -> Completable + + func putUserProfile( + profileImageUrl: String?, + nickname: String?, + email: String?, + instagramId: String?, + intro: String? + ) -> Completable + + func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable + func getNoticeList() -> Observable + func getNoticeDetail(noticeID: Int64) -> Observable + func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable + func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable } From 64bc196be86fb5f790ce025316118ea6080dfb7d Mon Sep 17 00:00:00 2001 From: JunYoung Date: Thu, 17 Apr 2025 23:15:24 +0900 Subject: [PATCH 137/393] =?UTF-8?q?refactor/#116:=20SortedRequestDTO=20Hom?= =?UTF-8?q?e,=20User=20=EB=B6=84=EB=A6=AC=20-=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/HomeAPI/HomeAPIEndpoint.swift | 15 ++++----------- .../RequestDTO/HomeSortedRequestDTO.swift | 7 +++++++ .../DataLayer/Network/API/SortedRequestDTO.swift | 16 ---------------- .../RequesetDTO/UserSortedRequestDTO.swift | 7 +++++++ .../Network/API/UserAPI/UserAPIEndPoint.swift | 8 ++++---- .../RepositoryImpl/HomeAPIRepositoryImpl.swift | 8 ++++---- .../RepositoryImpl/UserAPIRepositoryImpl.swift | 8 ++++---- .../Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift | 1 - 8 files changed, 30 insertions(+), 40 deletions(-) create mode 100644 Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift delete mode 100644 Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift create mode 100644 Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift index accb193d..1d38114f 100644 --- a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift @@ -1,16 +1,9 @@ -// -// HomeRepositoryImpl.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation struct HomeAPIEndpoint { static func fetchHome( - request: SortedRequestDTO + request: HomeSortedRequestDTO ) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, @@ -21,7 +14,7 @@ struct HomeAPIEndpoint { } static func fetchPopularPopUp( - request: SortedRequestDTO + request: HomeSortedRequestDTO ) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, @@ -32,7 +25,7 @@ struct HomeAPIEndpoint { } static func fetchNewPopUp( - request: SortedRequestDTO + request: HomeSortedRequestDTO ) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, @@ -43,7 +36,7 @@ struct HomeAPIEndpoint { } static func fetchCustomPopUp( - request: SortedRequestDTO + request: HomeSortedRequestDTO ) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift new file mode 100644 index 00000000..1f7b5f72 --- /dev/null +++ b/Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift @@ -0,0 +1,7 @@ +import Foundation + +struct HomeSortedRequestDTO: Encodable { + var page: Int32? + var size: Int32? + var sort: String? +} diff --git a/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift deleted file mode 100644 index 14ecf6d6..00000000 --- a/Poppool/Poppool/DataLayer/Network/API/SortedRequestDTO.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// SortedRequestDTO.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - -// TODO: SortedRequestDTO를 HOME, User로 나눠서 폴더에 넣어주기 - -import Foundation - -struct SortedRequestDTO: Encodable { - var page: Int32? - var size: Int32? - var sort: String? -} diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift new file mode 100644 index 00000000..aee72ce5 --- /dev/null +++ b/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift @@ -0,0 +1,7 @@ +import Foundation + +struct UserSortedRequestDTO: Encodable { + var page: Int32? + var size: Int32? + var sort: String? +} diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift b/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift index 3ddc45e1..eca9ac14 100644 --- a/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift @@ -142,7 +142,7 @@ struct UserAPIEndPoint { ) } - static func getMyCommentedPopUp(request: SortedRequestDTO) -> Endpoint { + static func getMyCommentedPopUp(request: UserSortedRequestDTO) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/users/commented/popup", @@ -151,7 +151,7 @@ struct UserAPIEndPoint { ) } - static func getBlockUserList(request: SortedRequestDTO) -> Endpoint { + static func getBlockUserList(request: UserSortedRequestDTO) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/users/blocked", @@ -176,7 +176,7 @@ struct UserAPIEndPoint { ) } - static func getRecentPopUp(request: SortedRequestDTO) -> Endpoint { + static func getRecentPopUp(request: UserSortedRequestDTO) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/users/recent-popupstores", @@ -185,7 +185,7 @@ struct UserAPIEndPoint { ) } - static func getBookmarkPopUp(request: SortedRequestDTO) -> Endpoint { + static func getBookmarkPopUp(request: UserSortedRequestDTO) -> Endpoint { return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/users/bookmark-popupstores", diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift index 2e432b88..bd4d02e2 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -12,25 +12,25 @@ final class HomeAPIRepositoryImpl: HomeAPIRepository { } func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchHome(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchCustomPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchNewPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchPopularPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift index d52ddf0b..c8d3c00b 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -114,13 +114,13 @@ final class UserAPIRepositoryImpl: UserAPIRepository { size: Int32?, sort: String? ) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getMyCommentedPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBlockUserList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } @@ -136,13 +136,13 @@ final class UserAPIRepositoryImpl: UserAPIRepository { } func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getRecentPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { - let request = SortedRequestDTO(page: page, size: size, sort: sort) + let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBookmarkPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index 4e53f095..c199fddb 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -31,7 +31,6 @@ final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { } func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { - let request = GetPopUpCommentRequestDTO(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) return repository.getPopUpComment(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) } } From 8be3180472bc223a2287d56ea552be46bf490bef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 02:30:07 +0900 Subject: [PATCH 138/393] =?UTF-8?q?refactor/#117:=20Admin=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=A0=88=EC=9D=B4=EC=96=B4=20DTO=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20Params=20=EA=B0=9D=EC=B2=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryImpl/AdminRepositoryImpl.swift | 189 +++++++++--------- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 44 ++-- .../Entity/AdminResponse/AdminStore.swift | 8 + .../AdminResponse/AdminStoreDetail.swift | 26 +++ .../AdminResponse/Params/AdminParams.swift | 49 +++++ .../Repository/AdminRepository.swift | 21 +- .../UseCase/AdminUseCase.swift | 22 +- .../Scene/Admin/AdminReactor.swift | 12 +- .../PopUpStoreRegisterReactor.swift | 67 +++---- .../Scene/Admin/AdminViewController.swift | 66 ++++-- .../Scene/MyPage/Main/MyPageReactor.swift | 11 +- 11 files changed, 305 insertions(+), 210 deletions(-) create mode 100644 Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift create mode 100644 Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift create mode 100644 Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift index 71de6881..46cf88d8 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift @@ -1,5 +1,4 @@ import Foundation - import Alamofire import RxSwift @@ -15,7 +14,7 @@ final class AdminRepositoryImpl: AdminRepository { } // MARK: - Store Methods - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { let endpoint = AdminAPIEndpoint.fetchStoreList( query: query, page: page, @@ -25,131 +24,135 @@ final class AdminRepositoryImpl: AdminRepository { with: endpoint, interceptor: tokenInterceptor ) + .map { response in + response.popUpStoreList?.map { + AdminStore(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl) + } ?? [] + } } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { let endpoint = AdminAPIEndpoint.fetchStoreDetail(id: id) return provider.requestData( with: endpoint, interceptor: tokenInterceptor ) + .map { dto in + AdminStoreDetail( + id: dto.id, + name: dto.name, + categoryId: dto.categoryId, + categoryName: dto.categoryName, + description: dto.desc, + address: dto.address, + startDate: dto.startDate, + endDate: dto.endDate, + createUserId: dto.createUserId, + createDateTime: dto.createDateTime, + mainImageUrl: dto.mainImageUrl, + bannerYn: dto.bannerYn, + images: dto.imageList.map { + AdminStoreDetail.StoreImage( + id: $0.id, + imageUrl: $0.imageUrl + ) + }, + latitude: dto.latitude, + longitude: dto.longitude, + markerTitle: dto.markerTitle, + markerSnippet: dto.markerSnippet + ) + } .catch { error in if case .responseSerializationFailed = error as? AFError { - // 빈 데이터 응답시 기본값 반환 - return Observable.just(GetAdminPopUpStoreDetailResponseDTO.empty) + return Observable.empty() } throw error } } - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable { - Logger.log(message: "createStore API 호출 시작", category: .info) - let endpoint = AdminAPIEndpoint.createStore(request: request) - Logger.log(message: "Request URL: \(endpoint.baseURL + endpoint.path)", category: .info) - Logger.log(message: "Request Body: \(request)", category: .info) - - return provider.requestData( - with: endpoint, - interceptor: tokenInterceptor - ) - .catch { error -> Observable in - if case .responseSerializationFailed(let reason) = error as? AFError, - case .inputDataNilOrZeroLength = reason { - // 빈 응답 데이터일 경우 성공으로 간주 - Logger.log(message: "빈 응답 데이터 처리: 성공으로 간주", category: .info) - return Observable.just(EmptyResponse()) - } - throw error - } - .do( - onNext: { _ in - Logger.log(message: "createStore API 호출 성공", category: .info) - }, - onError: { error in - Logger.log(message: "createStore API 호출 실패: \(error)", category: .error) - } + func createStore(params: CreateStoreParams) -> Completable { + let dto = CreatePopUpStoreRequestDTO( + name: params.name, + categoryId: params.categoryId, + desc: params.desc, + address: params.address, + startDate: params.startDate, + endDate: params.endDate, + mainImageUrl: params.mainImageUrl, + imageUrlList: params.imageUrlList, + latitude: params.latitude, + longitude: params.longitude, + markerTitle: params.markerTitle, + markerSnippet: params.markerSnippet, + startDateBeforeEndDate: params.startDateBeforeEndDate ) + let endpoint = AdminAPIEndpoint.createStore(request: dto) + return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable { - let endpoint = AdminAPIEndpoint.updateStore(request: request) - - Logger.log(message: """ - Store Update 요청: - URL: \(endpoint.baseURL + endpoint.path) - Method: PUT - Request: \(request) - """, category: .debug) - - return provider.requestData( - with: endpoint, - interceptor: tokenInterceptor + func updateStore(params: UpdateStoreParams) -> Completable { + let dto = UpdatePopUpStoreRequestDTO( + popUpStore: UpdatePopUpStoreRequestDTO.PopUpStore( + id: params.id, + name: params.name, + categoryId: params.categoryId, + desc: params.desc, + address: params.address, + startDate: params.startDate, + endDate: params.endDate, + mainImageUrl: params.mainImageUrl, + bannerYn: !params.mainImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, + imageUrl: params.imageUrlList.compactMap { $0 }, + startDateBeforeEndDate: params.startDateBeforeEndDate + ), + location: UpdatePopUpStoreRequestDTO.Location( + latitude: params.latitude, + longitude: params.longitude, + markerTitle: params.markerTitle, + markerSnippet: params.markerSnippet + ), + imagesToAdd: params.imageUrlList.compactMap { $0 }, + imagesToDelete: params.imagesToDelete ) - .catch { error -> Observable in - Logger.log(message: "Update Store Error 발생: \(error)", category: .error) - - if let afError = error as? AFError { - switch afError { - case .responseSerializationFailed(let reason): - Logger.log(message: "Serialization 실패 reason: \(reason)", category: .error) - if case .inputDataNilOrZeroLength = reason { - Logger.log(message: "빈 응답 데이터 - 성공으로 처리", category: .info) - return Observable.just(EmptyResponse()) - } - default: - Logger.log(message: "기타 AFError: \(afError)", category: .error) - } - } - - throw error - } - .do(onNext: { _ in - Logger.log(message: "Store Update 성공", category: .info) - }, onError: { error in - Logger.log(message: "Store Update 최종 실패: \(error)", category: .error) - }) + let endpoint = AdminAPIEndpoint.updateStore(request: dto) + return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func deleteStore(id: Int64) -> Observable { - Logger.log(message: "deleteStore API 호출 시작", category: .info) + func deleteStore(id: Int64) -> Completable { let endpoint = AdminAPIEndpoint.deleteStore(id: id) return provider.request(with: endpoint, interceptor: tokenInterceptor) - .andThen(Observable.just(EmptyResponse())) - .do( - onNext: { _ in - Logger.log(message: "deleteStore API 호출 성공", category: .info) - }, - onError: { error in - Logger.log(message: "deleteStore API 호출 실패: \(error)", category: .error) - } - ) } // MARK: - Notice Methods - func createNotice(request: CreateNoticeRequestDTO) -> Observable { - let endpoint = AdminAPIEndpoint.createNotice(request: request) - return provider.requestData( - with: endpoint, - interceptor: tokenInterceptor + func createNotice(params: CreateNoticeParams) -> Completable { + let dto = CreateNoticeRequestDTO( + title: params.title, + content: params.content, + imageUrlList: params.imageUrlList ) + let endpoint = AdminAPIEndpoint.createNotice(request: dto) + return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable { - let endpoint = AdminAPIEndpoint.updateNotice(id: id, request: request) - return provider.requestData( - with: endpoint, - interceptor: tokenInterceptor + func updateNotice(params: UpdateNoticeParams) -> Completable { + let dto = UpdateNoticeRequestDTO( + title: params.title, + content: params.content, + imageUrlList: params.imageUrlList, + imagesToDelete: params.imagesToDelete ) + let endpoint = AdminAPIEndpoint.updateNotice(id: params.id, request: dto) + return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func deleteNotice(id: Int64) -> Observable { + func deleteNotice(id: Int64) -> Completable { let endpoint = AdminAPIEndpoint.deleteNotice(id: id) - return provider.requestData( - with: endpoint, - interceptor: tokenInterceptor - ) + return provider.request(with: endpoint, interceptor: tokenInterceptor) } } + +// Helper extension - keeping this for utility purposes extension GetAdminPopUpStoreDetailResponseDTO { static var empty: GetAdminPopUpStoreDetailResponseDTO { return GetAdminPopUpStoreDetailResponseDTO( diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 77097ed2..1f11e19f 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -1,5 +1,4 @@ import Foundation - import RxSwift final class AdminUseCaseImpl: AdminUseCase { @@ -10,53 +9,52 @@ final class AdminUseCaseImpl: AdminUseCase { self.repository = repository } - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { return repository.fetchStoreList(query: query, page: page, size: size) } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { return repository.fetchStoreDetail(id: id) } - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable { - Logger.log(message: "createStore 호출 - 요청 데이터: \(request)", category: .debug) - return repository.createStore(request: request) - .do(onNext: { _ in - Logger.log(message: "createStore 성공", category: .info) - }, onError: { error in + func createStore(params: CreateStoreParams) -> Completable { + Logger.log(message: "createStore 호출 - 스토어명: \(params.name)", category: .debug) + return repository.createStore(params: params) + .do(onError: { error in Logger.log(message: "createStore 실패 - Error: \(error)", category: .error) + }, onCompleted: { + Logger.log(message: "createStore 성공", category: .info) }) } - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable { + func updateStore(params: UpdateStoreParams) -> Completable { Logger.log(message: """ Updating store with location: - Latitude: \(request.location.latitude) - Longitude: \(request.location.longitude) + Latitude: \(params.latitude) + Longitude: \(params.longitude) """, category: .debug) - - return repository.updateStore(request: request) - .do(onNext: { _ in - Logger.log(message: "Store update successful", category: .debug) - }, onError: { error in + return repository.updateStore(params: params) + .do(onError: { error in Logger.log(message: "Store update failed: \(error)", category: .error) + }, onCompleted: { + Logger.log(message: "Store update successful", category: .debug) }) } - func deleteStore(id: Int64) -> Observable { + func deleteStore(id: Int64) -> Completable { return repository.deleteStore(id: id) } // Notice - func createNotice(request: CreateNoticeRequestDTO) -> Observable { - return repository.createNotice(request: request) + func createNotice(params: CreateNoticeParams) -> Completable { + return repository.createNotice(params: params) } - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable { - return repository.updateNotice(id: id, request: request) + func updateNotice(params: UpdateNoticeParams) -> Completable { + return repository.updateNotice(params: params) } - func deleteNotice(id: Int64) -> Observable { + func deleteNotice(id: Int64) -> Completable { return repository.deleteNotice(id: id) } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift new file mode 100644 index 00000000..58db4f63 --- /dev/null +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -0,0 +1,8 @@ +import Foundation + +struct AdminStore { + let id: Int64 + let name: String + let categoryName: String + let mainImageUrl: String +} diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift new file mode 100644 index 00000000..dc8f91ef --- /dev/null +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -0,0 +1,26 @@ +import Foundation + +struct AdminStoreDetail { + let id: Int64 + let name: String + let categoryId: Int64 + let categoryName: String + let description: String + let address: String + let startDate: String + let endDate: String + let createUserId: String + let createDateTime: String + let mainImageUrl: String + let bannerYn: Bool + let images: [StoreImage] + let latitude: Double + let longitude: Double + let markerTitle: String + let markerSnippet: String + + struct StoreImage { + let id: Int64 + let imageUrl: String + } +} diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift new file mode 100644 index 00000000..8f5a9d7e --- /dev/null +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -0,0 +1,49 @@ +import Foundation + +struct CreateStoreParams { + let name: String + let categoryId: Int64 + let desc: String + let address: String + let startDate: String + let endDate: String + let mainImageUrl: String + let imageUrlList: [String?] + let latitude: Double + let longitude: Double + let markerTitle: String + let markerSnippet: String + let startDateBeforeEndDate: Bool +} + +struct UpdateStoreParams { + let id: Int64 + let name: String + let categoryId: Int64 + let desc: String + let address: String + let startDate: String + let endDate: String + let mainImageUrl: String + let imageUrlList: [String?] + let imagesToDelete: [Int64] + let latitude: Double + let longitude: Double + let markerTitle: String + let markerSnippet: String + let startDateBeforeEndDate: Bool +} + +struct CreateNoticeParams { + let title: String + let content: String + let imageUrlList: [String] +} + +struct UpdateNoticeParams { + let id: Int64 + let title: String + let content: String + let imageUrlList: [String] + let imagesToDelete: [Int64] +} diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift index da4112ec..8f87a343 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift @@ -3,13 +3,16 @@ import Foundation import RxSwift protocol AdminRepository { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable - func fetchStoreDetail(id: Int64) -> Observable - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable - func deleteStore(id: Int64) -> Observable - - func createNotice(request: CreateNoticeRequestDTO) -> Observable - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable - func deleteNotice(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> + func fetchStoreDetail(id: Int64) -> Observable + + func createStore(params: CreateStoreParams) -> Completable + + func updateStore(params: UpdateStoreParams) -> Completable + + func deleteStore(id: Int64) -> Completable + + func createNotice(params: CreateNoticeParams) -> Completable + func updateNotice(params: UpdateNoticeParams) -> Completable + func deleteNotice(id: Int64) -> Completable } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift index 390c45fb..08c59b15 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift @@ -1,16 +1,18 @@ import Foundation - import RxSwift protocol AdminUseCase { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable - func fetchStoreDetail(id: Int64) -> Observable - func createStore(request: CreatePopUpStoreRequestDTO) -> Observable - func updateStore(request: UpdatePopUpStoreRequestDTO) -> Observable - func deleteStore(id: Int64) -> Observable - // Notice - func createNotice(request: CreateNoticeRequestDTO) -> Observable - func updateNotice(id: Int64, request: UpdateNoticeRequestDTO) -> Observable - func deleteNotice(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> + func fetchStoreDetail(id: Int64) -> Observable + + func createStore(params: CreateStoreParams) -> Completable + + func updateStore(params: UpdateStoreParams) -> Completable + + func deleteStore(id: Int64) -> Completable + + func createNotice(params: CreateNoticeParams) -> Completable + func updateNotice(params: UpdateNoticeParams) -> Completable + func deleteNotice(id: Int64) -> Completable } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift index e258e6df..12382108 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift @@ -14,18 +14,18 @@ final class AdminReactor: Reactor { } enum Mutation { - case setStores([GetAdminPopUpStoreListResponseDTO.PopUpStore]) + case setStores([AdminStore]) case setIsLoading(Bool) case navigateToRegister(Bool) - case navigateToEdit(GetAdminPopUpStoreListResponseDTO.PopUpStore) // ✅ 수정 데이터 추가 + case navigateToEdit(AdminStore) // ✅ 수정 데이터 추가 } struct State { - var storeList: [GetAdminPopUpStoreListResponseDTO.PopUpStore] = [] + var storeList: [AdminStore] = [] var isLoading: Bool = false var shouldNavigateToRegister: Bool = false - var selectedStoreForEdit: GetAdminPopUpStoreListResponseDTO.PopUpStore? // ✅ 추가 + var selectedStoreForEdit: AdminStore? // ✅ 추가 } @@ -44,7 +44,7 @@ final class AdminReactor: Reactor { return .concat([ .just(.setIsLoading(true)), useCase.fetchStoreList(query: nil, page: 0, size: 100) - .map { .setStores($0.popUpStoreList ?? []) }, // ✅ nil 방지 + .map { .setStores($0) }, // ✅ nil 방지 .just(.setIsLoading(false)) ]) @@ -57,7 +57,7 @@ final class AdminReactor: Reactor { }, onError: { error in Logger.log(message: "조회 실패 - 에러: \(error.localizedDescription)", category: .error) }) - .map { .setStores($0.popUpStoreList ?? []) }, // ✅ nil 방지 + .map { .setStores($0) }, // ✅ nil 방지 .just(.setIsLoading(false)) ]) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index e2c6cb67..ab03c402 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -85,7 +85,7 @@ final class PopUpStoreRegisterReactor: Reactor { case addDeletedImage(id: Int64, path: String) // 기존 스토어 데이터 설정 - case setStoreDetail(GetAdminPopUpStoreDetailResponseDTO) + case setStoreDetail(AdminStoreDetail) case setOriginalImageIds([String: Int64]) // UI 상태 관리 @@ -357,7 +357,7 @@ final class PopUpStoreRegisterReactor: Reactor { newState.address = storeDetail.address newState.lat = String(storeDetail.latitude) newState.lon = String(storeDetail.longitude) - newState.description = storeDetail.desc + newState.description = storeDetail.description // 날짜 파싱 let isoFormatter = ISO8601DateFormatter() @@ -615,7 +615,7 @@ final class PopUpStoreRegisterReactor: Reactor { // 이미지 ID 매핑 초기화 및 설정 var originalImageIds: [String: Int64] = [:] - for image in storeDetail.imageList { + for image in storeDetail.images { originalImageIds[image.imageUrl] = image.id } @@ -629,7 +629,7 @@ final class PopUpStoreRegisterReactor: Reactor { let dispatchGroup = DispatchGroup() let imageObservable = Observable.create { observer in - for imageData in storeDetail.imageList { + for imageData in storeDetail.images { // 중복 이미지 건너뛰기 if loadedImageUrls.contains(imageData.imageUrl) { continue @@ -742,7 +742,7 @@ final class PopUpStoreRegisterReactor: Reactor { return false } ?? imagePaths.first ?? "" - let request = CreatePopUpStoreRequestDTO( + let params = CreateStoreParams( name: state.name, categoryId: state.categoryId, desc: state.description, @@ -758,8 +758,8 @@ final class PopUpStoreRegisterReactor: Reactor { startDateBeforeEndDate: true ) - return self.adminUseCase.createStore(request: request) - .map { _ in .setSuccess(true) } + return self.adminUseCase.createStore(params: params) + .andThen(Observable.just(.setSuccess(true))) } } @@ -856,43 +856,26 @@ final class PopUpStoreRegisterReactor: Reactor { mainImage = "" } - // 업데이트 요청 생성 - let request = UpdatePopUpStoreRequestDTO( - popUpStore: .init( - id: storeId, - name: state.name, - categoryId: state.categoryId, - desc: state.description, - address: state.address, - startDate: dates.startDate, - endDate: dates.endDate, - mainImageUrl: mainImage, - bannerYn: !mainImage.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty, - imageUrl: allPaths, - startDateBeforeEndDate: true - ), - location: .init( - latitude: Double(state.lat) ?? 0, - longitude: Double(state.lon) ?? 0, - markerTitle: state.markerTitle, - markerSnippet: state.markerSnippet - ), - imagesToAdd: newImagePaths ?? [], - imagesToDelete: state.deletedImageIds + let params = UpdateStoreParams( + id: storeId, + name: state.name, + categoryId: state.categoryId, + desc: state.description, + address: state.address, + startDate: dates.startDate, + endDate: dates.endDate, + mainImageUrl: mainImage, + imageUrlList: allPaths, + imagesToDelete: state.deletedImageIds, + latitude: Double(state.lat) ?? 0, + longitude: Double(state.lon) ?? 0, + markerTitle: state.markerTitle, + markerSnippet: state.markerSnippet, + startDateBeforeEndDate: true ) - // 서버에 스토어 정보 업데이트 요청 - return adminUseCase.updateStore(request: request) - .flatMap { [weak self] _ -> Observable in - guard let self = self else { return .empty() } - - // S3에서 삭제된 이미지 제거 - if !state.deletedImagePaths.isEmpty { - self.deleteImagesFromS3(state.deletedImagePaths) - } - - return .just(.setSuccess(true)) - } + return self.adminUseCase.updateStore(params: params) + .andThen(Observable.just(.setSuccess(true))) } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift index b38efef8..993b0cd2 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift @@ -16,7 +16,7 @@ final class AdminViewController: BaseViewController, View { private let adminUseCase: AdminUseCase // MARK: - Init - init(nickname: String, adminUseCase: AdminUseCase = AdminUseCaseImpl(repository: AdminRepositoryImpl(provider: ProviderImpl()))) { + init(nickname: String, adminUseCase: AdminUseCase) { self.nickname = nickname self.adminUseCase = adminUseCase self.mainView = AdminView(frame: .zero) @@ -122,7 +122,6 @@ final class AdminViewController: BaseViewController, View { alert.addAction(UIAlertAction(title: "취소", style: .cancel)) - // iPad support if let popoverController = alert.popoverPresentationController { popoverController.sourceView = mainView.menuButton popoverController.sourceRect = mainView.menuButton.bounds @@ -151,7 +150,7 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func showDeleteConfirmation(for store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { + private func showDeleteConfirmation(for store: AdminStore) { let alert = UIAlertController( title: "삭제 확인", message: "\(store.name)을(를) 삭제하시겠습니까?", @@ -166,22 +165,43 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func editStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { - let registerVC = PopUpStoreRegisterViewController( - nickname: nickname, - adminUseCase: adminUseCase, - editingStore: store - ) - - // 수정할 때도 completionHandler 추가 - registerVC.completionHandler = { [weak self] in - self?.reactor?.action.onNext(.reloadData) - } - - navigationController?.pushViewController(registerVC, animated: true) + private func editStore(_ store: AdminStore) { + adminUseCase.fetchStoreDetail(id: store.id) + .observe(on: MainScheduler.instance) + .subscribe( + onNext: { [weak self] storeDetail in + guard let self = self else { return } + let updateParams = UpdateStoreParams( + id: storeDetail.id, + name: storeDetail.name, + categoryId: storeDetail.categoryId, + desc: storeDetail.description, + address: storeDetail.address, + startDate: storeDetail.startDate, + endDate: storeDetail.endDate, + mainImageUrl: storeDetail.mainImageUrl, + imageUrlList: storeDetail.images.map { $0.imageUrl }, + imagesToDelete: [], + latitude: storeDetail.latitude, + longitude: storeDetail.longitude, + markerTitle: storeDetail.markerTitle, + markerSnippet: storeDetail.markerSnippet, + startDateBeforeEndDate: true + ) + let registerVC = PopUpStoreRegisterViewController( + nickname: self.nickname, + adminUseCase: self.adminUseCase + ) + self.navigationController?.pushViewController(registerVC, animated: true) + }, + onError: { [weak self] error in + self?.showErrorAlert(message: "스토어 정보 조회 실패: \(error.localizedDescription)") + } + ) + .disposed(by: disposeBag) } - private func deleteStore(_ store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { + private func deleteStore(_ store: AdminStore) { // 먼저 스토어 상세 정보를 가져와 모든 이미지 URL을 확인 adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) @@ -194,7 +214,7 @@ final class AdminViewController: BaseViewController, View { allImageUrls.append(storeDetail.mainImageUrl) // 다른 모든 이미지 URL 추가 - let otherImageUrls = storeDetail.imageList.map { $0.imageUrl } + let otherImageUrls = storeDetail.images.map { $0.imageUrl } allImageUrls.append(contentsOf: otherImageUrls) allImageUrls = Array(Set(allImageUrls)) @@ -206,7 +226,7 @@ final class AdminViewController: BaseViewController, View { .andThen(self.adminUseCase.deleteStore(id: store.id)) .observe(on: MainScheduler.instance) .subscribe( - onNext: { [weak self] _ in + onCompleted: { [weak self] in self?.reactor?.action.onNext(.reloadData) ToastMaker.createToast(message: "삭제되었습니다") }, @@ -277,7 +297,13 @@ final class AdminViewController: BaseViewController, View { cellIdentifier: AdminStoreCell.identifier, cellType: AdminStoreCell.self )) { _, item, cell in - cell.configure(with: item) + let dto = GetAdminPopUpStoreListResponseDTO.PopUpStore( + id: item.id, + name: item.name, + categoryName: item.categoryName, + mainImageUrl: item.mainImageUrl + ) + cell.configure(with: dto) } .disposed(by: disposeBag) } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 3777f7e1..1b271497 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -263,21 +263,18 @@ final class MyPageReactor: Reactor { let navigationController = UINavigationController(rootViewController: nextController) navigationController.modalPresentationStyle = .fullScreen controller.present(navigationController, animated: true) - case .moveToMyCommentScene(let controller): let nextController = MyCommentController() nextController.reactor = MyCommentReactor() controller.navigationController?.pushViewController(nextController, animated: true) - case .moveToAdminScene(let controller): // 관리자 VC let nickname = profileSection.inputDataList.first?.nickName ?? "" - let adminVC = AdminViewController(nickname: nickname) - adminVC.reactor = AdminReactor( - useCase: AdminUseCaseImpl( - repository: AdminRepositoryImpl(provider: ProviderImpl()) - ) + let adminUseCase = AdminUseCaseImpl( + repository: AdminRepositoryImpl(provider: ProviderImpl()) ) + let adminVC = AdminViewController(nickname: nickname, adminUseCase: adminUseCase) + adminVC.reactor = AdminReactor(useCase: adminUseCase) controller.navigationController?.pushViewController(adminVC, animated: true) } From 0d32c2658da4012a331e7f12fe88a84f068de2f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 03:00:08 +0900 Subject: [PATCH 139/393] =?UTF-8?q?refactor/#117:=20Map=20API=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20DTO=EC=A0=9C=EA=B1=B0=20=EB=B9=8C=EB=93=9C=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryImpl/MapRepositoryImpl.swift | 58 ++++++++++++++----- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 42 ++++++++------ .../Repository/MapRepository.swift | 4 +- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift index c78a6af6..2ce6dbeb 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift @@ -2,9 +2,8 @@ import Foundation import RxSwift -// MARK: - Implementation final class MapRepositoryImpl: MapRepository { - + private let provider: Provider init(provider: Provider) { @@ -17,7 +16,7 @@ final class MapRepositoryImpl: MapRepository { southWestLat: Double, southWestLon: Double, categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> { + ) -> Observable<[MapPopUpStore]> { return provider.requestData( with: MapAPIEndpoint.locations_fetchStoresInBounds( northEastLat: northEastLat, @@ -29,12 +28,30 @@ final class MapRepositoryImpl: MapRepository { interceptor: TokenInterceptor() ) .map { $0.popUpStoreList } + .map { dtoList in + dtoList.map { dto -> MapPopUpStore in + return MapPopUpStore( + id: dto.id, + category: dto.categoryName, + name: dto.name, + address: dto.address, + startDate: dto.startDate, + endDate: dto.endDate, + latitude: dto.latitude, + longitude: dto.longitude, + markerId: dto.id, + markerTitle: dto.markerTitle ?? dto.name, + markerSnippet: dto.markerSnippet ?? "", + mainImageUrl: dto.mainImageUrl + ) + } + } } func searchStores( query: String, categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> { + ) -> Observable<[MapPopUpStore]> { return provider.requestData( with: MapAPIEndpoint.locations_searchStores( query: query, @@ -43,10 +60,28 @@ final class MapRepositoryImpl: MapRepository { interceptor: TokenInterceptor() ) .map { $0.popUpStoreList } + .map { dtoList in + dtoList.map { dto -> MapPopUpStore in + return MapPopUpStore( + id: dto.id, + category: dto.categoryName, + name: dto.name, + address: dto.address, + startDate: dto.startDate, + endDate: dto.endDate, + latitude: dto.latitude, + longitude: dto.longitude, + markerId: dto.id, + markerTitle: dto.markerTitle ?? dto.name, + markerSnippet: dto.markerSnippet ?? "", + mainImageUrl: dto.mainImageUrl + ) + } + } } func fetchCategories() -> Observable<[CategoryResponse]> { - Logger.log(message: "카테고리 매핑 요청을 시작합니다.", category: .network) + Logger.log(message: "카테고리 목록 요청을 시작합니다.", category: .network) return provider.requestData( with: SignUpAPIEndpoint.signUp_getCategoryList(), @@ -54,26 +89,19 @@ final class MapRepositoryImpl: MapRepository { ) .do(onNext: { responseDTO in Logger.log( - message: """ - 카테고리 매핑 응답: - - Response: \(responseDTO) - - categoryResponseList: \(responseDTO.categoryResponseList) - """, + message: "카테고리 목록 응답 성공", category: .debug ) }) .map { responseDTO in - let categories = responseDTO.categoryResponseList.map { $0.toDomain() } - Logger.log(message: "매핑된 카테고리 데이터: \(categories)", category: .debug) - return categories + responseDTO.categoryResponseList.map { $0.toDomain() } } .catch { error in Logger.log( - message: "카테고리 매핑 요청 실패: \(error.localizedDescription)", + message: "카테고리 목록 요청 실패: \(error.localizedDescription)", category: .error ) throw error } } - } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift index f06968c0..7ffd64c2 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -21,15 +21,18 @@ final class MapUseCaseImpl: MapUseCase { southWestLon: Double, categories: [Int64] ) -> Observable<[MapPopUpStore]> { - return repository.fetchStoresInBounds( northEastLat: northEastLat, northEastLon: northEastLon, southWestLat: southWestLat, southWestLon: southWestLon, - categories: categories // ← 그대로 넘긴다 + categories: categories ) - .map { $0.map { $0.toDomain() } } + .do(onNext: { stores in + Logger.log(message: "맵 범위 내 스토어 \(stores.count)개 로드됨", category: .debug) + }, onError: { error in + Logger.log(message: "맵 범위 내 스토어 로드 실패: \(error)", category: .error) + }) } func searchStores( @@ -38,23 +41,28 @@ final class MapUseCaseImpl: MapUseCase { ) -> Observable<[MapPopUpStore]> { return repository.searchStores( query: query, - categories: categories.map { Int64($0) ?? 0 } + categories: categories ) - .map { $0.map { $0.toDomain() } } + .do(onNext: { stores in + Logger.log(message: "'\(query)' 검색 결과 \(stores.count)개 로드됨", category: .debug) + }, onError: { error in + Logger.log(message: "스토어 검색 실패: \(error)", category: .error) + }) } + func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] { - guard !selectedRegions.isEmpty else { return stores } + guard !selectedRegions.isEmpty else { return stores } - return stores.filter { store in - let components = store.address.components(separatedBy: " ") - guard components.count >= 2 else { return false } + return stores.filter { store in + let components = store.address.components(separatedBy: " ") + guard components.count >= 2 else { return false } - let mainRegion = components[0].replacingOccurrences(of: "특별시", with: "") - .replacingOccurrences(of: "광역시", with: "") - let subRegion = components[1] + let mainRegion = components[0].replacingOccurrences(of: "특별시", with: "") + .replacingOccurrences(of: "광역시", with: "") + let subRegion = components[1] - return selectedRegions.contains("\(mainRegion)전체") || - selectedRegions.contains(subRegion) - } - } - } + return selectedRegions.contains("\(mainRegion)전체") || + selectedRegions.contains(subRegion) + } + } +} diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift index 6f613828..cfdb4f65 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift @@ -17,12 +17,12 @@ protocol MapRepository { southWestLat: Double, southWestLon: Double, categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> + ) -> Observable<[MapPopUpStore]> func searchStores( query: String, categories: [Int64] - ) -> Observable<[MapPopUpStoreDTO]> + ) -> Observable<[MapPopUpStore]> func fetchCategories() -> Observable<[CategoryResponse]> } From 22a0571d8945a758c74797c1ad41d8079d6dbbd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 06:34:39 +0900 Subject: [PATCH 140/393] =?UTF-8?q?refactor/#117:=20MapPopUpStore=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=AA=A8=EB=8D=B8=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=20=ED=94=84=EB=A0=88=EC=9E=84=EC=9B=8C=ED=81=AC=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/MapResponse/MapPopUpStore.swift | 26 ------------------- .../Scene/Map/MapView/MapViewController.swift | 24 +++++++++++++++-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift index 2be1694e..65fc7ebb 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift @@ -1,5 +1,4 @@ import Foundation -import NMapsMap struct MapPopUpStore: Equatable { let id: Int64 @@ -14,29 +13,4 @@ struct MapPopUpStore: Equatable { let markerTitle: String let markerSnippet: String let mainImageUrl: String? - - var nmgCoordinate: NMGLatLng { - NMGLatLng(lat: latitude, lng: longitude) - } - - func toMarkerInput() -> MapMarker.Input { - return MapMarker.Input( - isSelected: false, - isCluster: false, - regionName: self.markerTitle, - count: 0 - ) - } - - func toStoreItem() -> StoreItem { - return StoreItem( - id: id, - thumbnailURL: mainImageUrl ?? "", - category: category, - title: name, - location: address, - dateRange: "\(startDate) ~ \(endDate)", - isBookmarked: false - ) - } } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift index e327b463..d8653307 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift @@ -501,7 +501,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.addMarkers(for: results) // 스토어 리스트 업데이트 - let storeItems = results.map { $0.toStoreItem() } + let storeItems = results.map { store in + StoreItem( + id: store.id, + thumbnailURL: store.mainImageUrl ?? "", + category: store.category, + title: store.name, + location: store.address, + dateRange: "\(store.startDate) ~ \(store.endDate)", + isBookmarked: false + ) + } self.storeListViewController.reactor?.action.onNext(.setStores(storeItems)) // 캐러셀 업데이트 @@ -1044,7 +1054,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func updateListView(with results: [MapPopUpStore]) { // MapPopUpStore 배열을 StoreItem 배열로 변환 - let storeItems = results.map { $0.toStoreItem() } + let storeItems = results.map { store in + StoreItem( + id: store.id, + thumbnailURL: store.mainImageUrl ?? "", + category: store.category, + title: store.name, + location: store.address, + dateRange: "\(store.startDate) ~ \(store.endDate)", + isBookmarked: false + ) + } storeListViewController.reactor?.action.onNext(.setStores(storeItems)) } From 9edf78542f6dd638770680d6c34fb2622e29c675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 06:35:08 +0900 Subject: [PATCH 141/393] =?UTF-8?q?refactor/#117:=20DTO=20->=20=EB=8F=84?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=EB=AA=A8=EB=8D=B8=20=EB=B3=80=ED=99=98=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20MapDomainModelConverter=EB=A1=9C?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MapAPI/ResponseDTO/MapPopUpStoreDTO.swift | 18 --------- .../Converter/MapDomainModelConverter.swift | 22 ++++++++++ .../RepositoryImpl/MapRepositoryImpl.swift | 40 +------------------ 3 files changed, 24 insertions(+), 56 deletions(-) create mode 100644 Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift index cd7bb5de..12d4916f 100644 --- a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift +++ b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift @@ -14,24 +14,6 @@ struct MapPopUpStoreDTO: Codable { let markerSnippet: String let mainImageUrl: String? let bookmarkYn: Bool? - - func toDomain() -> MapPopUpStore { - return MapPopUpStore( - id: id, - category: categoryName, - name: name, - address: address, - startDate: startDate, - endDate: endDate, - latitude: latitude, - longitude: longitude, - markerId: markerId, - markerTitle: markerTitle, - markerSnippet: markerSnippet, - mainImageUrl: mainImageUrl - - ) - } } struct GetViewBoundPopUpStoreListResponse: Decodable { diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift new file mode 100644 index 00000000..fb53ddab --- /dev/null +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift @@ -0,0 +1,22 @@ +import Foundation + +struct MapDomainModelConverter { + /// MapPopUpStoreDTO + /// → MapPopUpStore 도메인 모델로 변환 + static func convert(_ dto: MapPopUpStoreDTO) -> MapPopUpStore { + return MapPopUpStore( + id: dto.id, + category: dto.categoryName, + name: dto.name, + address: dto.address, + startDate: dto.startDate, + endDate: dto.endDate, + latitude: dto.latitude, + longitude: dto.longitude, + markerId: dto.markerId, + markerTitle: dto.markerTitle, + markerSnippet: dto.markerSnippet, + mainImageUrl: dto.mainImageUrl + ) + } +} diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift index 2ce6dbeb..45973865 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift @@ -27,25 +27,7 @@ final class MapRepositoryImpl: MapRepository { ), interceptor: TokenInterceptor() ) - .map { $0.popUpStoreList } - .map { dtoList in - dtoList.map { dto -> MapPopUpStore in - return MapPopUpStore( - id: dto.id, - category: dto.categoryName, - name: dto.name, - address: dto.address, - startDate: dto.startDate, - endDate: dto.endDate, - latitude: dto.latitude, - longitude: dto.longitude, - markerId: dto.id, - markerTitle: dto.markerTitle ?? dto.name, - markerSnippet: dto.markerSnippet ?? "", - mainImageUrl: dto.mainImageUrl - ) - } - } + .map { $0.popUpStoreList.map(MapDomainModelConverter.convert) } } func searchStores( @@ -59,25 +41,7 @@ final class MapRepositoryImpl: MapRepository { ), interceptor: TokenInterceptor() ) - .map { $0.popUpStoreList } - .map { dtoList in - dtoList.map { dto -> MapPopUpStore in - return MapPopUpStore( - id: dto.id, - category: dto.categoryName, - name: dto.name, - address: dto.address, - startDate: dto.startDate, - endDate: dto.endDate, - latitude: dto.latitude, - longitude: dto.longitude, - markerId: dto.id, - markerTitle: dto.markerTitle ?? dto.name, - markerSnippet: dto.markerSnippet ?? "", - mainImageUrl: dto.mainImageUrl - ) - } - } + .map { $0.popUpStoreList.map(MapDomainModelConverter.convert) } } func fetchCategories() -> Observable<[CategoryResponse]> { From 0e85d9b477269b0b868444acbd17cef2f9f3228f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 06:43:26 +0900 Subject: [PATCH 142/393] =?UTF-8?q?refactor/#117:=20Auth=20API=20DomainLay?= =?UTF-8?q?er=20DTO=20=EC=A0=9C=EA=B1=B0=20=EB=B9=8C=EB=93=9C=ED=99=95?= =?UTF-8?q?=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift | 6 ++++-- .../DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift | 4 +--- .../DomainInterface/Repository/AuthAPIRepository.swift | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift index a0847bd2..c1f27826 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -1,5 +1,4 @@ import Foundation - import RxSwift final class AuthAPIRepositoryImpl: AuthAPIRepository { @@ -20,8 +19,11 @@ final class AuthAPIRepositoryImpl: AuthAPIRepository { } } - func postTokenReissue() -> Observable { + func postTokenReissue() -> Observable { let endPoint = AuthAPIEndPoint.postTokenReissue() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) + .map { responseDTO in + return responseDTO.toDomain() + } } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift index 841a4034..258cb9e0 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift @@ -1,5 +1,4 @@ import Foundation - import RxSwift final class AuthAPIUseCaseImpl: AuthAPIUseCase { @@ -15,7 +14,6 @@ final class AuthAPIUseCaseImpl: AuthAPIUseCase { } func postTokenReissue() -> Observable { - let endPoint = AuthAPIEndPoint.postTokenReissue() - return repository.postTokenReissue().map { $0.toDomain() } + return repository.postTokenReissue() } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift index 630c801b..ba9ae29f 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift @@ -1,8 +1,7 @@ import Foundation - import RxSwift protocol AuthAPIRepository { func tryLogIn(userCredential: Encodable, socialType: String) -> Observable - func postTokenReissue() -> Observable + func postTokenReissue() -> Observable } From 1f4ce4867a9cbd328ae99002c634a4ddaa59975a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 08:29:35 +0900 Subject: [PATCH 143/393] =?UTF-8?q?refactor/#117:=20Comment=20API=20?= =?UTF-8?q?=EB=A0=88=ED=8F=AC=EC=A7=80=ED=86=A0=EB=A6=AC=20=EC=9D=B8?= =?UTF-8?q?=ED=84=B0=ED=8E=98=EC=9D=B4=EC=8A=A4=EC=97=90=EC=84=9C=20DTO=20?= =?UTF-8?q?=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EC=9E=84=ED=94=8C=EC=97=90=EC=84=9C=20=EB=A7=A4?= =?UTF-8?q?=ED=95=91=20=EB=B9=8C=EB=93=9C=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentAPIRepositoryImpl.swift | 15 +++++++------ .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 8 +++---- .../Repository/CommentAPIRepository.swift | 21 ++++++++++++++++--- .../UseCase/CommentAPIUseCase.swift | 2 +- 4 files changed, 32 insertions(+), 14 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift index 22ae8766..6fa51907 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift @@ -11,18 +11,21 @@ final class CommentAPIRepositoryImpl: CommentAPIRepository { self.provider = provider } - func postCommentAdd(request: PostCommentRequestDTO) -> Completable { - let endPoint = CommentAPIEndPoint.postCommentAdd(request: request) + func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { + let requestDTO = PostCommentRequestDTO(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList) + let endPoint = CommentAPIEndPoint.postCommentAdd(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteComment(request: DeleteCommentRequestDTO) -> Completable { - let endPoint = CommentAPIEndPoint.deleteComment(request: request) + func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { + let requestDTO = DeleteCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId) + let endPoint = CommentAPIEndPoint.deleteComment(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func editComment(request: PutCommentRequestDTO) -> Completable { - let endPoint = CommentAPIEndPoint.editComment(request: request) + func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable { + let requestDTO = PutCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList) + let endPoint = CommentAPIEndPoint.editComment(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } } diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index c7a31242..9086e118 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift final class CommentAPIUseCaseImpl: CommentAPIUseCase { - + private let repository: CommentAPIRepository init(repository: CommentAPIRepository) { @@ -11,14 +11,14 @@ final class CommentAPIUseCaseImpl: CommentAPIUseCase { } func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { - return repository.postCommentAdd(request: .init(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList)) + return repository.postCommentAdd(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList) } func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { - return repository.deleteComment(request: .init(popUpStoreId: popUpStoreId, commentId: commentId)) + return repository.deleteComment(popUpStoreId: popUpStoreId, commentId: commentId) } func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable { - return repository.editComment(request: .init(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList)) + return repository.editComment(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList) } } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift index bd079fc8..e353e65c 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift @@ -3,7 +3,22 @@ import Foundation import RxSwift protocol CommentAPIRepository { - func postCommentAdd(request: PostCommentRequestDTO) -> Completable - func deleteComment(request: DeleteCommentRequestDTO) -> Completable - func editComment(request: PutCommentRequestDTO) -> Completable + func postCommentAdd( + popUpStoreId: Int64, + content: String?, + commentType: String?, + imageUrlList: [String?] + ) -> Completable + + func deleteComment( + popUpStoreId: Int64, + commentId: Int64 + ) -> Completable + + func editComment( + popUpStoreId: Int64, + commentId: Int64, + content: String?, + imageUrlList: [PutCommentImageDataRequestDTO]? + ) -> Completable } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift index be737e40..021c842a 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift @@ -5,5 +5,5 @@ import RxSwift protocol CommentAPIUseCase { func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable - func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable + func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [String?]?) -> Completable } From ca1ddb797bdd9fe4a5b854f480f1eff566a71252 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 20 Apr 2025 00:22:17 +0000 Subject: [PATCH 144/393] style/#117: Apply SwiftLint autocorrect --- .../DataLayer/RepositoryImpl/AdminRepositoryImpl.swift | 2 +- .../DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift | 2 +- .../Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift | 2 +- .../DomainInterface/Repository/MapRepository.swift | 1 - .../DomainLayer/DomainInterface/UseCase/MapUseCase.swift | 1 - .../DomainInterface/UseCase/SignUpAPIUseCase.swift | 4 ++-- 6 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift index 46cf88d8..5856ca40 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift @@ -1,5 +1,5 @@ -import Foundation import Alamofire +import Foundation import RxSwift final class AdminRepositoryImpl: AdminRepository { diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift index 8d0fa37c..25a7791f 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift final class MapDirectionRepositoryImpl: MapDirectionRepository { - + private let provider: Provider private let tokenInterceptor = TokenInterceptor() diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift index 45973865..2a519f4b 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift @@ -51,7 +51,7 @@ final class MapRepositoryImpl: MapRepository { with: SignUpAPIEndpoint.signUp_getCategoryList(), interceptor: TokenInterceptor() ) - .do(onNext: { responseDTO in + .do(onNext: { _ in Logger.log( message: "카테고리 목록 응답 성공", category: .debug diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift index cfdb4f65..4e6c1a19 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift @@ -5,7 +5,6 @@ // Created by 송영훈 on 4/14/25. // - import Foundation import RxSwift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift index 9eb687da..c2944362 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift @@ -5,7 +5,6 @@ // Created by 송영훈 on 4/14/25. // - import Foundation import RxSwift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift index e0dfeb36..8711e4a7 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift @@ -12,8 +12,8 @@ protocol SignUpAPIUseCase { interests: [Int64], appleAuthorizationCode: String? ) -> Completable - + func checkNickName(nickName: String) -> Observable - + func fetchCategoryList() -> Observable<[CategoryResponse]> } From 0eb1045f65c71d08cd5f813da4cbbee32442de26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 17:10:17 +0900 Subject: [PATCH 145/393] =?UTF-8?q?refactor/#117:=20AdminVC=20DTO=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=20=EC=A0=9C=EA=B1=B0=20,=20Params=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EC=97=90=20=EB=A7=9E=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RepositoryImpl/AdminRepositoryImpl.swift | 10 +++++----- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 4 ++-- ...StoreDetail.swift => StoreDetailResponse.swift} | 2 +- .../{AdminStore.swift => StoreResponse.swift} | 2 +- .../Repository/AdminRepository.swift | 4 ++-- .../DomainInterface/UseCase/AdminUseCase.swift | 4 ++-- .../DomainInterface/UseCase/MapUseCase.swift | 7 ------- .../Presentation/Scene/Admin/AdminReactor.swift | 8 ++++---- .../AdminRegister/PopUpStoreRegisterReactor.swift | 2 +- .../Presentation/Scene/Admin/AdminStoreCell.swift | 3 +-- .../Scene/Admin/AdminViewController.swift | 14 ++++---------- 11 files changed, 23 insertions(+), 37 deletions(-) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/{AdminStoreDetail.swift => StoreDetailResponse.swift} (94%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/{AdminStore.swift => StoreResponse.swift} (83%) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift index 5856ca40..1edf2d41 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift @@ -14,7 +14,7 @@ final class AdminRepositoryImpl: AdminRepository { } // MARK: - Store Methods - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> { let endpoint = AdminAPIEndpoint.fetchStoreList( query: query, page: page, @@ -26,19 +26,19 @@ final class AdminRepositoryImpl: AdminRepository { ) .map { response in response.popUpStoreList?.map { - AdminStore(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl) + StoreResponse(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl) } ?? [] } } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { let endpoint = AdminAPIEndpoint.fetchStoreDetail(id: id) return provider.requestData( with: endpoint, interceptor: tokenInterceptor ) .map { dto in - AdminStoreDetail( + StoreDetailResponse( id: dto.id, name: dto.name, categoryId: dto.categoryId, @@ -52,7 +52,7 @@ final class AdminRepositoryImpl: AdminRepository { mainImageUrl: dto.mainImageUrl, bannerYn: dto.bannerYn, images: dto.imageList.map { - AdminStoreDetail.StoreImage( + StoreDetailResponse.StoreImage( id: $0.id, imageUrl: $0.imageUrl ) diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 1f11e19f..3dfcb67f 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -9,11 +9,11 @@ final class AdminUseCaseImpl: AdminUseCase { self.repository = repository } - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> { return repository.fetchStoreList(query: query, page: page, size: size) } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { return repository.fetchStoreDetail(id: id) } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift similarity index 94% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift index dc8f91ef..352b7f9f 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift @@ -1,6 +1,6 @@ import Foundation -struct AdminStoreDetail { +struct StoreDetailResponse { let id: Int64 let name: String let categoryId: Int64 diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift similarity index 83% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift index 58db4f63..2e7913aa 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift @@ -1,6 +1,6 @@ import Foundation -struct AdminStore { +struct StoreResponse { let id: Int64 let name: String let categoryName: String diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift index 8f87a343..a30695e0 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift @@ -3,8 +3,8 @@ import Foundation import RxSwift protocol AdminRepository { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> - func fetchStoreDetail(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> + func fetchStoreDetail(id: Int64) -> Observable func createStore(params: CreateStoreParams) -> Completable diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift index 08c59b15..b9a8161f 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift @@ -3,8 +3,8 @@ import RxSwift protocol AdminUseCase { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> - func fetchStoreDetail(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> + func fetchStoreDetail(id: Int64) -> Observable func createStore(params: CreateStoreParams) -> Completable diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift index c2944362..f425adf0 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift @@ -1,10 +1,3 @@ -// -// MapUseCase.swift -// Poppool -// -// Created by 송영훈 on 4/14/25. -// - import Foundation import RxSwift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift index 4e9d8bc9..8ca61403 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift @@ -14,18 +14,18 @@ final class AdminReactor: Reactor { } enum Mutation { - case setStores([AdminStore]) + case setStores([StoreResponse]) case setIsLoading(Bool) case navigateToRegister(Bool) - case navigateToEdit(AdminStore) // ✅ 수정 데이터 추가 + case navigateToEdit(StoreResponse) // ✅ 수정 데이터 추가 } struct State { - var storeList: [AdminStore] = [] + var storeList: [StoreResponse] = [] var isLoading: Bool = false var shouldNavigateToRegister: Bool = false - var selectedStoreForEdit: AdminStore? // ✅ 추가 + var selectedStoreForEdit: StoreResponse? // ✅ 추가 } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 6989d910..7ef8fb60 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -89,7 +89,7 @@ final class PopUpStoreRegisterReactor: Reactor { case addDeletedImage(id: Int64, path: String) // 기존 스토어 데이터 설정 - case setStoreDetail(AdminStoreDetail) + case setStoreDetail(StoreDetailResponse) case setOriginalImageIds([String: Int64]) // UI 상태 관리 diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift index a29c4ee6..e7468603 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift @@ -75,14 +75,13 @@ final class AdminStoreCell: UITableViewCell { } // MARK: - Configure - func configure(with store: GetAdminPopUpStoreListResponseDTO.PopUpStore) { + func configure(with store: StoreResponse) { Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) titleLabel.text = store.name categoryLabel.text = store.categoryName statusChip.text = "운영" - // mainImageUrl에서 baseURL 부분 제거 let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift index aa85097f..108773ee 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift @@ -153,7 +153,7 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func showDeleteConfirmation(for store: AdminStore) { + private func showDeleteConfirmation(for store: StoreResponse) { let alert = UIAlertController( title: "삭제 확인", message: "\(store.name)을(를) 삭제하시겠습니까?", @@ -168,7 +168,7 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func editStore(_ store: AdminStore) { + private func editStore(_ store: StoreResponse) { adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) .subscribe( @@ -201,7 +201,7 @@ final class AdminViewController: BaseViewController, View { .disposed(by: disposeBag) } - private func deleteStore(_ store: AdminStore) { + private func deleteStore(_ store: StoreResponse) { // 먼저 스토어 상세 정보를 가져와 모든 이미지 URL을 확인 adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) @@ -294,13 +294,7 @@ final class AdminViewController: BaseViewController, View { cellIdentifier: AdminStoreCell.identifier, cellType: AdminStoreCell.self )) { _, store, cell in - let dto = GetAdminPopUpStoreListResponseDTO.PopUpStore( - id: store.id, - name: store.name, - categoryName: store.categoryName, - mainImageUrl: store.mainImageUrl - ) - cell.configure(with: dto) + cell.configure(with: store) } .disposed(by: disposeBag) } From 11a86206182d151fc00f2d134e4d47b1a528d5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sun, 20 Apr 2025 19:10:36 +0900 Subject: [PATCH 146/393] =?UTF-8?q?refactor/#117:=20=ED=8C=9D=EC=97=85?= =?UTF-8?q?=EB=A0=88=EC=A7=80=EC=8A=A4=ED=84=B0=20=EB=B7=B0=EC=BB=A8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20DTO=20=EC=A0=9C=EA=B1=B0=20=EB=B9=8C?= =?UTF-8?q?=EB=93=9C=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=99=95=EC=9D=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/MapAPI/MapAPIEndpoint.swift | 1 - .../MapAPI/ResponseDTO/MapPopUpStoreDTO.swift | 18 ++++++++++++++- .../RepositoryImpl/AdminRepositoryImpl.swift | 10 ++++----- .../Converter/MapDomainModelConverter.swift | 22 ------------------- .../RepositoryImpl/MapRepositoryImpl.swift | 4 ++-- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 4 ++-- .../{StoreResponse.swift => AdminStore.swift} | 2 +- ...lResponse.swift => AdminStoreDetail.swift} | 2 +- .../Repository/AdminRepository.swift | 4 ++-- .../UseCase/AdminUseCase.swift | 4 ++-- .../Scene/Admin/AdminReactor.swift | 8 +++---- .../PopUpStoreRegisterReactor.swift | 4 ++-- .../PopUpStoreRegisterViewController.swift | 2 +- .../Scene/Admin/AdminStoreCell.swift | 2 +- .../Scene/Admin/AdminViewController.swift | 6 ++--- 15 files changed, 43 insertions(+), 50 deletions(-) delete mode 100644 Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/{StoreResponse.swift => AdminStore.swift} (83%) rename Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/{StoreDetailResponse.swift => AdminStoreDetail.swift} (94%) diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift index da8bc4fd..334067a5 100644 --- a/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift +++ b/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift @@ -52,7 +52,6 @@ struct MapAPIEndpoint { } } -// MARK: - Query DTOs struct BoundQueryDTO: Encodable { let northEastLat: Double let northEastLon: Double diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift index 12d4916f..7f356980 100644 --- a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift +++ b/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift @@ -14,8 +14,24 @@ struct MapPopUpStoreDTO: Codable { let markerSnippet: String let mainImageUrl: String? let bookmarkYn: Bool? -} + func toDomain() -> MapPopUpStore { + return MapPopUpStore( + id: id, + category: categoryName, + name: name, + address: address, + startDate: startDate, + endDate: endDate, + latitude: latitude, + longitude: longitude, + markerId: markerId, + markerTitle: markerTitle, + markerSnippet: markerSnippet, + mainImageUrl: mainImageUrl + ) + } +} struct GetViewBoundPopUpStoreListResponse: Decodable { let popUpStoreList: [MapPopUpStoreDTO] } diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift index 1edf2d41..5856ca40 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift @@ -14,7 +14,7 @@ final class AdminRepositoryImpl: AdminRepository { } // MARK: - Store Methods - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { let endpoint = AdminAPIEndpoint.fetchStoreList( query: query, page: page, @@ -26,19 +26,19 @@ final class AdminRepositoryImpl: AdminRepository { ) .map { response in response.popUpStoreList?.map { - StoreResponse(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl) + AdminStore(id: $0.id, name: $0.name, categoryName: $0.categoryName, mainImageUrl: $0.mainImageUrl) } ?? [] } } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { let endpoint = AdminAPIEndpoint.fetchStoreDetail(id: id) return provider.requestData( with: endpoint, interceptor: tokenInterceptor ) .map { dto in - StoreDetailResponse( + AdminStoreDetail( id: dto.id, name: dto.name, categoryId: dto.categoryId, @@ -52,7 +52,7 @@ final class AdminRepositoryImpl: AdminRepository { mainImageUrl: dto.mainImageUrl, bannerYn: dto.bannerYn, images: dto.imageList.map { - StoreDetailResponse.StoreImage( + AdminStoreDetail.StoreImage( id: $0.id, imageUrl: $0.imageUrl ) diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift deleted file mode 100644 index fb53ddab..00000000 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/Converter/MapDomainModelConverter.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation - -struct MapDomainModelConverter { - /// MapPopUpStoreDTO - /// → MapPopUpStore 도메인 모델로 변환 - static func convert(_ dto: MapPopUpStoreDTO) -> MapPopUpStore { - return MapPopUpStore( - id: dto.id, - category: dto.categoryName, - name: dto.name, - address: dto.address, - startDate: dto.startDate, - endDate: dto.endDate, - latitude: dto.latitude, - longitude: dto.longitude, - markerId: dto.markerId, - markerTitle: dto.markerTitle, - markerSnippet: dto.markerSnippet, - mainImageUrl: dto.mainImageUrl - ) - } -} diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift index 2a519f4b..08ffdbee 100644 --- a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift @@ -27,7 +27,7 @@ final class MapRepositoryImpl: MapRepository { ), interceptor: TokenInterceptor() ) - .map { $0.popUpStoreList.map(MapDomainModelConverter.convert) } + .map { $0.popUpStoreList.map { $0.toDomain() } } } func searchStores( @@ -41,7 +41,7 @@ final class MapRepositoryImpl: MapRepository { ), interceptor: TokenInterceptor() ) - .map { $0.popUpStoreList.map(MapDomainModelConverter.convert) } + .map { $0.popUpStoreList.map { $0.toDomain() } } } func fetchCategories() -> Observable<[CategoryResponse]> { diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 3dfcb67f..1f11e19f 100644 --- a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -9,11 +9,11 @@ final class AdminUseCaseImpl: AdminUseCase { self.repository = repository } - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> { + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { return repository.fetchStoreList(query: query, page: page, size: size) } - func fetchStoreDetail(id: Int64) -> Observable { + func fetchStoreDetail(id: Int64) -> Observable { return repository.fetchStoreDetail(id: id) } diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift similarity index 83% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift index 2e7913aa..58db4f63 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreResponse.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -1,6 +1,6 @@ import Foundation -struct StoreResponse { +struct AdminStore { let id: Int64 let name: String let categoryName: String diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift similarity index 94% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift rename to Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index 352b7f9f..dc8f91ef 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/StoreDetailResponse.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -1,6 +1,6 @@ import Foundation -struct StoreDetailResponse { +struct AdminStoreDetail { let id: Int64 let name: String let categoryId: Int64 diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift index a30695e0..8f87a343 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift @@ -3,8 +3,8 @@ import Foundation import RxSwift protocol AdminRepository { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> - func fetchStoreDetail(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> + func fetchStoreDetail(id: Int64) -> Observable func createStore(params: CreateStoreParams) -> Completable diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift index b9a8161f..08c59b15 100644 --- a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift +++ b/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift @@ -3,8 +3,8 @@ import RxSwift protocol AdminUseCase { - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[StoreResponse]> - func fetchStoreDetail(id: Int64) -> Observable + func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> + func fetchStoreDetail(id: Int64) -> Observable func createStore(params: CreateStoreParams) -> Completable diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift index 8ca61403..4e9d8bc9 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift @@ -14,18 +14,18 @@ final class AdminReactor: Reactor { } enum Mutation { - case setStores([StoreResponse]) + case setStores([AdminStore]) case setIsLoading(Bool) case navigateToRegister(Bool) - case navigateToEdit(StoreResponse) // ✅ 수정 데이터 추가 + case navigateToEdit(AdminStore) // ✅ 수정 데이터 추가 } struct State { - var storeList: [StoreResponse] = [] + var storeList: [AdminStore] = [] var isLoading: Bool = false var shouldNavigateToRegister: Bool = false - var selectedStoreForEdit: StoreResponse? // ✅ 추가 + var selectedStoreForEdit: AdminStore? // ✅ 추가 } diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 7ef8fb60..fd4b5088 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -19,7 +19,7 @@ final class PopUpStoreRegisterReactor: Reactor { init( adminUseCase: AdminUseCase, presignedService: PreSignedService, - editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil + editingStore: AdminStore? = nil ) { self.adminUseCase = adminUseCase self.presignedService = presignedService @@ -89,7 +89,7 @@ final class PopUpStoreRegisterReactor: Reactor { case addDeletedImage(id: Int64, path: String) // 기존 스토어 데이터 설정 - case setStoreDetail(StoreDetailResponse) + case setStoreDetail(AdminStoreDetail) case setOriginalImageIds([String: Int64]) // UI 상태 관리 diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index ed6fd4d0..3a33d18e 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -20,7 +20,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { // MARK: - Initializer init( nickname: String, - editingStore: GetAdminPopUpStoreListResponseDTO.PopUpStore? = nil + editingStore: AdminStore? = nil ) { self.nickname = nickname self.mainView = PopUpRegisterView() diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift index e7468603..1789ce87 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift @@ -75,7 +75,7 @@ final class AdminStoreCell: UITableViewCell { } // MARK: - Configure - func configure(with store: StoreResponse) { + func configure(with store: AdminStore) { Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) titleLabel.text = store.name diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift index 108773ee..29c8b765 100644 --- a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift @@ -153,7 +153,7 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func showDeleteConfirmation(for store: StoreResponse) { + private func showDeleteConfirmation(for store: AdminStore) { let alert = UIAlertController( title: "삭제 확인", message: "\(store.name)을(를) 삭제하시겠습니까?", @@ -168,7 +168,7 @@ final class AdminViewController: BaseViewController, View { present(alert, animated: true) } - private func editStore(_ store: StoreResponse) { + private func editStore(_ store: AdminStore) { adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) .subscribe( @@ -201,7 +201,7 @@ final class AdminViewController: BaseViewController, View { .disposed(by: disposeBag) } - private func deleteStore(_ store: StoreResponse) { + private func deleteStore(_ store: AdminStore) { // 먼저 스토어 상세 정보를 가져와 모든 이미지 URL을 확인 adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) From af70a35746a2585d5329ddfdee64647a638d7dc4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:16:52 +0900 Subject: [PATCH 147/393] =?UTF-8?q?refactor/#112:=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=ED=99=94=20=EB=B0=8F=20Layer=EC=97=90=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 352 +++++++++++++ .../DIContainer/DIContainer.swift | 0 .../DIContainer/DependencyWrapper.swift | 0 .../Infrastructure/Extension/Date?+.swift | 0 .../Infrastructure/Extension/Reactive+.swift | 0 .../Infrastructure/Extension/String?+.swift | 0 .../Extension/UIApplication+.swift | 0 .../Extension/UICollectionReusableView+.swift | 0 .../Extension/UICollectionViewCell+.swift | 0 .../Infrastructure/Extension/UIColor+.swift | 0 .../Infrastructure/Extension/UIFont+.swift | 0 .../Infrastructure/Extension/UIImage+.swift | 0 .../Extension/UIImageView+.swift | 0 .../Infrastructure/Extension/UILabel+.swift | 0 .../Extension/UINavigationController+.swift | 0 .../Extension/UITableViewCell+.swift | 0 .../Extension/UITextField+.swift | 0 .../Infrastructure/Extension/UIView+.swift | 0 .../ImageLoader/DiskStorage.swift | 0 .../ImageLoader/ImageLoader.swift | 0 .../ImageLoader/MemoryStorage.swift | 0 .../Infrastructure/Logger/Logger.swift | 0 .../Infrastructure/Secrets.swift | 0 .../Service/AppleLoginService.swift | 0 .../Service/AuthServiceable.swift | 0 .../Service/KakaoLoginService.swift | 0 .../Service/KeyChainService.swift | 0 .../Service/PreSignedService.swift | 0 .../Service/UserDefaultService.swift | 0 .../Data/Data.xcodeproj/project.pbxproj | 349 +++++++++++++ .../API/AdminAPI/AdminAPIEndpoint.swift | 0 .../ResponseDTO/AdminResponseDTO.swift | 0 .../GetAdminPopUpStoreListResponseDTO.swift | 0 .../Network/API/AuthAPI/AuthAPIEndPoint.swift | 0 .../ResponseDTO/LoginResponseDTO.swift | 0 .../PostTokenReissueResponseDTO.swift | 0 .../API/CommentAPI/CommentAPIEndPoint.swift | 0 .../RequestDTO/DeleteCommentRequestDTO.swift | 0 .../RequestDTO/PostCommentRequestDTO.swift | 0 .../RequestDTO/PutCommentRequestDTO.swift | 0 .../Network/API/HomeAPI/HomeAPIEndpoint.swift | 0 .../RequestDTO/HomeSortedRequestDTO.swift | 0 .../ResponseDTO/BannerPopUpStoreDTO.swift | 0 .../ResponseDTO/GetHomeInfoResponseDTO.swift | 0 .../ResponseDTO/PopUpStoreResponseDTO.swift | 0 .../API/MapAPI/FindDirectionEndPoint.swift | 0 .../Network/API/MapAPI/MapAPIEndpoint.swift | 0 .../GetPopUpDirectionResponseDTO.swift | 0 .../MapAPI/ResponseDTO/MapPopUpStoreDTO.swift | 0 .../API/PopUpAPI/PopUpAPIEndPoint.swift | 0 .../GetPopUpCommentRequestDTO.swift | 0 .../RequestDTO/GetPopUpDetailRequestDTO.swift | 0 .../GetSearchPopUpListRequestDTO.swift | 0 .../GetClosePopUpListResponseDTO.swift | 0 .../GetOpenPopUpListResponseDTO.swift | 0 .../GetPopUpCommentResponseDTO.swift | 0 .../GetPopUpDetailResponseDTO.swift | 0 .../GetSearchPopUpListResponseDTO.swift | 0 .../PreSignedAPI/PreSignedAPIEndPoint.swift | 0 .../RequestDTO/PresignedURLRequestDTO.swift | 0 .../ResponseDTO/PreSignedURLDTO.swift | 0 .../ResponseDTO/PreSignedURLResponseDTO.swift | 0 .../RequestDTO/CheckNickNameRequestDTO.swift | 0 .../RequestDTO/SignUpRequestDTO.swift | 0 .../GetCategoryListResponseDTO.swift | 0 .../API/SignUpAPI/SignUpAPIEndpoint.swift | 0 .../RequesetDTO/CommentLikeRequestDTO.swift | 0 .../RequesetDTO/GetMyCommentRequestDTO.swift | 0 .../GetOtherUserCommentListRequestDTO.swift | 0 .../PostBookmarkPopUpRequestDTO.swift | 0 .../RequesetDTO/PostUserBlockRequestDTO.swift | 0 .../PutUserCategoryRequestDTO.swift | 0 .../PutUserProfileRequestDTO.swift | 0 .../PutUserTailoredInfoRequestDTO.swift | 0 .../RequesetDTO/UserSortedRequestDTO.swift | 0 .../GetBlockUserListResponseDTO.swift | 0 .../GetMyCommentedPopUpResponseDTO.swift | 0 .../ResponseDTO/GetMyPageResponseDTO.swift | 0 .../ResponseDTO/GetMyProfileResponseDTO.swift | 0 .../GetNoticeDetailResponseDTO.swift | 0 .../GetNoticeListResponseDTO.swift | 0 ...herUserCommentedPopUpListResponseDTO.swift | 0 .../GetRecentPopUpResponseDTO.swift | 0 .../GetWithdrawlListResponseDTO.swift | 0 .../Network/API/UserAPI/UserAPIEndPoint.swift | 0 .../Data}/Network/Common/NetworkError.swift | 0 .../Data}/Network/Common/Requestable.swift | 0 .../Data}/Network/Common/Responsable.swift | 0 .../Data}/Network/EndPoint/Endpoint.swift | 0 .../Network/EndPoint/MultipartEndPoint.swift | 0 .../Network/EndPoint/RequestEndpoint.swift | 0 .../Interceptor/TokenInterceptor.swift | 0 .../Data}/Network/Provider/Provider.swift | 0 .../Data}/Network/Provider/ProviderImpl.swift | 0 .../RepositoryImpl/AdminRepositoryImpl.swift | 0 .../AuthAPIRepositoryImpl.swift | 0 .../CommentAPIRepositoryImpl.swift | 0 .../HomeAPIRepositoryImpl.swift | 0 .../MapDirectionRepositoryImpl.swift | 0 .../RepositoryImpl/MapRepositoryImpl.swift | 0 .../PopUpAPIRepositoryImpl.swift | 0 .../RepositoryImpl/SignUpRepositoryImpl.swift | 0 .../UserAPIRepositoryImpl.swift | 0 .../Domain/Domain.xcodeproj/project.pbxproj | 488 ++++++++++++++++++ .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 0 .../UseCaseImpl/AuthAPIUseCaseImpl.swift | 0 .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 0 .../UseCaseImpl/HomeAPIUseCaseImpl.swift | 0 .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 0 .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 0 .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 0 .../UseCaseImpl/UserAPIUseCaseImpl.swift | 0 .../Entity/AdminResponse/AdminStore.swift | 0 .../AdminResponse/AdminStoreDetail.swift | 0 .../AdminResponse/Params/AdminParams.swift | 0 .../AuthResponse/CategoryResponse.swift | 0 .../Entity/AuthResponse/LoginResponse.swift | 0 .../PostTokenReissueResponse.swift | 0 .../HomeResponse/BannerPopUpStore.swift | 0 .../HomeResponse/GetHomeInfoResponse.swift | 0 .../Entity/MapResponse/MapPopUpStore.swift | 0 .../GetPopUpCommentResponse.swift | 0 .../GetPopUpDetailResponse.swift | 0 .../GetSearchBottomPopUpListResponse.swift | 0 .../GetSearchPopUpListResponse.swift | 0 .../PopUpResponse/PopUpStoreResponse.swift | 0 .../GetBlockUserListResponse.swift | 0 .../UserResponse/GetMyCommentResponse.swift | 0 .../UserResponse/GetMyPageResponse.swift | 0 .../UserResponse/GetMyProfileResponse.swift | 0 .../GetNoticeDetailResponse.swift | 0 .../UserResponse/GetNoticeListResponse.swift | 0 ...tOtherUserCommentedPopUpListResponse.swift | 0 .../UserResponse/GetRecentPopUpResponse.swift | 0 .../GetWithdrawlListResponse.swift | 0 .../Repository/AdminRepository.swift | 0 .../Repository/AuthAPIRepository.swift | 0 .../Repository/CommentAPIRepository.swift | 0 .../Repository/HomeAPIRepository.swift | 0 .../Repository/MapDirectionRepository.swift | 0 .../Repository/MapRepository.swift | 0 .../Repository/PopUpAPIRepository.swift | 0 .../Repository/SignUpRepository.swift | 0 .../Repository/UserAPIRepository.swift | 0 .../UseCase/AdminUseCase.swift | 0 .../UseCase/AuthAPIUseCase.swift | 0 .../UseCase/CommentAPIUseCase.swift | 0 .../UseCase/HomeAPIUseCase.swift | 0 .../DomainInterface/UseCase/MapUseCase.swift | 0 .../UseCase/PopUpAPIUseCase.swift | 0 .../UseCase/SignUpAPIUseCase.swift | 0 .../UseCase/UserAPIUseCase.swift | 0 Poppool/Poppool.xcodeproj/project.pbxproj | 4 +- .../contents.xcworkspacedata | 35 ++ .../xcshareddata/swiftpm/Package.resolved | 168 ++++++ .../{ => Resource}/Poppool.entitlements | 0 .../Presentation.xcodeproj/project.pbxproj | 352 +++++++++++++ .../Presentation/Components/PPButton.swift | 0 .../Components/PPCancelHeaderView.swift | 0 .../Presentation/Components/PPLabel.swift | 0 .../Presentation/Components/PPPicker.swift | 0 .../PPProgressIndicator.swift | 0 .../PPProgressIndicator/PPProgressView.swift | 0 .../Components/PPReturnHeaderView.swift | 0 .../Components/PPSegmentedControl.swift | 0 .../AdminBottomSheetReactor.swift | 0 .../AdminBottomSheetView.swift | 0 .../AdminBottomSheetViewController.swift | 0 .../Scene/Admin/AdminReactor.swift | 0 .../PopUpImagesCollectionView.swift | 0 .../PopUpStoreRegisterReactor.swift | 0 .../PopUpStoreRegisterView.swift | 0 .../PopUpStoreRegisterViewController.swift | 0 .../Scene/Admin/AdminStoreCell.swift | 0 .../Presentation/Scene/Admin/AdminView.swift | 0 .../Scene/Admin/AdminViewController.swift | 0 .../Presentation/Scene/Admin/ImageCell.swift | 0 .../CommentCheck/CommentCheckController.swift | 0 .../CommentCheck/CommentCheckReactor.swift | 0 .../CommentCheck/CommentCheckView.swift | 0 .../CommentDetailController.swift | 0 .../CommentDetail/CommentDetailReactor.swift | 0 .../CommentDetailContentSection.swift | 0 .../CommentDetailContentSectionCell.swift | 0 .../View/CommentDetailImageSection.swift | 0 .../View/CommentDetailView.swift | 0 .../CommentMyMenuController.swift | 0 .../CommentMyMenu/CommentMyMenuReactor.swift | 0 .../CommentMyMenu/CommentMyMenuView.swift | 0 .../CommentUserInfoController.swift | 0 .../CommentUserInfoReactor.swift | 0 .../CommentUserInfo/CommentUserInfoView.swift | 0 .../CommentList/CommentListController.swift | 0 .../CommentList/CommentListReactor.swift | 0 .../CommentListTitleSection.swift | 0 .../CommentListTitleSectionCell.swift | 0 .../CommentList/View/CommentListView.swift | 0 .../CommentSelectedController.swift | 0 .../CommentSelectedReactor.swift | 0 .../CommentSelected/CommentSelectedView.swift | 0 .../CommentUserBlockController.swift | 0 .../CommentUserBlockReactor.swift | 0 .../CommentUserBlockView.swift | 0 .../InstaCommentAddController.swift | 0 .../InstaComment/InstaCommentAddReactor.swift | 0 .../View/InstaCommentAddView.swift | 0 .../InstaGuideChildSection.swift | 0 .../InstaGuideChildSectionCell.swift | 0 .../InstaGuideSection/InstaGuideSection.swift | 0 .../InstaGuideSectionCell.swift | 0 .../NormalCommentAddController.swift | 0 .../NormalCommentAddReactor.swift | 0 .../AddCommentDescriptionSection.swift | 0 .../AddCommentDescriptionSectionCell.swift | 0 .../AddCommentImageSection.swift | 0 .../AddCommentImageSectionCell.swift | 0 .../AddCommentSection/AddCommentSection.swift | 0 .../AddCommentSectionCell.swift | 0 .../AddCommentTitleSection.swift | 0 .../AddCommentTitleSectionCell.swift | 0 .../View/NormalCommentAddView.swift | 0 .../NormalCommentEditController.swift | 0 .../NormalCommentEditReactor.swift | 0 .../NormalCommentEditView.swift | 0 .../OtherUserCommentController.swift | 0 .../OtherUserCommentReactor.swift | 0 .../OtherUserCommentSection.swift | 0 .../OtherUserCommentSectionCell.swift | 0 .../View/OtherUserCommentView.swift | 0 .../Scene/Detail/DetailController.swift | 0 .../Scene/Detail/DetailReactor.swift | 0 .../DetailCommentImageCell.swift | 0 .../DetailCommentProfileView.swift | 0 .../DetailCommentSection.swift | 0 .../DetailCommentSectionCell.swift | 0 .../DetailCommentTitleSection.swift | 0 .../DetailCommentTitleSectionCell.swift | 0 .../DetailContentSection.swift | 0 .../DetailContentSectionCell.swift | 0 .../DetailEmptyCommetSection.swift | 0 .../DetailEmptyCommetSectionCell.swift | 0 .../DetailInfoSection/DetailInfoSection.swift | 0 .../DetailInfoSectionCell.swift | 0 .../DetailSimilarSection.swift | 0 .../DetailSimilarSectionCell.swift | 0 .../DetailTitleSection.swift | 0 .../DetailTitleSectionCell.swift | 0 .../Scene/Detail/View/DetailView.swift | 0 .../Scene/Home/List/HomeListController.swift | 0 .../Scene/Home/List/HomeListReactor.swift | 0 .../Scene/Home/List/HomePopUpType.swift | 0 .../Home/List/View/HomeCardGridSection.swift | 0 .../Scene/Home/List/View/HomeListView.swift | 0 .../Scene/Home/Main/HomeController.swift | 0 .../Scene/Home/Main/HomeReactor.swift | 0 .../HomeCardSection/HomeCardSection.swift | 0 .../HomeCardSection/HomeCardSectionCell.swift | 0 .../Scene/Home/Main/View/HomeHeaderView.swift | 0 .../HomePopularCardSection.swift | 0 .../HomePopularCardSectionCell.swift | 0 .../HomeTitleSection/HomeTitleSection.swift | 0 .../HomeTitleSectionCell.swift | 0 .../Scene/Home/Main/View/HomeView.swift | 0 .../ImageBannerChildSection.swift | 0 .../ImageBannerChildSectionCell.swift | 0 .../ImageBannerSection.swift | 0 .../ImageBannerSectionCell.swift | 0 .../SectionBackGroundDecorationView.swift | 0 .../View/SpacingSection/SpacingSection.swift | 0 .../SpacingSection/SpacingSectionCell.swift | 0 .../ImageDetail/ImageDetailController.swift | 0 .../ImageDetail/ImageDetailReactor.swift | 0 .../Scene/ImageDetail/ImageDetailView.swift | 0 .../Scene/Login/LastLoginView.swift | 0 .../Scene/Login/Main/LoginController.swift | 0 .../Scene/Login/Main/LoginReactor.swift | 0 .../Scene/Login/Main/LoginView.swift | 0 .../Scene/Login/Sub/SubLoginController.swift | 0 .../Scene/Login/Sub/SubLoginReactor.swift | 0 .../Scene/Login/Sub/SubLoginView.swift | 0 .../BalloonBackgroundView.swift | 0 .../FillterSheetView/BalloonChipCell.swift | 0 .../FilterBottomSheetReactor.swift | 0 .../FilterBottomSheetView.swift | 0 .../FilterBottomSheetViewController.swift | 0 .../Map/FillterSheetView/FilterCell.swift | 0 .../Map/FillterSheetView/FilterChip.swift | 0 .../FillterSheetView/FilterChipsView.swift | 0 .../FullScreenMapViewController.swift | 0 .../MapGuideView/MapGuideReactor.swift | 0 .../MapGuideView/MapGuideViewController.swift | 0 .../MapPopupCarouselView.swift | 0 .../Map/MapPopupCardView/PopupCardCell.swift | 0 .../Scene/Map/MapView/MapMarker.swift | 0 .../Scene/Map/MapView/MapReactor.swift | 0 .../Scene/Map/MapView/MapSearchInput.swift | 0 .../Scene/Map/MapView/MapView.swift | 0 .../Scene/Map/MapView/MapViewController.swift | 0 .../Scene/Map/MapView/MarkerTooltipView.swift | 0 .../Map/StoreListView/StoreListCell.swift | 0 .../Map/StoreListView/StoreListReactor.swift | 0 .../Map/StoreListView/StoreListSection.swift | 0 .../Map/StoreListView/StoreListView.swift | 0 .../StoreListViewController.swift | 0 .../Block/BlockUserManageController.swift | 0 .../MyPage/Block/BlockUserManageReactor.swift | 0 .../BlockUserListSection.swift | 0 .../BlockUserListSectionCell.swift | 0 .../Block/View/BlockUserManageView.swift | 0 .../Main/MyPageBookmarkController.swift | 0 .../Bookmark/Main/MyPageBookmarkReactor.swift | 0 .../Bookmark/Main/MyPageBookmarkView.swift | 0 .../Bookmark/Main/View/CountButtonView.swift | 0 .../PopUpCardSection/PopUpCardSection.swift | 0 .../PopUpCardSectionCell.swift | 0 .../View/PopUpCardSection/PopUpCardView.swift | 0 ...BookMarkPopUpViewTypeModalController.swift | 0 .../BookMarkPopUpViewTypeModalReactor.swift | 0 .../BookMarkPopUpViewTypeModalView.swift | 0 .../Scene/MyPage/FAQ/FAQController.swift | 0 .../Scene/MyPage/FAQ/FAQReactor.swift | 0 .../FAQDropdownSection.swift | 0 .../FAQDropdownSectionCell.swift | 0 .../Scene/MyPage/FAQ/View/FAQView.swift | 0 .../Scene/MyPage/Main/MyPageController.swift | 0 .../Scene/MyPage/Main/MyPageReactor.swift | 0 .../MyPageCommentSection.swift | 0 .../MyPageCommentSectionCell.swift | 0 .../MyPageListSection/MyPageListSection.swift | 0 .../MyPageListSectionCell.swift | 0 .../MyPageLogoutSection.swift | 0 .../MyPageLogoutSectionCell.swift | 0 .../MyPageMyCommentTitleSection.swift | 0 .../MyPageMyCommentTitleSectionCell.swift | 0 .../MyPageProfileSection.swift | 0 .../MyPageProfileSectionCell.swift | 0 .../Scene/MyPage/Main/View/MyPageView.swift | 0 .../MyComment/Main/MyCommentController.swift | 0 .../MyComment/Main/MyCommentReactor.swift | 0 .../ListCountButtonSection.swift | 0 .../ListCountButtonSectionCell.swift | 0 .../MyComment/Main/View/MyCommentView.swift | 0 .../MyCommentedPopUpGridSection.swift | 0 .../MyCommentedPopUpGridSectionCell.swift | 0 .../MyCommentSortedModalController.swift | 0 .../MyCommentSortedModalReactor.swift | 0 .../MyCommentSortedModalView.swift | 0 .../Detail/MyPageNoticeDetailController.swift | 0 .../Detail/MyPageNoticeDetailReactor.swift | 0 .../Detail/MyPageNoticeDetailView.swift | 0 .../Notice/List/MyPageNoticeController.swift | 0 .../Notice/List/MyPageNoticeReactor.swift | 0 .../Notice/List/View/MyPageNoticeView.swift | 0 .../NoticeListSection/NoticeListSection.swift | 0 .../NoticeListSectionCell.swift | 0 .../CategoryEditModalController.swift | 0 .../CategoryEditModalReactor.swift | 0 .../CategoryEditModalView.swift | 0 .../InfoEditModalController.swift | 0 .../InfoEditModal/InfoEditModalReactor.swift | 0 .../InfoEditModal/InfoEditModalView.swift | 0 .../Main/ProfileEditController.swift | 0 .../ProfileEdit/Main/ProfileEditReactor.swift | 0 .../Main/View/ProfileEditListButton.swift | 0 .../Main/View/ProfileEditView.swift | 0 .../Recent/MyPageRecentController.swift | 0 .../MyPage/Recent/MyPageRecentReactor.swift | 0 .../MyPage/Recent/View/MyPageRecentView.swift | 0 .../RecentPopUpSection.swift | 0 .../MyPage/Terms/MyPageTermsController.swift | 0 .../MyPage/Terms/MyPageTermsReactor.swift | 0 .../WithdrawlCheckModalController.swift | 0 .../WithdrawlCheckModalReactor.swift | 0 .../CheckModal/WithdrawlCheckModalView.swift | 0 .../WithdrawlCompleteController.swift | 0 .../Complete/WithdrawlCompleteView.swift | 0 .../View/WithdrawlCheckSection.swift | 0 .../View/WithdrawlCheckSectionCell.swift | 0 .../View/WithdrawlReasonView.swift | 0 .../WithdrawlReasonController.swift | 0 .../WithdrawlReasonReactor.swift | 0 .../AfterSearch/SearchResultController.swift | 0 .../AfterSearch/SearchResultReactor.swift | 0 .../SearchResultCountSection.swift | 0 .../SearchResultCountSectionCell.swift | 0 .../AfterSearch/View/SearchResultView.swift | 0 .../BeforeSearch/SearchController.swift | 0 .../Search/BeforeSearch/SearchReactor.swift | 0 .../CancelableTagSection.swift | 0 .../CancelableTagSectionCell.swift | 0 .../SearchCountTitleSection.swift | 0 .../SearchCountTitleSectionCell.swift | 0 .../SearchTitleSection.swift | 0 .../SearchTitleSectionCell.swift | 0 .../Search/BeforeSearch/View/SearchView.swift | 0 .../SearchCategoryController.swift | 0 .../SearchCategoryReactor.swift | 0 .../SearchCategoryView.swift | 0 .../Search/Main/SearchMainController.swift | 0 .../Scene/Search/Main/SearchMainReactor.swift | 0 .../Scene/Search/Main/SearchMainView.swift | 0 .../SearchSortedController.swift | 0 .../SearchSortedReactor.swift | 0 .../SortedController/SearchSortedView.swift | 0 .../SignUp/Main/SignUpMainController.swift | 0 .../Scene/SignUp/Main/SignUpMainReactor.swift | 0 .../SignUp/Main/View/SignUpMainView.swift | 0 .../SignUpCompleteController.swift | 0 .../SignUpCompleteReactor.swift | 0 .../SignUpComplete/SignUpCompleteView.swift | 0 .../SignUp/Step1/SignUpStep1Controller.swift | 0 .../SignUp/Step1/SignUpStep1Reactor.swift | 0 .../Step1/View/SignUpCheckBoxButton.swift | 0 .../SignUp/Step1/View/SignUpStep1View.swift | 0 .../SignUp/Step1/View/SignUpTermsView.swift | 0 .../Scene/SignUp/Step2/IntroState.swift | 0 .../Scene/SignUp/Step2/NickNameState.swift | 0 .../SignUp/Step2/SignUpStep2Controller.swift | 0 .../SignUp/Step2/SignUpStep2Reactor.swift | 0 .../Scene/SignUp/Step2/SignUpStep2View.swift | 0 .../SignUp/Step3/SignUpStep3Controller.swift | 0 .../SignUp/Step3/SignUpStep3Reactor.swift | 0 .../SignUp/Step3/View/SignUpStep3View.swift | 0 .../Step3/View/TagSection/TagSection.swift | 0 .../View/TagSection/TagSectionCell.swift | 0 .../AgeSelectedController.swift | 0 .../AgeSelectedModal/AgeSelectedReactor.swift | 0 .../AgeSelectedModal/AgeSelectedView.swift | 0 .../Step4/Main/SignUpStep4Controller.swift | 0 .../Step4/Main/SignUpStep4Reactor.swift | 0 .../Step4/Main/View/AgeSelectedButton.swift | 0 .../Step4/Main/View/SignUpStep4View.swift | 0 .../TermsDetail/TermsDetailController.swift | 0 .../SignUp/TermsDetail/TermsDetailView.swift | 0 .../Scene/Splash/SplashController.swift | 0 .../Scene/Splash/View/SplashView.swift | 0 .../TabbarController/TabbarController.swift | 0 .../Utills/Common/ClusteringManager.swift | 0 .../Utills/Common/ClusteringModels.swift | 0 .../Utills/Common/DateTimePickerManager.swift | 0 .../Utills/Common/ExtendedImage.swift | 0 .../Utills/Common/FilterType.swift | 0 .../LocationPermissionBottomSheet.swift | 0 .../Utills/Common/MapFilterChips.swift | 0 .../Utills/Common/MapUtilities.swift | 0 .../Common/NMFMapViewDelegateProxy.swift | 0 .../Utills/Common/RegionDefinitions.swift | 0 .../Controllers/BaseTabmanController.swift | 0 .../Controllers/BaseViewController.swift | 0 .../Utills/Interfaces/InOutputable.swift | 0 .../Sectionable/SectionDecorationItem.swift | 0 .../SectionSupplementaryItem.swift | 0 .../Interfaces/Sectionable/Sectionable.swift | 0 .../Utills/ToastMaker/BookMarkToastView.swift | 0 .../Utills/ToastMaker/ToastMaker.swift | 0 .../Utills/ToastMaker/ToastView.swift | 0 456 files changed, 1746 insertions(+), 2 deletions(-) create mode 100644 Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/DIContainer/DIContainer.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/DIContainer/DependencyWrapper.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/Date?+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/Reactive+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/String?+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIApplication+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UICollectionReusableView+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UICollectionViewCell+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIColor+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIFont+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIImage+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIImageView+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UILabel+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UINavigationController+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UITableViewCell+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UITextField+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Extension/UIView+.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/ImageLoader/DiskStorage.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/ImageLoader/ImageLoader.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/ImageLoader/MemoryStorage.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Logger/Logger.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Secrets.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/AppleLoginService.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/AuthServiceable.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/KakaoLoginService.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/KeyChainService.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/PreSignedService.swift (100%) rename Poppool/{Poppool/CoreLayer => CoreLayer/Infrastructure}/Infrastructure/Service/UserDefaultService.swift (100%) create mode 100644 Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AdminAPI/AdminAPIEndpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AuthAPI/AuthAPIEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/CommentAPI/CommentAPIEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/HomeAPI/HomeAPIEndpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/MapAPI/FindDirectionEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/MapAPI/MapAPIEndpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/PopUpAPIEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/SignUpAPI/SignUpAPIEndpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/API/UserAPI/UserAPIEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Common/NetworkError.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Common/Requestable.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Common/Responsable.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/EndPoint/Endpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/EndPoint/MultipartEndPoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/EndPoint/RequestEndpoint.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Interceptor/TokenInterceptor.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Provider/Provider.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/Network/Provider/ProviderImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/AdminRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/AuthAPIRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/CommentAPIRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/HomeAPIRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/MapDirectionRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/MapRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/PopUpAPIRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/SignUpRepositoryImpl.swift (100%) rename Poppool/{Poppool/DataLayer => DataLayer/Data/Data}/RepositoryImpl/UserAPIRepositoryImpl.swift (100%) create mode 100644 Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/AdminUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/MapUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AdminResponse/AdminStore.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AuthResponse/CategoryResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AuthResponse/LoginResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/MapResponse/MapPopUpStore.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/AdminRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/AuthAPIRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/CommentAPIRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/HomeAPIRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/MapDirectionRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/MapRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/PopUpAPIRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/SignUpRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/Repository/UserAPIRepository.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/AdminUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/AuthAPIUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/CommentAPIUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/HomeAPIUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/MapUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/PopUpAPIUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/SignUpAPIUseCase.swift (100%) rename Poppool/{Poppool/DomainLayer => DomainLayer/Domain}/DomainInterface/UseCase/UserAPIUseCase.swift (100%) create mode 100644 Poppool/Poppool.xcworkspace/contents.xcworkspacedata create mode 100644 Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved rename Poppool/Poppool/{ => Resource}/Poppool.entitlements (100%) create mode 100644 Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPButton.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPCancelHeaderView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPLabel.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPPicker.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPProgressIndicator/PPProgressView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPReturnHeaderView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Components/PPSegmentedControl.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminStoreCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/AdminViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Admin/ImageCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentList/CommentListController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentList/CommentListReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentList/View/CommentListView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/DetailController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/DetailReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Detail/View/DetailView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/List/HomeListController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/List/HomeListReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/List/HomePopUpType.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/List/View/HomeCardGridSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/List/View/HomeListView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/HomeController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/HomeReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeHeaderView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/HomeView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/ImageDetail/ImageDetailController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/ImageDetail/ImageDetailReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/ImageDetail/ImageDetailView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/LastLoginView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Main/LoginController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Main/LoginReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Main/LoginView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Sub/SubLoginController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Sub/SubLoginReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Login/Sub/SubLoginView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterChip.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MapMarker.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MapReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MapSearchInput.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MapView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MapViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/MapView/MarkerTooltipView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/StoreListView/StoreListCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/StoreListView/StoreListReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/StoreListView/StoreListSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/StoreListView/StoreListView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Map/StoreListView/StoreListViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Block/BlockUserManageController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/FAQ/FAQController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/FAQ/FAQReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/FAQ/View/FAQView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/MyPageController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/MyPageReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Main/View/MyPageView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/AfterSearch/SearchResultController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/SearchController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/Main/SearchMainController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/Main/SearchMainReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/Main/SearchMainView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/SortedController/SearchSortedController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Search/SortedController/SearchSortedView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Main/SignUpMainController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step2/IntroState.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step2/NickNameState.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Splash/SplashController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/Splash/View/SplashView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Scene/TabbarController/TabbarController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/ClusteringManager.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/ClusteringModels.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/DateTimePickerManager.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/ExtendedImage.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/FilterType.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/LocationPermissionBottomSheet.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/MapFilterChips.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/MapUtilities.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Common/RegionDefinitions.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Controllers/BaseTabmanController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Controllers/BaseViewController.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Interfaces/InOutputable.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/ToastMaker/BookMarkToastView.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/ToastMaker/ToastMaker.swift (100%) rename Poppool/{Poppool/PresentationLayer => PresentationLayer/Presentation}/Presentation/Utills/ToastMaker/ToastView.swift (100%) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj new file mode 100644 index 00000000..cdc524ab --- /dev/null +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -0,0 +1,352 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 058CC9182DB5383C0084221A /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 058CC91A2DB5383C0084221A /* Infrastructure */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Infrastructure; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 058CC9152DB5383C0084221A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058CC90E2DB5383C0084221A = { + isa = PBXGroup; + children = ( + 058CC91A2DB5383C0084221A /* Infrastructure */, + 058CC9192DB5383C0084221A /* Products */, + ); + sourceTree = ""; + }; + 058CC9192DB5383C0084221A /* Products */ = { + isa = PBXGroup; + children = ( + 058CC9182DB5383C0084221A /* Infrastructure.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 058CC9132DB5383C0084221A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 058CC9172DB5383C0084221A /* Infrastructure */ = { + isa = PBXNativeTarget; + buildConfigurationList = 058CC91F2DB5383C0084221A /* Build configuration list for PBXNativeTarget "Infrastructure" */; + buildPhases = ( + 058CC9132DB5383C0084221A /* Headers */, + 058CC9142DB5383C0084221A /* Sources */, + 058CC9152DB5383C0084221A /* Frameworks */, + 058CC9162DB5383C0084221A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 058CC91A2DB5383C0084221A /* Infrastructure */, + ); + name = Infrastructure; + packageProductDependencies = ( + ); + productName = Infrastructure; + productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 058CC90F2DB5383C0084221A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 058CC9172DB5383C0084221A = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 058CC9122DB5383C0084221A /* Build configuration list for PBXProject "Infrastructure" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 058CC90E2DB5383C0084221A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 058CC9192DB5383C0084221A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 058CC9172DB5383C0084221A /* Infrastructure */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 058CC9162DB5383C0084221A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 058CC9142DB5383C0084221A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 058CC91D2DB5383C0084221A /* 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.4; + 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; + }; + 058CC91E2DB5383C0084221A /* 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.4; + 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; + }; + 058CC9202DB5383C0084221A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Infrastructure; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 058CC9212DB5383C0084221A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Infrastructure; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 058CC9122DB5383C0084221A /* Build configuration list for PBXProject "Infrastructure" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC91D2DB5383C0084221A /* Debug */, + 058CC91E2DB5383C0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 058CC91F2DB5383C0084221A /* Build configuration list for PBXNativeTarget "Infrastructure" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC9202DB5383C0084221A /* Debug */, + 058CC9212DB5383C0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 058CC90F2DB5383C0084221A /* Project object */; +} diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DIContainer.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/DIContainer/DependencyWrapper.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/Date?+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/Date?+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/Reactive+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/Reactive+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/String?+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/String?+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIApplication+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIApplication+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIApplication+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIApplication+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionReusableView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionReusableView+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionReusableView+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionReusableView+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionViewCell+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionViewCell+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UICollectionViewCell+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionViewCell+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIColor+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIColor+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIFont+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIFont+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIFont+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImage+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImage+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImage+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImage+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIImageView+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UILabel+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UILabel+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UILabel+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UILabel+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UINavigationController+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UINavigationController+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UINavigationController+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UINavigationController+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITableViewCell+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITableViewCell+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITableViewCell+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITableViewCell+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITextField+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITextField+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UITextField+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITextField+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIView+.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Extension/UIView+.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIView+.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/DiskStorage.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/DiskStorage.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/DiskStorage.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/DiskStorage.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/ImageLoader.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/MemoryStorage.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/MemoryStorage.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/ImageLoader/MemoryStorage.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/MemoryStorage.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Logger/Logger.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Secrets.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Secrets.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/AppleLoginService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/AppleLoginService.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/AuthServiceable.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AuthServiceable.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/AuthServiceable.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AuthServiceable.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/KakaoLoginService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/KakaoLoginService.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/KeyChainService.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/PreSignedService.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/PreSignedService.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/PreSignedService.swift diff --git a/Poppool/Poppool/CoreLayer/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift similarity index 100% rename from Poppool/Poppool/CoreLayer/Infrastructure/Service/UserDefaultService.swift rename to Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj new file mode 100644 index 00000000..1d9b6350 --- /dev/null +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -0,0 +1,349 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 058CC8DC2DB5376A0084221A /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 05C1D05C2DB5387500508FFD /* Data */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Data; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 058CC8D92DB5376A0084221A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058CC8D22DB5376A0084221A = { + isa = PBXGroup; + children = ( + 05C1D05C2DB5387500508FFD /* Data */, + 058CC8DD2DB5376A0084221A /* Products */, + ); + sourceTree = ""; + }; + 058CC8DD2DB5376A0084221A /* Products */ = { + isa = PBXGroup; + children = ( + 058CC8DC2DB5376A0084221A /* Data.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 058CC8D72DB5376A0084221A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 058CC8DB2DB5376A0084221A /* Data */ = { + isa = PBXNativeTarget; + buildConfigurationList = 058CC8E32DB5376A0084221A /* Build configuration list for PBXNativeTarget "Data" */; + buildPhases = ( + 058CC8D72DB5376A0084221A /* Headers */, + 058CC8D82DB5376A0084221A /* Sources */, + 058CC8D92DB5376A0084221A /* Frameworks */, + 058CC8DA2DB5376A0084221A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Data; + packageProductDependencies = ( + ); + productName = Data; + productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 058CC8D32DB5376A0084221A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 058CC8DB2DB5376A0084221A = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 058CC8D62DB5376A0084221A /* Build configuration list for PBXProject "Data" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 058CC8D22DB5376A0084221A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 058CC8DD2DB5376A0084221A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 058CC8DB2DB5376A0084221A /* Data */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 058CC8DA2DB5376A0084221A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 058CC8D82DB5376A0084221A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 058CC8E12DB5376A0084221A /* 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.4; + 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; + }; + 058CC8E22DB5376A0084221A /* 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.4; + 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; + }; + 058CC8E42DB5376A0084221A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Data; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 058CC8E52DB5376A0084221A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Data; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 058CC8D62DB5376A0084221A /* Build configuration list for PBXProject "Data" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC8E12DB5376A0084221A /* Debug */, + 058CC8E22DB5376A0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 058CC8E32DB5376A0084221A /* Build configuration list for PBXNativeTarget "Data" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC8E42DB5376A0084221A /* Debug */, + 058CC8E52DB5376A0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 058CC8D32DB5376A0084221A /* Project object */; +} diff --git a/Poppool/Poppool/DataLayer/Network/API/AdminAPI/AdminAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AdminAPI/AdminAPIEndpoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/AuthAPI/AuthAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/AuthAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AuthAPI/AuthAPIEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/AuthAPI/AuthAPIEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/CommentAPI/CommentAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/CommentAPI/CommentAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/CommentAPI/CommentAPIEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/CommentAPI/CommentAPIEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/HomeAPI/HomeAPIEndpoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/FindDirectionEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/MapAPI/FindDirectionEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/MapAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/MapAPI/MapAPIEndpoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/MapAPI/MapAPIEndpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/PopUpAPIEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/SignUpAPI/SignUpAPIEndpoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift diff --git a/Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/API/UserAPI/UserAPIEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/Common/NetworkError.swift b/Poppool/DataLayer/Data/Data/Network/Common/NetworkError.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Common/NetworkError.swift rename to Poppool/DataLayer/Data/Data/Network/Common/NetworkError.swift diff --git a/Poppool/Poppool/DataLayer/Network/Common/Requestable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Common/Requestable.swift rename to Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift diff --git a/Poppool/Poppool/DataLayer/Network/Common/Responsable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Common/Responsable.swift rename to Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift diff --git a/Poppool/Poppool/DataLayer/Network/EndPoint/Endpoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/EndPoint/Endpoint.swift rename to Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/EndPoint/MultipartEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/EndPoint/MultipartEndPoint.swift rename to Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/EndPoint/RequestEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/RequestEndpoint.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/EndPoint/RequestEndpoint.swift rename to Poppool/DataLayer/Data/Data/Network/EndPoint/RequestEndpoint.swift diff --git a/Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift b/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Interceptor/TokenInterceptor.swift rename to Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift diff --git a/Poppool/Poppool/DataLayer/Network/Provider/Provider.swift b/Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Provider/Provider.swift rename to Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift diff --git a/Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/Network/Provider/ProviderImpl.swift rename to Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/AdminRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/AuthAPIRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/CommentAPIRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/HomeAPIRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/MapDirectionRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/MapRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/PopUpAPIRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/SignUpRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift diff --git a/Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift similarity index 100% rename from Poppool/Poppool/DataLayer/RepositoryImpl/UserAPIRepositoryImpl.swift rename to Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj new file mode 100644 index 00000000..477f9ea6 --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -0,0 +1,488 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 058CC8F02DB5377F0084221A /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 058CC8F22DB5377F0084221A /* Domain */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Domain; + sourceTree = ""; + }; + 05C1D0DC2DB538A600508FFD /* DomainInterface */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = DomainInterface; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 058CC8ED2DB5377F0084221A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05C1D0D82DB538A600508FFD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058CC8E62DB5377F0084221A = { + isa = PBXGroup; + children = ( + 058CC8F22DB5377F0084221A /* Domain */, + 05C1D0DC2DB538A600508FFD /* DomainInterface */, + 058CC8F12DB5377F0084221A /* Products */, + ); + sourceTree = ""; + }; + 058CC8F12DB5377F0084221A /* Products */ = { + isa = PBXGroup; + children = ( + 058CC8F02DB5377F0084221A /* Domain.framework */, + 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 058CC8EB2DB5377F0084221A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05C1D0D62DB538A600508FFD /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 058CC8EF2DB5377F0084221A /* Domain */ = { + isa = PBXNativeTarget; + buildConfigurationList = 058CC8F72DB5377F0084221A /* Build configuration list for PBXNativeTarget "Domain" */; + buildPhases = ( + 058CC8EB2DB5377F0084221A /* Headers */, + 058CC8EC2DB5377F0084221A /* Sources */, + 058CC8ED2DB5377F0084221A /* Frameworks */, + 058CC8EE2DB5377F0084221A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 058CC8F22DB5377F0084221A /* Domain */, + ); + name = Domain; + packageProductDependencies = ( + ); + productName = Domain; + productReference = 058CC8F02DB5377F0084221A /* Domain.framework */; + productType = "com.apple.product-type.framework"; + }; + 05C1D0DA2DB538A600508FFD /* DomainInterface */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05C1D0DF2DB538A600508FFD /* Build configuration list for PBXNativeTarget "DomainInterface" */; + buildPhases = ( + 05C1D0D62DB538A600508FFD /* Headers */, + 05C1D0D72DB538A600508FFD /* Sources */, + 05C1D0D82DB538A600508FFD /* Frameworks */, + 05C1D0D92DB538A600508FFD /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 05C1D0DC2DB538A600508FFD /* DomainInterface */, + ); + name = DomainInterface; + packageProductDependencies = ( + ); + productName = DomainInterface; + productReference = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 058CC8E72DB5377F0084221A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 058CC8EF2DB5377F0084221A = { + CreatedOnToolsVersion = 16.3; + }; + 05C1D0DA2DB538A600508FFD = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 058CC8EA2DB5377F0084221A /* Build configuration list for PBXProject "Domain" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 058CC8E62DB5377F0084221A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 058CC8F12DB5377F0084221A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 058CC8EF2DB5377F0084221A /* Domain */, + 05C1D0DA2DB538A600508FFD /* DomainInterface */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 058CC8EE2DB5377F0084221A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05C1D0D92DB538A600508FFD /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 058CC8EC2DB5377F0084221A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 05C1D0D72DB538A600508FFD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 058CC8F52DB5377F0084221A /* 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.4; + 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; + }; + 058CC8F62DB5377F0084221A /* 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.4; + 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; + }; + 058CC8F82DB5377F0084221A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Domain; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 058CC8F92DB5377F0084221A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Domain; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 05C1D0E02DB538A600508FFD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.DomainInterface; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 05C1D0E12DB538A600508FFD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.DomainInterface; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 058CC8EA2DB5377F0084221A /* Build configuration list for PBXProject "Domain" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC8F52DB5377F0084221A /* Debug */, + 058CC8F62DB5377F0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 058CC8F72DB5377F0084221A /* Build configuration list for PBXNativeTarget "Domain" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC8F82DB5377F0084221A /* Debug */, + 058CC8F92DB5377F0084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 05C1D0DF2DB538A600508FFD /* Build configuration list for PBXNativeTarget "DomainInterface" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05C1D0E02DB538A600508FFD /* Debug */, + 05C1D0E12DB538A600508FFD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 058CC8E72DB5377F0084221A /* Project object */; +} diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AdminUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/MapUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift rename to Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStore.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/CategoryResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/LoginResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/LoginResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/MapResponse/MapPopUpStore.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/AdminRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/AuthAPIRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/CommentAPIRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/HomeAPIRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapDirectionRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapDirectionRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/MapRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/PopUpAPIRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/SignUpRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/Repository/UserAPIRepository.swift rename to Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AdminUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AuthAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/AuthAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/CommentAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/HomeAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/HomeAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/MapUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/PopUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/PopUpAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/SignUpAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift diff --git a/Poppool/Poppool/DomainLayer/DomainInterface/UseCase/UserAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift similarity index 100% rename from Poppool/Poppool/DomainLayer/DomainInterface/UseCase/UserAPIUseCase.swift rename to Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index d87bba1e..ac472c55 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -373,7 +373,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; + CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; @@ -419,7 +419,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = Poppool/Poppool.entitlements; + CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..5e470cb2 --- /dev/null +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..c64560f9 --- /dev/null +++ b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,168 @@ +{ + "originHash" : "893e9d5956a6d674d140654334c58c276c99001321206803c07c48415f2e6ac3", + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire.git", + "state" : { + "revision" : "513364f870f6bfc468f9d2ff0a95caccc10044c5", + "version" : "5.10.2" + } + }, + { + "identity" : "floatingpanel", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scenee/FloatingPanel.git", + "state" : { + "revision" : "a1f20cedb14bd1ddc63e30a8dd10c85e8f1fa011", + "version" : "2.8.7" + } + }, + { + "identity" : "kakao-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/kakao/kakao-ios-sdk.git", + "state" : { + "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", + "version" : "2.24.0" + } + }, + { + "identity" : "lottie-spm", + "kind" : "remoteSourceControl", + "location" : "https://github.com/airbnb/lottie-spm.git", + "state" : { + "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", + "version" : "4.5.1" + } + }, + { + "identity" : "pageboy", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Pageboy.git", + "state" : { + "revision" : "be0c1f6f1964cfb07f9d819b0863f2c3f255f612", + "version" : "4.2.0" + } + }, + { + "identity" : "panmodal", + "kind" : "remoteSourceControl", + "location" : "https://github.com/slackhq/PanModal.git", + "state" : { + "revision" : "b012aecb6b67a8e46369227f893c12544846613f", + "version" : "1.2.7" + } + }, + { + "identity" : "reactorkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/ReactorKit.git", + "state" : { + "revision" : "8fa33f09c6f6621a2aa536d739956d53b84dd139", + "version" : "3.2.0" + } + }, + { + "identity" : "rxdatasources", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxDataSources.git", + "state" : { + "revision" : "90c29b48b628479097fe775ed1966d75ac374518", + "version" : "5.0.2" + } + }, + { + "identity" : "rxgesture", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxGesture.git", + "state" : { + "revision" : "1b137c576b4aaaab949235752278956697c9e4a0", + "version" : "4.0.4" + } + }, + { + "identity" : "rxkeyboard", + "kind" : "remoteSourceControl", + "location" : "https://github.com/RxSwiftCommunity/RxKeyboard.git", + "state" : { + "revision" : "63f6377975c962a1d89f012a6f1e5bebb2c502b7", + "version" : "2.0.1" + } + }, + { + "identity" : "rxswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactiveX/RxSwift.git", + "state" : { + "revision" : "5dd1907d64f0d36f158f61a466bab75067224893", + "version" : "6.9.0" + } + }, + { + "identity" : "snapkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/SnapKit/SnapKit.git", + "state" : { + "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4", + "version" : "5.7.1" + } + }, + { + "identity" : "spm-nmapsgeometry", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsGeometry.git", + "state" : { + "revision" : "436d5e2e684f557faf5ef5862fd6633a42d7af11", + "version" : "1.0.2" + } + }, + { + "identity" : "spm-nmapsmap", + "kind" : "remoteSourceControl", + "location" : "https://github.com/navermaps/SPM-NMapsMap", + "state" : { + "revision" : "ad89e53fdfec3b8d8994280fb0414b5a7b1c3e8e", + "version" : "3.21.0" + } + }, + { + "identity" : "swiftsoup", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scinfu/SwiftSoup", + "state" : { + "revision" : "bba848db50462894e7fc0891d018dfecad4ef11e", + "version" : "2.8.7" + } + }, + { + "identity" : "tabman", + "kind" : "remoteSourceControl", + "location" : "https://github.com/uias/Tabman.git", + "state" : { + "revision" : "3b2213290eb93e55bb50b49d1a179033005c11ab", + "version" : "3.2.0" + } + }, + { + "identity" : "then", + "kind" : "remoteSourceControl", + "location" : "https://github.com/devxoul/Then", + "state" : { + "revision" : "d41ef523faef0f911369f79c0b96815d9dbb6d7a", + "version" : "3.0.0" + } + }, + { + "identity" : "weakmaptable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/ReactorKit/WeakMapTable.git", + "state" : { + "revision" : "cb05d64cef2bbf51e85c53adee937df46540a74e", + "version" : "1.2.1" + } + } + ], + "version" : 3 +} diff --git a/Poppool/Poppool/Poppool.entitlements b/Poppool/Poppool/Resource/Poppool.entitlements similarity index 100% rename from Poppool/Poppool/Poppool.entitlements rename to Poppool/Poppool/Resource/Poppool.entitlements diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj new file mode 100644 index 00000000..f58fe33f --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -0,0 +1,352 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 058CC9042DB537960084221A /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 058CC9062DB537960084221A /* Presentation */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = Presentation; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 058CC9012DB537960084221A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058CC8FA2DB537960084221A = { + isa = PBXGroup; + children = ( + 058CC9062DB537960084221A /* Presentation */, + 058CC9052DB537960084221A /* Products */, + ); + sourceTree = ""; + }; + 058CC9052DB537960084221A /* Products */ = { + isa = PBXGroup; + children = ( + 058CC9042DB537960084221A /* Presentation.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 058CC8FF2DB537960084221A /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 058CC9032DB537960084221A /* Presentation */ = { + isa = PBXNativeTarget; + buildConfigurationList = 058CC90B2DB537960084221A /* Build configuration list for PBXNativeTarget "Presentation" */; + buildPhases = ( + 058CC8FF2DB537960084221A /* Headers */, + 058CC9002DB537960084221A /* Sources */, + 058CC9012DB537960084221A /* Frameworks */, + 058CC9022DB537960084221A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 058CC9062DB537960084221A /* Presentation */, + ); + name = Presentation; + packageProductDependencies = ( + ); + productName = Presentation; + productReference = 058CC9042DB537960084221A /* Presentation.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 058CC8FB2DB537960084221A /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 058CC9032DB537960084221A = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 058CC8FE2DB537960084221A /* Build configuration list for PBXProject "Presentation" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 058CC8FA2DB537960084221A; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 058CC9052DB537960084221A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 058CC9032DB537960084221A /* Presentation */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 058CC9022DB537960084221A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 058CC9002DB537960084221A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 058CC9092DB537960084221A /* 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.4; + 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; + }; + 058CC90A2DB537960084221A /* 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.4; + 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; + }; + 058CC90C2DB537960084221A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Presentation; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 058CC90D2DB537960084221A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + 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"; + 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.Presentation; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 058CC8FE2DB537960084221A /* Build configuration list for PBXProject "Presentation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC9092DB537960084221A /* Debug */, + 058CC90A2DB537960084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 058CC90B2DB537960084221A /* Build configuration list for PBXNativeTarget "Presentation" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058CC90C2DB537960084221A /* Debug */, + 058CC90D2DB537960084221A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 058CC8FB2DB537960084221A /* Project object */; +} diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPButton.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPButton.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPButton.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPCancelHeaderView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPCancelHeaderView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPCancelHeaderView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPCancelHeaderView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPLabel.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPLabel.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPLabel.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPLabel.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPPicker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPPicker.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPPicker.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPPicker.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPProgressIndicator/PPProgressView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPReturnHeaderView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPReturnHeaderView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPReturnHeaderView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPReturnHeaderView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Components/PPSegmentedControl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Components/PPSegmentedControl.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Components/PPSegmentedControl.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Components/PPSegmentedControl.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminStoreCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/AdminViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/ImageCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Admin/ImageCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/CommentListReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentList/View/CommentListView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/DetailReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Detail/View/DetailView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomeListReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomePopUpType.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomePopUpType.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/HomePopUpType.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomePopUpType.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeCardGridSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeCardGridSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeListView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/List/View/HomeListView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/HomeReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeHeaderView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeHeaderView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/HomeView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/ImageDetail/ImageDetailView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/LastLoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/LastLoginView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/LastLoginView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Main/LoginView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Login/Sub/SubLoginView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChip.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChip.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/MapPopupCarouselView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapMarker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapMarker.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapSearchInput.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapSearchInput.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MapViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MarkerTooltipView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/MapView/MarkerTooltipView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Map/StoreListView/StoreListViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/FAQReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/FAQ/View/FAQView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/MyPageReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Main/View/MyPageView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditListButton.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/Main/SearchMainView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Search/SortedController/SearchSortedView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/IntroState.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/IntroState.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/IntroState.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/IntroState.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/NickNameState.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/NickNameState.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/NickNameState.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/NickNameState.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/SplashController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/View/SplashView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/View/SplashView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/Splash/View/SplashView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/View/SplashView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Scene/TabbarController/TabbarController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringManager.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringManager.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringModels.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ClusteringModels.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/DateTimePickerManager.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/DateTimePickerManager.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/DateTimePickerManager.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/DateTimePickerManager.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ExtendedImage.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ExtendedImage.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/ExtendedImage.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ExtendedImage.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/FilterType.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/FilterType.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/FilterType.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/LocationPermissionBottomSheet.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/LocationPermissionBottomSheet.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/LocationPermissionBottomSheet.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/LocationPermissionBottomSheet.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapFilterChips.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/MapFilterChips.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapFilterChips.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/MapFilterChips.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapUtilities.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/MapUtilities.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/MapUtilities.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/MapUtilities.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/NMFMapViewDelegateProxy.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/RegionDefinitions.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/RegionDefinitions.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Common/RegionDefinitions.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/RegionDefinitions.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseTabmanController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Controllers/BaseViewController.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/InOutputable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/InOutputable.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/BookMarkToastView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/BookMarkToastView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastMaker.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift diff --git a/Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastView.swift similarity index 100% rename from Poppool/Poppool/PresentationLayer/Presentation/Utills/ToastMaker/ToastView.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastView.swift From 3dd99219de56acf73ea49a907447e661e93fdded Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:19:41 +0900 Subject: [PATCH 148/393] =?UTF-8?q?chore/#112:=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EA=B2=BD=EA=B3=A0=20=ED=95=B4?= =?UTF-8?q?=EC=A0=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EB=B9=8C=EB=93=9C=20?= =?UTF-8?q?=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 --- .../Infrastructure.xcodeproj/project.pbxproj | 4 ++-- Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj | 4 ++-- .../DomainLayer/Domain/Domain.xcodeproj/project.pbxproj | 8 ++++---- .../Presentation/Presentation.xcodeproj/project.pbxproj | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index cdc524ab..c0f697aa 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -264,7 +264,7 @@ 058CC9202DB5383C0084221A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -296,7 +296,7 @@ 058CC9212DB5383C0084221A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 1d9b6350..7a207670 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -261,7 +261,7 @@ 058CC8E42DB5376A0084221A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -293,7 +293,7 @@ 058CC8E52DB5376A0084221A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index 477f9ea6..57b425c2 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -327,7 +327,7 @@ 058CC8F82DB5377F0084221A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -359,7 +359,7 @@ 058CC8F92DB5377F0084221A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -391,7 +391,7 @@ 05C1D0E02DB538A600508FFD /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -423,7 +423,7 @@ 05C1D0E12DB538A600508FFD /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index f58fe33f..acc077fa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -264,7 +264,7 @@ 058CC90C2DB537960084221A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; @@ -296,7 +296,7 @@ 058CC90D2DB537960084221A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DYLIB_COMPATIBILITY_VERSION = 1; From a832442e5bbe4bd2f5453c715ebd9f9dad83f8bc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:20:26 +0900 Subject: [PATCH 149/393] =?UTF-8?q?refactor/#112:=20=EC=9D=B8=ED=84=B0?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=8A=A4=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C?= =?UTF-8?q?=EC=96=B4=EC=9E=90=EB=A5=BC=20public=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 --- .../DomainInterface/Repository/AdminRepository.swift | 2 +- .../DomainInterface/Repository/AuthAPIRepository.swift | 3 ++- .../Repository/CommentAPIRepository.swift | 2 +- .../DomainInterface/Repository/HomeAPIRepository.swift | 2 +- .../Repository/MapDirectionRepository.swift | 2 +- .../DomainInterface/Repository/MapRepository.swift | 9 +-------- .../DomainInterface/Repository/PopUpAPIRepository.swift | 2 +- .../DomainInterface/Repository/SignUpRepository.swift | 2 +- .../DomainInterface/Repository/UserAPIRepository.swift | 2 +- .../Domain/DomainInterface/UseCase/AdminUseCase.swift | 3 ++- .../Domain/DomainInterface/UseCase/AuthAPIUseCase.swift | 2 +- .../DomainInterface/UseCase/CommentAPIUseCase.swift | 2 +- .../Domain/DomainInterface/UseCase/HomeAPIUseCase.swift | 2 +- .../Domain/DomainInterface/UseCase/MapUseCase.swift | 2 +- .../Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift | 2 +- .../DomainInterface/UseCase/SignUpAPIUseCase.swift | 2 +- .../Domain/DomainInterface/UseCase/UserAPIUseCase.swift | 2 +- 17 files changed, 19 insertions(+), 24 deletions(-) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift index 8f87a343..4a9f2972 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AdminRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol AdminRepository { +public protocol AdminRepository { func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> func fetchStoreDetail(id: Int64) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift index ba9ae29f..33154753 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AuthAPIRepository.swift @@ -1,7 +1,8 @@ import Foundation + import RxSwift -protocol AuthAPIRepository { +public protocol AuthAPIRepository { func tryLogIn(userCredential: Encodable, socialType: String) -> Observable func postTokenReissue() -> Observable } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift index e353e65c..3562db2c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol CommentAPIRepository { +public protocol CommentAPIRepository { func postCommentAdd( popUpStoreId: Int64, content: String?, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift index 4b7f0bb1..d417f03a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/HomeAPIRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol HomeAPIRepository { +public protocol HomeAPIRepository { func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift index d73ff0a0..b933470a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift @@ -2,6 +2,6 @@ import Foundation import RxSwift -protocol MapDirectionRepository { +public protocol MapDirectionRepository { func getPopUpDirection(popUpStoreId: Int64) -> Observable } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift index 4e6c1a19..4f086267 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift @@ -1,15 +1,8 @@ -// -// MapRepository.swift -// Poppool -// -// Created by 송영훈 on 4/14/25. -// - import Foundation import RxSwift -protocol MapRepository { +public protocol MapRepository { func fetchStoresInBounds( northEastLat: Double, northEastLon: Double, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift index d03a9666..b6c000ec 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/PopUpAPIRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol PopUpAPIRepository { +public protocol PopUpAPIRepository { func postBookmarkPopUp(popUpStoreId: Int64) -> Completable func getClosePopUpList( diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift index 6e7ef1b0..7ce31985 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol SignUpRepository { +public protocol SignUpRepository { func checkNickName(nickName: String) -> Observable func fetchCategoryList() -> Observable<[CategoryResponse]> func trySignUp( diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift index 3012787d..2b2658a0 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol UserAPIRepository { +public protocol UserAPIRepository { func postBookmarkPopUp(popUpStoreId: Int64) -> Completable func deleteBookmarkPopUp(popUpStoreId: Int64) -> Completable func postCommentLike(commentId: Int64) -> Completable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift index 08c59b15..5e083d73 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AdminUseCase.swift @@ -1,7 +1,8 @@ import Foundation + import RxSwift -protocol AdminUseCase { +public protocol AdminUseCase { func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> func fetchStoreDetail(id: Int64) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift index 0ccfaa35..44ce761c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AuthAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol AuthAPIUseCase { +public protocol AuthAPIUseCase { func postTryLogin(userCredential: Encodable, socialType: String) -> Observable func postTokenReissue() -> Observable } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift index 021c842a..5114f0d3 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/CommentAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol CommentAPIUseCase { +public protocol CommentAPIUseCase { func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [String?]?) -> Completable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift index 8bb6b392..30f6d2f0 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/HomeAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol HomeAPIUseCase { +public protocol HomeAPIUseCase { func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift index f425adf0..b4c82a70 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol MapUseCase { +public protocol MapUseCase { func fetchCategories() -> Observable<[CategoryResponse]> func fetchStoresInBounds( northEastLat: Double, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift index 1e122002..7748b47f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol PopUpAPIUseCase { +public protocol PopUpAPIUseCase { func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable func getSearchPopUpList(query: String?) -> Observable func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool?) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift index 8711e4a7..dc8fb97d 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol SignUpAPIUseCase { +public protocol SignUpAPIUseCase { func trySignUp( nickName: String, gender: String, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift index e73f4cdf..6f74f9f8 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift @@ -2,7 +2,7 @@ import Foundation import RxSwift -protocol UserAPIUseCase { +public protocol UserAPIUseCase { func postBookmarkPopUp(popUpID: Int64) -> Completable func deleteBookmarkPopUp(popUpID: Int64) -> Completable func postCommentLike(commentId: Int64) -> Completable From 8e1998a21fc5722c92916d2f4527dba82623f993 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:22:16 +0900 Subject: [PATCH 150/393] =?UTF-8?q?chore/#112:=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=EA=B0=84=20=ED=95=84=EC=9A=94=ED=95=9C=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 37 ++++++++++ .../Domain/Domain.xcodeproj/project.pbxproj | 71 +++++++++++++++++++ Poppool/Poppool.xcodeproj/project.pbxproj | 46 ++++++++++++ .../Presentation.xcodeproj/project.pbxproj | 37 ++++++++++ 4 files changed, 191 insertions(+) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 7a207670..2a120ba4 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -6,8 +6,32 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; }; + 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; + 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 05C1D61B2DB53A5600508FFD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */, + 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 058CC8DC2DB5376A0084221A /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D6152DB53A5600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D6162DB53A5600508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -23,6 +47,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, + 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -33,6 +59,7 @@ isa = PBXGroup; children = ( 05C1D05C2DB5387500508FFD /* Data */, + 05C1D6142DB53A5600508FFD /* Frameworks */, 058CC8DD2DB5376A0084221A /* Products */, ); sourceTree = ""; @@ -45,6 +72,15 @@ name = Products; sourceTree = ""; }; + 05C1D6142DB53A5600508FFD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05C1D6152DB53A5600508FFD /* DomainInterface.framework */, + 05C1D6162DB53A5600508FFD /* Infrastructure.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -66,6 +102,7 @@ 058CC8D82DB5376A0084221A /* Sources */, 058CC8D92DB5376A0084221A /* Frameworks */, 058CC8DA2DB5376A0084221A /* Resources */, + 05C1D61B2DB53A5600508FFD /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index 57b425c2..b5d78435 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -6,9 +6,56 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; }; + 05C1D61F2DB53A6700508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; }; + 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; }; + 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 05C1D6202DB53A6700508FFD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 058CC8E72DB5377F0084221A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05C1D0DA2DB538A600508FFD; + remoteInfo = DomainInterface; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 05C1D6242DB53A6700508FFD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05C1D61F2DB53A6700508FFD /* DomainInterface.framework in Embed Frameworks */, + 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 05C1D6282DB53A6E00508FFD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 058CC8F02DB5377F0084221A /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -29,6 +76,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */, + 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -36,6 +85,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -47,6 +97,7 @@ children = ( 058CC8F22DB5377F0084221A /* Domain */, 05C1D0DC2DB538A600508FFD /* DomainInterface */, + 05C1D61C2DB53A6700508FFD /* Frameworks */, 058CC8F12DB5377F0084221A /* Products */, ); sourceTree = ""; @@ -60,6 +111,15 @@ name = Products; sourceTree = ""; }; + 05C1D61C2DB53A6700508FFD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */, + 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -88,10 +148,12 @@ 058CC8EC2DB5377F0084221A /* Sources */, 058CC8ED2DB5377F0084221A /* Frameworks */, 058CC8EE2DB5377F0084221A /* Resources */, + 05C1D6242DB53A6700508FFD /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 05C1D6212DB53A6700508FFD /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 058CC8F22DB5377F0084221A /* Domain */, @@ -111,6 +173,7 @@ 05C1D0D72DB538A600508FFD /* Sources */, 05C1D0D82DB538A600508FFD /* Frameworks */, 05C1D0D92DB538A600508FFD /* Resources */, + 05C1D6282DB53A6E00508FFD /* Embed Frameworks */, ); buildRules = ( ); @@ -198,6 +261,14 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 05C1D6212DB53A6700508FFD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05C1D0DA2DB538A600508FFD /* DomainInterface */; + targetProxy = 05C1D6202DB53A6700508FFD /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ 058CC8F52DB5377F0084221A /* Debug */ = { isa = XCBuildConfiguration; diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index ac472c55..b89c06d8 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -7,6 +7,14 @@ objects = { /* Begin PBXBuildFile section */ + 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; + 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; }; + 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; }; + 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; }; + 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 082197A12D426DCB0054094A /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 082197A02D426DCB0054094A /* Then */; }; 083A25D02CF364B70099B58E /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 083A25CF2CF364B70099B58E /* Alamofire */; }; 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 088DE2432D104EE70030FA9E /* SwiftSoup */; }; @@ -28,8 +36,29 @@ BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA420F2CF35FF5005EECF6 /* Lottie */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + 05C1D6132DB53A4900508FFD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */, + 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */, + 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */, + 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 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; }; + 05C1D6092DB53A4900508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D60A2DB53A4900508FFD /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDCA41BD2CF35AC0005EECF6 /* Poppool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Poppool.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -65,13 +94,17 @@ buildActionMask = 2147483647; files = ( BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */, + 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */, BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, + 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */, 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, + 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */, + 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, @@ -89,11 +122,23 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 05C1D6062DB53A4900508FFD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05C1D6072DB53A4900508FFD /* Data.framework */, + 05C1D6082DB53A4900508FFD /* Domain.framework */, + 05C1D6092DB53A4900508FFD /* Infrastructure.framework */, + 05C1D60A2DB53A4900508FFD /* Presentation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; BDCA41B42CF35AC0005EECF6 = { isa = PBXGroup; children = ( 05229DD02D99519200D88E73 /* .swiftlint.yml */, 058789AD2DAD2E10004F81E2 /* Poppool */, + 05C1D6062DB53A4900508FFD /* Frameworks */, BDCA41BE2CF35AC0005EECF6 /* Products */, ); sourceTree = ""; @@ -117,6 +162,7 @@ BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, BDCA41BB2CF35AC0005EECF6 /* Resources */, + 05C1D6132DB53A4900508FFD /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index acc077fa..48d6a932 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -6,8 +6,32 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; + 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; + 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 05C1D6302DB53A8200508FFD /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */, + 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 058CC9042DB537960084221A /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -23,6 +47,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, + 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -33,6 +59,7 @@ isa = PBXGroup; children = ( 058CC9062DB537960084221A /* Presentation */, + 05C1D6292DB53A8200508FFD /* Frameworks */, 058CC9052DB537960084221A /* Products */, ); sourceTree = ""; @@ -45,6 +72,15 @@ name = Products; sourceTree = ""; }; + 05C1D6292DB53A8200508FFD /* Frameworks */ = { + isa = PBXGroup; + children = ( + 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */, + 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -66,6 +102,7 @@ 058CC9002DB537960084221A /* Sources */, 058CC9012DB537960084221A /* Frameworks */, 058CC9022DB537960084221A /* Resources */, + 05C1D6302DB53A8200508FFD /* Embed Frameworks */, ); buildRules = ( ); From 512dcde56346ff738c0dc0ad82c2b96e1cdc8441 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:24:23 +0900 Subject: [PATCH 151/393] =?UTF-8?q?chore/#112:=20Build=20phase=EC=97=90=20?= =?UTF-8?q?=EC=BB=B4=ED=8C=8C=EC=9D=BC=ED=95=A0=20=EC=86=8C=EC=8A=A4=20?= =?UTF-8?q?=EB=AA=A9=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 --- .../Data/Data.xcodeproj/project.pbxproj | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 2a120ba4..b05e0790 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -34,9 +34,94 @@ 05C1D6162DB53A5600508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 05C1D67A2DB53AB000508FFD /* Exceptions for "Data" folder in "Data" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Network/API/AdminAPI/AdminAPIEndpoint.swift, + Network/API/AdminAPI/ResponseDTO/AdminResponseDTO.swift, + Network/API/AdminAPI/ResponseDTO/GetAdminPopUpStoreListResponseDTO.swift, + Network/API/AuthAPI/AuthAPIEndPoint.swift, + Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift, + Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift, + Network/API/CommentAPI/CommentAPIEndPoint.swift, + Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift, + Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift, + Network/API/CommentAPI/RequestDTO/PutCommentRequestDTO.swift, + Network/API/HomeAPI/HomeAPIEndpoint.swift, + Network/API/HomeAPI/RequestDTO/HomeSortedRequestDTO.swift, + Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift, + Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift, + Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift, + Network/API/MapAPI/FindDirectionEndPoint.swift, + Network/API/MapAPI/MapAPIEndpoint.swift, + Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift, + Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift, + Network/API/PopUpAPI/PopUpAPIEndPoint.swift, + Network/API/PopUpAPI/RequestDTO/GetPopUpCommentRequestDTO.swift, + Network/API/PopUpAPI/RequestDTO/GetPopUpDetailRequestDTO.swift, + Network/API/PopUpAPI/RequestDTO/GetSearchPopUpListRequestDTO.swift, + Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift, + Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift, + Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift, + Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift, + Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift, + Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift, + Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift, + Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift, + Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift, + Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift, + Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift, + Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift, + Network/API/SignUpAPI/SignUpAPIEndpoint.swift, + Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/GetOtherUserCommentListRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/PostBookmarkPopUpRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/PostUserBlockRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/PutUserCategoryRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/PutUserProfileRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/PutUserTailoredInfoRequestDTO.swift, + Network/API/UserAPI/RequesetDTO/UserSortedRequestDTO.swift, + Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift, + Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift, + Network/API/UserAPI/UserAPIEndPoint.swift, + Network/Common/NetworkError.swift, + Network/Common/Requestable.swift, + Network/Common/Responsable.swift, + Network/EndPoint/Endpoint.swift, + Network/EndPoint/MultipartEndPoint.swift, + Network/EndPoint/RequestEndpoint.swift, + Network/Interceptor/TokenInterceptor.swift, + Network/Provider/Provider.swift, + Network/Provider/ProviderImpl.swift, + RepositoryImpl/AdminRepositoryImpl.swift, + RepositoryImpl/AuthAPIRepositoryImpl.swift, + RepositoryImpl/CommentAPIRepositoryImpl.swift, + RepositoryImpl/HomeAPIRepositoryImpl.swift, + RepositoryImpl/MapDirectionRepositoryImpl.swift, + RepositoryImpl/MapRepositoryImpl.swift, + RepositoryImpl/PopUpAPIRepositoryImpl.swift, + RepositoryImpl/SignUpRepositoryImpl.swift, + RepositoryImpl/UserAPIRepositoryImpl.swift, + ); + target = 058CC8DB2DB5376A0084221A /* Data */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + /* Begin PBXFileSystemSynchronizedRootGroup section */ 05C1D05C2DB5387500508FFD /* Data */ = { isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 05C1D67A2DB53AB000508FFD /* Exceptions for "Data" folder in "Data" target */, + ); path = Data; sourceTree = ""; }; From c6795b461c92bc75231f0232edf27332ff0ec4be Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:26:33 +0900 Subject: [PATCH 152/393] =?UTF-8?q?refactor/#112:=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=EC=B2=B4=EC=97=90=20DomainInterface=EB=A5=BC=20import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data/RepositoryImpl/AdminRepositoryImpl.swift | 5 ++++- .../Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift | 3 +++ .../Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift | 2 ++ .../Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift | 2 ++ .../Data/RepositoryImpl/MapDirectionRepositoryImpl.swift | 2 ++ .../Data/Data/RepositoryImpl/MapRepositoryImpl.swift | 2 ++ .../Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift | 2 ++ .../Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift | 2 ++ .../Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift | 2 ++ .../Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift | 3 +++ .../Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift | 3 +++ .../Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift | 3 +++ .../Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift | 2 ++ .../Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift | 2 ++ .../Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift | 2 ++ .../Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift | 2 ++ .../Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift | 2 ++ 17 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift index 5856ca40..73bf0cf3 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift @@ -1,5 +1,8 @@ -import Alamofire import Foundation + +import DomainInterface + +import Alamofire import RxSwift final class AdminRepositoryImpl: AdminRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift index c1f27826..bd991c59 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -1,4 +1,7 @@ import Foundation + +import DomainInterface + import RxSwift final class AuthAPIRepositoryImpl: AuthAPIRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift index 6fa51907..210c31c0 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class CommentAPIRepositoryImpl: CommentAPIRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift index bd4d02e2..fe8e4b44 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class HomeAPIRepositoryImpl: HomeAPIRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index 25a7791f..b9f8c297 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class MapDirectionRepositoryImpl: MapDirectionRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 08ffdbee..fa6b742c 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class MapRepositoryImpl: MapRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift index ab8e22d6..37600666 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class PopUpAPIRepositoryImpl: PopUpAPIRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift index d6b071d0..a3b4f7fe 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class SignUpRepositoryImpl: SignUpRepository { diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift index c8d3c00b..31ca397c 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class UserAPIRepositoryImpl: UserAPIRepository { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 1f11e19f..52c25f65 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -1,4 +1,7 @@ import Foundation + +import DomainInterface + import RxSwift final class AdminUseCaseImpl: AdminUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift index 258cb9e0..81aabcdc 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift @@ -1,4 +1,7 @@ import Foundation + +import DomainInterface + import RxSwift final class AuthAPIUseCaseImpl: AuthAPIUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index 96b13ae4..695e669f 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -1,4 +1,7 @@ import Foundation + +import DomainInterface + import RxSwift final class CommentAPIUseCaseImpl: CommentAPIUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index 2251ba97..17ee1e6f 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class HomeAPIUseCaseImpl: HomeAPIUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index 7ffd64c2..a6b7d558 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class MapUseCaseImpl: MapUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index c199fddb..b437cb38 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index 78c69f75..baf641ca 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index d13ea254..f33df46e 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + import RxSwift final class UserAPIUseCaseImpl: UserAPIUseCase { From c52cfd8492a91b5b94544bcbc5fc067d224da45b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:30:02 +0900 Subject: [PATCH 153/393] =?UTF-8?q?refactor/#112:=20=EC=99=B8=EB=B6=80?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=95=8C=EB=8F=84=EB=A1=9D=20Entity=20?= =?UTF-8?q?=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90=EB=A5=BC=20public?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/AdminResponse/AdminStore.swift | 2 +- .../Entity/AdminResponse/AdminStoreDetail.swift | 2 +- .../Entity/AdminResponse/Params/AdminParams.swift | 8 ++++---- .../Entity/AuthResponse/CategoryResponse.swift | 9 +-------- .../Entity/AuthResponse/LoginResponse.swift | 9 +-------- .../AuthResponse/PostTokenReissueResponse.swift | 9 +-------- .../Entity/HomeResponse/BannerPopUpStore.swift | 9 +-------- .../Entity/HomeResponse/GetHomeInfoResponse.swift | 9 +-------- .../Entity/MapResponse/MapPopUpStore.swift | 2 +- .../PopUpResponse/GetPopUpCommentResponse.swift | 9 +-------- .../PopUpResponse/GetPopUpDetailResponse.swift | 15 ++++----------- .../GetSearchBottomPopUpListResponse.swift | 9 +-------- .../GetSearchPopUpListResponse.swift | 9 +-------- .../Entity/PopUpResponse/PopUpStoreResponse.swift | 9 +-------- .../UserResponse/GetBlockUserListResponse.swift | 11 ++--------- .../UserResponse/GetMyCommentResponse.swift | 11 ++--------- .../Entity/UserResponse/GetMyPageResponse.swift | 11 ++--------- .../UserResponse/GetMyProfileResponse.swift | 9 +-------- .../UserResponse/GetNoticeDetailResponse.swift | 9 +-------- .../UserResponse/GetNoticeListResponse.swift | 11 ++--------- .../GetOtherUserCommentedPopUpListResponse.swift | 11 ++--------- .../UserResponse/GetRecentPopUpResponse.swift | 14 ++++---------- .../UserResponse/GetWithdrawlListResponse.swift | 11 ++--------- 23 files changed, 38 insertions(+), 170 deletions(-) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift index 58db4f63..54f2a211 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -1,6 +1,6 @@ import Foundation -struct AdminStore { +public struct AdminStore { let id: Int64 let name: String let categoryName: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index dc8f91ef..62c26415 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -1,6 +1,6 @@ import Foundation -struct AdminStoreDetail { +public struct AdminStoreDetail { let id: Int64 let name: String let categoryId: Int64 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift index 8f5a9d7e..f9262d37 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -1,6 +1,6 @@ import Foundation -struct CreateStoreParams { +public struct CreateStoreParams { let name: String let categoryId: Int64 let desc: String @@ -16,7 +16,7 @@ struct CreateStoreParams { let startDateBeforeEndDate: Bool } -struct UpdateStoreParams { +public struct UpdateStoreParams { let id: Int64 let name: String let categoryId: Int64 @@ -34,13 +34,13 @@ struct UpdateStoreParams { let startDateBeforeEndDate: Bool } -struct CreateNoticeParams { +public struct CreateNoticeParams { let title: String let content: String let imageUrlList: [String] } -struct UpdateNoticeParams { +public struct UpdateNoticeParams { let id: Int64 let title: String let content: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index 20972e3c..8951e68d 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -1,13 +1,6 @@ -// -// Category.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation -struct CategoryResponse { +public struct CategoryResponse { let categoryId: Int64 let category: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift index 7cb0f2a1..676a45b3 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift @@ -1,13 +1,6 @@ -// -// LoginResponse.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation -struct LoginResponse { +public struct LoginResponse { var userId: String var grantType: String var accessToken: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift index 299e9612..d661cfba 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift @@ -1,13 +1,6 @@ -// -// PostTokenReissueResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/16/25. -// - import Foundation -struct PostTokenReissueResponse { +public struct PostTokenReissueResponse { var accessToken: String? var refreshToken: String? var accessTokenExpiresAt: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift index 11868aaf..9f572658 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift @@ -1,13 +1,6 @@ -// -// BannerPopUpStore.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation -struct BannerPopUpStore { +public struct BannerPopUpStore { var id: Int64 var name: String var mainImageUrl: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift index a12a3fce..10a39158 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift @@ -1,13 +1,6 @@ -// -// GetHomeInfoResponse.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation -struct GetHomeInfoResponse { +public struct GetHomeInfoResponse { var bannerPopUpStoreList: [BannerPopUpStore] var nickname: String? var customPopUpStoreList: [PopUpStoreResponse] diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift index 65fc7ebb..5bf58009 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift @@ -1,6 +1,6 @@ import Foundation -struct MapPopUpStore: Equatable { +public struct MapPopUpStore: Equatable { let id: Int64 let category: String let name: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift index 9befaef6..2d60f542 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift @@ -1,12 +1,5 @@ -// -// GetPopUpCommentResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import Foundation -struct GetPopUpCommentResponse { +public struct GetPopUpCommentResponse { let commentList: [GetPopUpDetailCommentResponse] } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift index 55497518..c1b7b27a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift @@ -1,13 +1,6 @@ -// -// GetPopUpDetailResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import Foundation -struct GetPopUpDetailResponse { +public struct GetPopUpDetailResponse { let name: String? let desc: String? let startDate: String? @@ -25,12 +18,12 @@ struct GetPopUpDetailResponse { let similarPopUpStoreList: [GetPopUpDetailSimilarResponse] } -struct GetPopUpDetailImageResponse { +public struct GetPopUpDetailImageResponse { let id: Int64 let imageUrl: String? } -struct GetPopUpDetailCommentResponse { +public struct GetPopUpDetailCommentResponse { let commentId: Int64 let creator: String? let nickname: String? @@ -44,7 +37,7 @@ struct GetPopUpDetailCommentResponse { let commentImageList: [GetPopUpDetailImageResponse] } -struct GetPopUpDetailSimilarResponse { +public struct GetPopUpDetailSimilarResponse { let id: Int64 let name: String? let mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift index 9ae5c6b1..b639b478 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift @@ -1,13 +1,6 @@ -// -// GetSearchBottomPopUpListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation -struct GetSearchBottomPopUpListResponse { +public struct GetSearchBottomPopUpListResponse { var popUpStoreList: [PopUpStoreResponse] var loginYn: Bool var totalPages: Int32 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift index c0cb86f5..cca24b8a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift @@ -1,13 +1,6 @@ -// -// GetSearchPopUpListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import Foundation -struct GetSearchPopUpListResponse { +public struct GetSearchPopUpListResponse { var popUpStoreList: [PopUpStoreResponse] var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift index 48cd3914..255d95fb 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift @@ -1,13 +1,6 @@ -// -// PopUpStoreResponse.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation -struct PopUpStoreResponse { +public struct PopUpStoreResponse { let id: Int64 let category: String? let name: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift index bc7f1adc..253acf72 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift @@ -1,19 +1,12 @@ -// -// GetBlockUserListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import Foundation -struct GetBlockUserListResponse { +public struct GetBlockUserListResponse { var blockedUserInfoList: [GetBlockUserListDataResponse] var totalPages: Int32 var totalElements: Int32 } -struct GetBlockUserListDataResponse { +public struct GetBlockUserListDataResponse { var userId: String? var profileImageUrl: String? var nickname: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift index 6d47e920..9edacc86 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift @@ -1,17 +1,10 @@ -// -// GetMyCommentResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import Foundation -struct GetMyCommentedPopUpResponse { +public struct GetMyCommentedPopUpResponse { var popUpInfoList: [GetMyCommentedPopUpDataResponse] } -struct GetMyCommentedPopUpDataResponse { +public struct GetMyCommentedPopUpDataResponse { var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift index 0586e572..34faf968 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift @@ -1,13 +1,6 @@ -// -// GetMyPageResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// - import Foundation -struct GetMyPageResponse { +public struct GetMyPageResponse { var nickname: String? var profileImageUrl: String? var intro: String? @@ -17,7 +10,7 @@ struct GetMyPageResponse { var myCommentedPopUpList: [GetMyPagePopUpResponse] } -struct GetMyPagePopUpResponse { +public struct GetMyPagePopUpResponse { var popUpStoreId: Int64 var popUpStoreName: String? var mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift index 6c71ce4b..77f0be1b 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift @@ -1,13 +1,6 @@ -// -// GetMyProfileResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import Foundation -struct GetMyProfileResponse { +public struct GetMyProfileResponse { var profileImageUrl: String? var nickname: String? var email: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift index a259e9d4..dce77077 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift @@ -1,13 +1,6 @@ -// -// GetNoticeDetailResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import Foundation -struct GetNoticeDetailResponse { +public struct GetNoticeDetailResponse { var id: Int64 var title: String? var content: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift index 045e2164..cb0cbfb3 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift @@ -1,17 +1,10 @@ -// -// GetNoticeListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import Foundation -struct GetNoticeListResponse { +public struct GetNoticeListResponse { var noticeInfoList: [GetNoticeListDataResponse] } -struct GetNoticeListDataResponse { +public struct GetNoticeListDataResponse { var id: Int64 var title: String? var createdDateTime: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift index 71fce6de..af25688a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift @@ -1,17 +1,10 @@ -// -// GetOtherUserCommentedPopUpListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import Foundation -struct GetOtherUserCommentedPopUpListResponse { +public struct GetOtherUserCommentedPopUpListResponse { var popUpInfoList: [GetOtherUserCommentedPopUpResponse] } -struct GetOtherUserCommentedPopUpResponse { +public struct GetOtherUserCommentedPopUpResponse { var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift index 6960a262..5606d9d9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift @@ -1,19 +1,12 @@ -// -// GetRecentPopUpResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import Foundation -struct GetRecentPopUpResponse { +public struct GetRecentPopUpResponse { var popUpInfoList: [GetRecentPopUpDataResponse] var totalPages: Int32 var totalElements: Int32 } -struct GetRecentPopUpDataResponse { +public struct GetRecentPopUpDataResponse { var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? @@ -23,7 +16,8 @@ struct GetRecentPopUpDataResponse { var address: String? var closeYn: Bool } -extension GetRecentPopUpDataResponse { + +public extension GetRecentPopUpDataResponse { func toStoreItem() -> StoreItem { return StoreItem( id: self.popUpStoreId, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index 9bdf33d6..decf5f72 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -1,17 +1,10 @@ -// -// GetWithdrawlListResponse.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import Foundation -struct GetWithdrawlListResponse { +public struct GetWithdrawlListResponse { var withDrawlSurveyList: [GetWithdrawlListDataResponse] } -struct GetWithdrawlListDataResponse { +public struct GetWithdrawlListDataResponse { var id: Int64 var survey: String? } From 147f35fd972465e7c955439210c30f7781e7b8e9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:34:48 +0900 Subject: [PATCH 154/393] =?UTF-8?q?chore/#112:=20Infra=EC=97=90=20Rx,=20Ka?= =?UTF-8?q?kao=20=EA=B4=80=EB=A0=A8=20=ED=8C=A8=ED=82=A4=EC=A7=80=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 --- .../Infrastructure.xcodeproj/project.pbxproj | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index c0f697aa..8f7ecdc1 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -6,6 +6,13 @@ objectVersion = 77; objects = { +/* Begin PBXBuildFile section */ + 05C1D8302DB53CE300508FFD /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */; }; + 05C1D8322DB53CE300508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */; }; + 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; }; + 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; }; +/* End PBXBuildFile section */ + /* Begin PBXFileReference section */ 058CC9182DB5383C0084221A /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -23,6 +30,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */, + 05C1D8322DB53CE300508FFD /* RxSwift-Dynamic in Frameworks */, + 05C1D8302DB53CE300508FFD /* RxCocoa-Dynamic in Frameworks */, + 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +87,10 @@ ); name = Infrastructure; packageProductDependencies = ( + 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */, + 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */, + 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */, + 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */, ); productName = Infrastructure; productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; @@ -105,6 +120,10 @@ ); mainGroup = 058CC90E2DB5383C0084221A; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */, + 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9192DB5383C0084221A /* Products */; projectDirPath = ""; @@ -347,6 +366,48 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; + 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.24.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxCocoa-Dynamic"; + }; + 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxSwift-Dynamic"; + }; + 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC90F2DB5383C0084221A /* Project object */; } From aa9633719e645300df5163c23982d348098e82f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:35:06 +0900 Subject: [PATCH 155/393] =?UTF-8?q?chore/#112:=20Domain=EC=97=90=20?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=A8=ED=82=A4=EC=A7=80=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 --- .../Domain/Domain.xcodeproj/project.pbxproj | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index b5d78435..8b19b2ae 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; }; 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05C1D82A2DB53CC200508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */; }; + 05C1D82C2DB53CD100508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,6 +79,7 @@ buildActionMask = 2147483647; files = ( 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */, + 05C1D82A2DB53CC200508FFD /* RxSwift-Dynamic in Frameworks */, 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -85,6 +88,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05C1D82C2DB53CD100508FFD /* RxSwift-Dynamic in Frameworks */, 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -160,6 +164,7 @@ ); name = Domain; packageProductDependencies = ( + 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */, ); productName = Domain; productReference = 058CC8F02DB5377F0084221A /* Domain.framework */; @@ -184,6 +189,7 @@ ); name = DomainInterface; packageProductDependencies = ( + 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */, ); productName = DomainInterface; productReference = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; @@ -216,6 +222,9 @@ ); mainGroup = 058CC8E62DB5377F0084221A; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 058CC8F12DB5377F0084221A /* Products */; projectDirPath = ""; @@ -554,6 +563,30 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxSwift-Dynamic"; + }; + 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxSwift-Dynamic"; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8E72DB5377F0084221A /* Project object */; } From bf74f1e2e52c8e67a16fce5c20e37dc268e3a619 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 20 Apr 2025 23:39:40 +0900 Subject: [PATCH 156/393] =?UTF-8?q?refactor/#112:=20Logger=EC=99=80=20?= =?UTF-8?q?=EC=97=B0=EA=B4=80=EB=90=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Logger에서 필요한 Entity의 접근 제어자 수정 - Logger에서 사용하는 private 제거 --- .../Infrastructure/Logger/Logger.swift | 17 ++---- .../AdminResponse/Params/AdminParams.swift | 56 +++++++++---------- 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift index 9ad233b2..16360c15 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift @@ -1,14 +1,7 @@ -// -// Logger.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/9/24. -// - import Foundation -struct Logger { - enum Level { +public struct Logger { + public enum Level { case info case debug case network @@ -55,12 +48,10 @@ struct Logger { static var isShowLine: Bool = false static var isShowLog: Bool = true - static private let noInputText = "Input is not found" - - static func log( + public static func log( message: Any, category: Level, - fileName: String = noInputText, + fileName: String = "Input is not found", line: Int? = nil ) { if isShowLog { diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift index f9262d37..cbae967f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -1,37 +1,37 @@ import Foundation public struct CreateStoreParams { - let name: String - let categoryId: Int64 - let desc: String - let address: String - let startDate: String - let endDate: String - let mainImageUrl: String - let imageUrlList: [String?] - let latitude: Double - let longitude: Double - let markerTitle: String - let markerSnippet: String - let startDateBeforeEndDate: Bool + public let name: String + public let categoryId: Int64 + public let desc: String + public let address: String + public let startDate: String + public let endDate: String + public let mainImageUrl: String + public let imageUrlList: [String?] + public let latitude: Double + public let longitude: Double + public let markerTitle: String + public let markerSnippet: String + public let startDateBeforeEndDate: Bool } public struct UpdateStoreParams { - let id: Int64 - let name: String - let categoryId: Int64 - let desc: String - let address: String - let startDate: String - let endDate: String - let mainImageUrl: String - let imageUrlList: [String?] - let imagesToDelete: [Int64] - let latitude: Double - let longitude: Double - let markerTitle: String - let markerSnippet: String - let startDateBeforeEndDate: Bool + public let id: Int64 + public let name: String + public let categoryId: Int64 + public let desc: String + public let address: String + public let startDate: String + public let endDate: String + public let mainImageUrl: String + public let imageUrlList: [String?] + public let imagesToDelete: [Int64] + public let latitude: Double + public let longitude: Double + public let markerTitle: String + public let markerSnippet: String + public let startDateBeforeEndDate: Bool } public struct CreateNoticeParams { From f20aef88c0288da5072949e2834d513fe278716d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 01:57:47 +0900 Subject: [PATCH 157/393] =?UTF-8?q?fix/#112:=20PreSignedService=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9D=84=20DataLayer=EB=A5=BC=20=ED=86=B5?= =?UTF-8?q?=ED=95=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 - PreSignedService를 InfraStructure에서 DataLayer로 이동 - PreSignedService를 이용할 수 있도록 관련 UseCase와 Repository의 인터페이스와 구현체 구현 - PreSignedService를 사용하던 PresentationLayer의 일부 Reactor들을 UseCase를 이용하도록 수정 - imageService라는 명칭을 PreSignedUseCase로 수정 --- .../Data/Data.xcodeproj/project.pbxproj | 2 + .../Network}/Service/PreSignedService.swift | 10 +--- .../PreSignedRepositoryImpl.swift | 29 ++++++++++ .../UseCaseImpl/PreSignedUseCaseImpl.swift | 23 ++++++++ .../Repository/PreSignedRepository.swift | 9 +++ .../UseCase/PreSignedUseCase.swift | 9 +++ .../PopUpStoreRegisterReactor.swift | 21 +++---- .../PopUpStoreRegisterViewController.swift | 3 +- .../Scene/Admin/AdminViewController.swift | 10 +++- .../CommentList/CommentListReactor.swift | 23 ++++---- .../NormalCommentAddReactor.swift | 47 ++++++++------- .../NormalCommentEditReactor.swift | 58 +++++++++---------- .../Scene/Detail/DetailReactor.swift | 16 +++-- .../HomeCardSection/HomeCardSectionCell.swift | 3 +- .../HomePopularCardSectionCell.swift | 1 - .../ProfileEdit/Main/ProfileEditReactor.swift | 24 ++++---- 16 files changed, 179 insertions(+), 109 deletions(-) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => DataLayer/Data/Data/Network}/Service/PreSignedService.swift (99%) create mode 100644 Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift create mode 100644 Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Repository/PreSignedRepository.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/UseCase/PreSignedUseCase.swift diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index b05e0790..c28adfdb 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -102,6 +102,7 @@ Network/Interceptor/TokenInterceptor.swift, Network/Provider/Provider.swift, Network/Provider/ProviderImpl.swift, + Network/Service/PreSignedService.swift, RepositoryImpl/AdminRepositoryImpl.swift, RepositoryImpl/AuthAPIRepositoryImpl.swift, RepositoryImpl/CommentAPIRepositoryImpl.swift, @@ -109,6 +110,7 @@ RepositoryImpl/MapDirectionRepositoryImpl.swift, RepositoryImpl/MapRepositoryImpl.swift, RepositoryImpl/PopUpAPIRepositoryImpl.swift, + RepositoryImpl/PreSignedRepositoryImpl.swift, RepositoryImpl/SignUpRepositoryImpl.swift, RepositoryImpl/UserAPIRepositoryImpl.swift, ); diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/PreSignedService.swift b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift similarity index 99% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Service/PreSignedService.swift rename to Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift index ad1ef9a2..3609bc2b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/PreSignedService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift @@ -1,13 +1,7 @@ -// -// PreSignedService.swift -// PopPool -// -// Created by SeoJunYoung on 9/5/24. -// - -import Foundation import UIKit +import Infrastructure + import Alamofire import RxCocoa import RxSwift diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift new file mode 100644 index 00000000..4f6eec21 --- /dev/null +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift @@ -0,0 +1,29 @@ +import UIKit + +import DomainInterface + +import RxSwift + +public final class PreSignedRepositoryImpl: PreSignedRepository { + + private let service = PreSignedService() + + public func tryUpload(presignedURLRequest: [(filePath: String, image: UIImage)]) -> Single { + return service.tryUpload(datas: presignedURLRequest.map { + PreSignedService.PresignedURLRequest( + filePath: $0.filePath, + image: $0.image + ) + }) + } + + public func tryDelete(objectKeyList: [String]) -> Completable { + return service.tryDelete( + targetPaths: PresignedURLRequestDTO(objectKeyList: objectKeyList) + ) + } + + public func fullImageURL(from filePath: String) -> URL? { + return service.fullImageURL(from: filePath) + } +} diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift new file mode 100644 index 00000000..493f2ea8 --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift @@ -0,0 +1,23 @@ +import UIKit + +import DomainInterface + +import RxSwift + +public final class PreSignedUseCaseImpl: PreSignedUseCase { + private let repository: PreSignedRepository + + init(repository: PreSignedRepository) { + self.repository = repository + } + + public func tryUpload(presignedURLRequest: [(filePath: String, image: UIImage)]) -> Single { + return repository.tryUpload(presignedURLRequest: presignedURLRequest) + } + public func tryDelete(objectKeyList: [String]) -> Completable { + return repository.tryDelete(objectKeyList: objectKeyList) + } + public func fullImageURL(from filePath: String) -> URL? { + repository.fullImageURL(from: filePath) + } +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/PreSignedRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/PreSignedRepository.swift new file mode 100644 index 00000000..483becff --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/PreSignedRepository.swift @@ -0,0 +1,9 @@ +import UIKit + +import RxSwift + +public protocol PreSignedRepository { + func tryUpload(presignedURLRequest: [(filePath: String, image: UIImage)]) -> Single + func tryDelete(objectKeyList: [String]) -> Completable + func fullImageURL(from filePath: String) -> URL? +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PreSignedUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PreSignedUseCase.swift new file mode 100644 index 00000000..13b92620 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PreSignedUseCase.swift @@ -0,0 +1,9 @@ +import UIKit + +import RxSwift + +public protocol PreSignedUseCase { + func tryUpload(presignedURLRequest: [(filePath: String, image: UIImage)]) -> Single + func tryDelete(objectKeyList: [String]) -> Completable + func fullImageURL(from filePath: String) -> URL? +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index fd4b5088..b984a6f8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -1,7 +1,8 @@ import UIKit - import CoreLocation +import DomainInterface + import ReactorKit import RxCocoa import RxSwift @@ -10,7 +11,7 @@ final class PopUpStoreRegisterReactor: Reactor { // MARK: - Properties private let adminUseCase: AdminUseCase - private let presignedService: PreSignedService + private let preSignedUseCase: PreSignedUseCase private let isEditMode: Bool private let editingStoreId: Int64? @@ -18,11 +19,11 @@ final class PopUpStoreRegisterReactor: Reactor { init( adminUseCase: AdminUseCase, - presignedService: PreSignedService, + preSignedUseCase: PreSignedUseCase, editingStore: AdminStore? = nil ) { self.adminUseCase = adminUseCase - self.presignedService = presignedService + self.preSignedUseCase = preSignedUseCase self.isEditMode = editingStore != nil self.editingStoreId = editingStore?.id @@ -520,7 +521,7 @@ final class PopUpStoreRegisterReactor: Reactor { private func deleteImagesFromS3(_ imagePaths: [String]) { guard !imagePaths.isEmpty else { return } - presignedService.tryDelete(targetPaths: .init(objectKeyList: imagePaths)) + preSignedUseCase.tryDelete(objectKeyList: imagePaths) .subscribe( onCompleted: { Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) @@ -648,7 +649,7 @@ final class PopUpStoreRegisterReactor: Reactor { dispatchGroup.enter() - if let imageURL = self.presignedService.fullImageURL(from: imageData.imageUrl) { + if let imageURL = self.preSignedUseCase.fullImageURL(from: imageData.imageUrl) { URLSession.shared.dataTask(with: imageURL) { data, _, error in defer { dispatchGroup.leave() } @@ -722,8 +723,8 @@ final class PopUpStoreRegisterReactor: Reactor { isMain: image.isMain) } - return presignedService.tryUpload(datas: updatedImages.map { - PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) + return preSignedUseCase.tryUpload(presignedURLRequest: updatedImages.map { + return (filePath: $0.filePath, image: $0.image) }) .asObservable() // Single을 Observable로 변환 .map { _ in updatedImages.map { $0.filePath } } @@ -802,8 +803,8 @@ final class PopUpStoreRegisterReactor: Reactor { isMain: image.isMain) } - return presignedService.tryUpload(datas: updatedImages.map { - PreSignedService.PresignedURLRequest(filePath: $0.filePath, image: $0.image) + return preSignedUseCase.tryUpload(presignedURLRequest: updatedImages.map { + return (filePath: $0.filePath, image: $0.image) }) .asObservable() .map { _ in updatedImages.map { $0.filePath } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 3a33d18e..199d4c26 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -27,10 +27,9 @@ final class PopUpStoreRegisterViewController: BaseViewController { super.init() - let presignedService = PreSignedService() let reactor = PopUpStoreRegisterReactor( adminUseCase: DIContainer.resolve(AdminUseCase.self), - presignedService: presignedService, + preSignedUseCase: DIContainer.resolve(PreSignedUseCasae.self), editingStore: editingStore ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index 29c8b765..7e98d74a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -1,7 +1,11 @@ +import UIKit + +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift -import UIKit final class AdminViewController: BaseViewController, View { @@ -221,8 +225,8 @@ final class AdminViewController: BaseViewController, View { Logger.log(message: "삭제할 이미지: \(allImageUrls.count)개", category: .debug) - let imageService = PreSignedService() - imageService.tryDelete(targetPaths: .init(objectKeyList: allImageUrls)) + @Dependency var preSignedUseCase: PreSignedUseCase + preSignedUseCase.tryDelete(objectKeyList: allImageUrls) .andThen(self.adminUseCase.deleteStore(id: store.id)) .observe(on: MainScheduler.instance) .subscribe( diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index 7bdd33c5..d8141f79 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -1,12 +1,8 @@ -// -// CommentListReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift @@ -48,7 +44,7 @@ final class CommentListReactor: Reactor { private var page: Int32 = 0 private var appendDataIsEmpty: Bool = false - private var imageService = PreSignedService() + private let preSignedUseCase: PreSignedUseCase private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase: CommentAPIUseCase @@ -78,7 +74,8 @@ final class CommentListReactor: Reactor { popUpName: String?, userAPIUseCase: UserAPIUseCase, popUpAPIUseCase: PopUpAPIUseCase, - commentAPIUseCase: CommentAPIUseCase + commentAPIUseCase: CommentAPIUseCase, + preSignedUseCase: PreSignedUseCase ) { self.initialState = State() self.popUpID = popUpID @@ -86,6 +83,7 @@ final class CommentListReactor: Reactor { self.userAPIUseCase = userAPIUseCase self.popUpAPIUseCase = popUpAPIUseCase self.commentAPIUseCase = commentAPIUseCase + self.preSignedUseCase = preSignedUseCase } // MARK: - Reactor Methods @@ -287,7 +285,6 @@ final class CommentListReactor: Reactor { func showMyCommentMenu(controller: BaseViewController, comment: DetailCommentSection.CellType.Input) { let nextController = CommentMyMenuController() nextController.reactor = CommentMyMenuReactor(nickName: comment.nickName) - imageService = PreSignedService() controller.presentPanModal(nextController) nextController.reactor?.state @@ -304,10 +301,10 @@ final class CommentListReactor: Reactor { .disposed(by: self.disposeBag) let commentList = comment.imageList.compactMap { $0 } - self.imageService.tryDelete(targetPaths: .init(objectKeyList: commentList)) - .subscribe { + self.preSignedUseCase.tryDelete(objectKeyList: commentList) + .subscribe(onDisposed: { Logger.log(message: "S3 Image Delete 완료", category: .info) - } + }) .disposed(by: self.disposeBag) case .edit: owner.dismiss(animated: true) { [weak controller] in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index 8e00f247..e3acc723 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -1,13 +1,9 @@ -// -// NormalCommentAddReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import PhotosUI import UIKit +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift @@ -48,7 +44,7 @@ final class NormalCommentAddReactor: Reactor { private var popUpName: String private let commentAPIUseCase: CommentAPIUseCase - private let imageService = PreSignedService() + private let preSignedUseCase: PreSignedUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -77,12 +73,14 @@ final class NormalCommentAddReactor: Reactor { init( popUpID: Int64, popUpName: String, - commentAPIUseCase: CommentAPIUseCase + commentAPIUseCase: CommentAPIUseCase, + preSignedUseCase: PreSignedUseCase ) { self.initialState = State() self.popUpID = popUpID self.popUpName = popUpName self.commentAPIUseCase = commentAPIUseCase + self.preSignedUseCase = preSignedUseCase } // MARK: - Reactor Methods @@ -160,20 +158,25 @@ final class NormalCommentAddReactor: Reactor { let uuid = UUID().uuidString let pathList = images.map { "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg" } - imageService.tryUpload(datas: images.map { .init(filePath: "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg", image: $0.element)}) - .subscribe(onSuccess: { [weak self] _ in - guard let self = self else { return } - self.commentAPIUseCase.postCommentAdd(popUpStoreId: self.popUpID, content: newState.text, commentType: "NORMAL", imageUrlList: pathList) - .subscribe(onDisposed: { - controller.navigationController?.popViewController(animated: true) { - DispatchQueue.main.asyncAfter(deadline: .now()) { - ToastMaker.createToast(message: "코멘트 작성을 완료했어요") - } + preSignedUseCase.tryUpload(presignedURLRequest: images.map { + return ( + filePath: "PopUpComment/\(popUpName)/\(uuid)/\($0.offset).jpg", + image: $0.element + ) + }) + .subscribe(onSuccess: { [weak self] _ in + guard let self = self else { return } + self.commentAPIUseCase.postCommentAdd(popUpStoreId: self.popUpID, content: newState.text, commentType: "NORMAL", imageUrlList: pathList) + .subscribe(onDisposed: { + controller.navigationController?.popViewController(animated: true) { + DispatchQueue.main.asyncAfter(deadline: .now()) { + ToastMaker.createToast(message: "코멘트 작성을 완료했어요") } - }) - .disposed(by: disposeBag) - }) - .disposed(by: disposeBag) + } + }) + .disposed(by: disposeBag) + }) + .disposed(by: disposeBag) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index b33014d9..3d5182b7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -1,13 +1,9 @@ -// -// NormalCommentEditReactor.swift -// Poppool -// -// Created by SeoJunYoung on 2/1/25. -// - import PhotosUI import UIKit +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift @@ -49,7 +45,7 @@ final class NormalCommentEditReactor: Reactor { private var originComment: DetailCommentSection.CellType.Input private let commentAPIUseCase: CommentAPIUseCase - private let imageService = PreSignedService() + private let preSignedUseCase: PreSignedUseCase lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -80,13 +76,15 @@ final class NormalCommentEditReactor: Reactor { popUpID: Int64, popUpName: String, comment: DetailCommentSection.CellType.Input, - commentAPIUseCase: CommentAPIUseCase + commentAPIUseCase: CommentAPIUseCase, + preSignedUseCase: PreSignedUseCase ) { self.initialState = State(text: comment.comment) self.popUpID = popUpID self.popUpName = popUpName self.originComment = comment self.commentAPIUseCase = commentAPIUseCase + self.preSignedUseCase = preSignedUseCase let imageList = zip(comment.imageList, comment.imageIDList) imageSection.inputDataList.append(contentsOf: imageList.map({ url, id in .init(image: nil, isFirstCell: false, isEditCase: true, imageURL: url, imageID: id) @@ -174,26 +172,28 @@ final class NormalCommentEditReactor: Reactor { var convertDeleteImages: [PutCommentImageDataRequestDTO] = deleteImages.map { .init(imageId: $0.1, imageUrl: $0.0, actionType: "DELETE")} if !addImages.isEmpty { - imageService.tryUpload(datas: addImages.map { .init(filePath: pathList[$0.offset], image: $0.element)}) - .subscribe { [weak self] _ in + preSignedUseCase.tryUpload(presignedURLRequest: addImages.map { + return (filePath: pathList[$0.offset], image: $0.element) + }) + .subscribe { [weak self] _ in + guard let self = self else { return } + self.commentAPIUseCase.editComment( + popUpStoreId: self.popUpID, + commentId: self.originComment.commentID, + content: newState.text, + imageUrlList: (convertAddImages + convertKeepImages + convertDeleteImages).map { $0.imageUrl } + ) + .subscribe(onDisposed: { [weak self, weak controller] in guard let self = self else { return } - self.commentAPIUseCase.editComment( - popUpStoreId: self.popUpID, - commentId: self.originComment.commentID, - content: newState.text, - imageUrlList: (convertAddImages + convertKeepImages + convertDeleteImages).map { $0.imageUrl } - ) - .subscribe { [weak self, weak controller] in - guard let self = self else { return } - self.imageService.tryDelete(targetPaths: .init(objectKeyList: deleteImages.compactMap { $0.0 })) - .subscribe { - controller?.navigationController?.popViewController(animated: true) - } - .disposed(by: self.disposeBag) - } - .disposed(by: self.disposeBag) - } - .disposed(by: disposeBag) + self.preSignedUseCase.tryDelete(objectKeyList: deleteImages.compactMap { $0.0 }) + .subscribe(onDisposed: { + controller?.navigationController?.popViewController(animated: true) + }) + .disposed(by: self.disposeBag) + }) + .disposed(by: self.disposeBag) + } + .disposed(by: disposeBag) } else { commentAPIUseCase.editComment( popUpStoreId: self.popUpID, @@ -203,7 +203,7 @@ final class NormalCommentEditReactor: Reactor { ) .subscribe { [weak self, weak controller] in guard let self = self else { return } - self.imageService.tryDelete(targetPaths: .init(objectKeyList: deleteImages.compactMap { $0.0 })) + self.preSignedUseCase.tryDelete(objectKeyList: deleteImages.compactMap { $0.0 }) .subscribe { controller?.navigationController?.popViewController(animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 181667ed..4335cc05 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -1,5 +1,8 @@ import UIKit +import Infrastructure +import DomainInterface + import LinkPresentation import ReactorKit import RxCocoa @@ -57,7 +60,7 @@ final class DetailReactor: Reactor { private var isLogin: Bool = false private var isFirstRequest: Bool = true - private var imageService = PreSignedService() + private var preSignedUseCase: PreSignedUseCase private let popUpAPIUseCase: PopUpAPIUseCase private let userAPIUseCase: UserAPIUseCase private let commentAPIUseCase: CommentAPIUseCase @@ -98,12 +101,14 @@ final class DetailReactor: Reactor { popUpID: Int64, userAPIUseCase: UserAPIUseCase, popUpAPIUseCase: PopUpAPIUseCase, - commentAPIUseCase: CommentAPIUseCase + commentAPIUseCase: CommentAPIUseCase, + preSignedUseCase: PreSignedUseCase ) { self.popUpID = popUpID self.userAPIUseCase = userAPIUseCase self.popUpAPIUseCase = popUpAPIUseCase self.commentAPIUseCase = commentAPIUseCase + self.preSignedUseCase = preSignedUseCase self.initialState = State() } @@ -513,7 +518,6 @@ extension DetailReactor { func showMyCommentMenu(controller: BaseViewController, indexPath: IndexPath, comment: DetailCommentSection.CellType.Input) { let nextController = CommentMyMenuController() nextController.reactor = CommentMyMenuReactor(nickName: comment.nickName) - imageService = PreSignedService() controller.presentPanModal(nextController) nextController.reactor?.state @@ -530,10 +534,10 @@ extension DetailReactor { .disposed(by: self.disposeBag) let commentList = comment.imageList.compactMap { $0 } - self.imageService.tryDelete(targetPaths: .init(objectKeyList: commentList)) - .subscribe { + self.preSignedUseCase.tryDelete(objectKeyList: commentList) + .subscribe(onDisposed: { Logger.log(message: "S3 Image Delete 완료", category: .info) - } + }) .disposed(by: self.disposeBag) case .edit: diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 30bb15e1..8c26452a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -56,8 +56,7 @@ final class HomeCardSectionCell: UICollectionViewCell { label.textColor = .w100 return label }() - - private let imageService = PreSignedService() + // MARK: - init override init(frame: CGRect) { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index b4bd7862..33170f64 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -57,7 +57,6 @@ final class HomePopularCardSectionCell: UICollectionViewCell { let disposeBag = DisposeBag() - private let imageService = PreSignedService() // MARK: - init override init(frame: CGRect) { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 00b17647..0b57dd84 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -1,13 +1,9 @@ -// -// ProfileEditReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/4/25. -// - import PhotosUI import UIKit +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift @@ -66,15 +62,17 @@ final class ProfileEditReactor: Reactor { private let userAPIUseCase: UserAPIUseCase private let signUpAPIUseCase: SignUpAPIUseCase - private let imageService = PreSignedService() + private let preSignedUseCase: PreSignedUseCase // MARK: - init init( userAPIUseCase: UserAPIUseCase, - signUpAPIUseCase: SignUpAPIUseCase + signUpAPIUseCase: SignUpAPIUseCase, + preSignedUseCase: PreSignedUseCase ) { self.userAPIUseCase = userAPIUseCase self.signUpAPIUseCase = signUpAPIUseCase + self.preSignedUseCase = preSignedUseCase self.initialState = State() } @@ -191,14 +189,14 @@ final class ProfileEditReactor: Reactor { let newPath = "ProfileImage/\(UUID().uuidString).jpg" currentImagePath = newPath if originProfileData?.profileImageUrl == nil { - return imageService.tryUpload(datas: [.init(filePath: newPath, image: changeImage)]) + return preSignedUseCase.tryUpload(presignedURLRequest: [(filePath: newPath, image: changeImage)]) .asObservable() .map { .loadView } } else { let deletePath = originProfileData?.profileImageUrl ?? "" - return imageService.tryDelete(targetPaths: .init(objectKeyList: [deletePath])) + return preSignedUseCase.tryDelete(objectKeyList: [deletePath]) .andThen( - imageService.tryUpload(datas: [.init(filePath: newPath, image: changeImage)]) + preSignedUseCase.tryUpload(presignedURLRequest: [(filePath: newPath, image: changeImage)]) .asObservable() .map { .loadView } ) @@ -211,7 +209,7 @@ final class ProfileEditReactor: Reactor { } else { currentImagePath = nil let deletePath = originProfileData?.profileImageUrl ?? "" - return imageService.tryDelete(targetPaths: .init(objectKeyList: [deletePath])) + return preSignedUseCase.tryDelete(objectKeyList: [deletePath]) .andThen(Observable.just(.loadView)) } } else { From 01e2cff4ecd4c51904a48c5445b71200fca94354 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:05:36 +0900 Subject: [PATCH 158/393] =?UTF-8?q?remove/#112:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20Domain=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=84=ED=84=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UserResponse/GetRecentPopUpResponse.swift | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift index 5606d9d9..22b7e924 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift @@ -16,17 +16,3 @@ public struct GetRecentPopUpDataResponse { var address: String? var closeYn: Bool } - -public extension GetRecentPopUpDataResponse { - func toStoreItem() -> StoreItem { - return StoreItem( - id: self.popUpStoreId, - thumbnailURL: self.mainImageUrl ?? "", - category: "카테고리", - title: self.popUpStoreName ?? "제목 없음", - location: self.address ?? "주소 없음", - dateRange: "\(self.startDate ?? "") ~ \(self.endDate ?? "")", - isBookmarked: self.closeYn - ) - } -} From 907702c4e9cdf15f996cf7fd18f1ed72ca28ff20 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:06:06 +0900 Subject: [PATCH 159/393] =?UTF-8?q?refactor/#112:=20Secrets=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Infrastructure/Secrets.swift | 12 ++++++------ .../Data/Data/RepositoryImpl/MapRepositoryImpl.swift | 1 + .../AdminRegister/PopUpStoreRegisterReactor.swift | 1 + 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift index 7338561a..80a48514 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Secrets.swift @@ -1,23 +1,23 @@ import Foundation -enum Secrets { - static var kakaoAuthAppKey: String { +public enum Secrets { + public static var kakaoAuthAppKey: String { return getValue(forKey: "KAKAO_AUTH_APP_KEY") } - static var popPoolBaseURL: String { + public static var popPoolBaseURL: String { return getValue(forKey: "POPPOOL_BASE_URL") } - static var popPoolS3BaseURL: String { + public static var popPoolS3BaseURL: String { return getValue(forKey: "POPPOOL_S3_BASE_URL") } - static var popPoolAPIKey: String { + public static var popPoolAPIKey: String { return getValue(forKey: "POPPOOL_API_KEY") } - static var naverMapClientID: String { + public static var naverMapClientID: String { return getValue(forKey: "NAVER_MAP_CLIENT_ID") } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index fa6b742c..9ac2c055 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -1,6 +1,7 @@ import Foundation import DomainInterface +import Infrastructure import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index b984a6f8..53df9995 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -1,6 +1,7 @@ import UIKit import CoreLocation +import Infrastructure import DomainInterface import ReactorKit From a17463bae44395e1c3358598ddd30bf6b770acf5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:23:26 +0900 Subject: [PATCH 160/393] =?UTF-8?q?refactor/#112:=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 20 ++++++++++ .../Service/KeyChainService.swift | 11 ++--- .../Data/Data.xcodeproj/project.pbxproj | 2 + .../Data/Data/Network/Provider/Provider.swift | 9 +---- .../Data/Network/Provider/ProviderImpl.swift | 9 ++++- .../RepositoryImpl/AdminRepositoryImpl.swift | 4 +- .../AuthAPIRepositoryImpl.swift | 4 +- .../CommentAPIRepositoryImpl.swift | 4 +- .../HomeAPIRepositoryImpl.swift | 4 +- .../MapDirectionRepositoryImpl.swift | 4 +- .../RepositoryImpl/MapRepositoryImpl.swift | 4 +- .../PopUpAPIRepositoryImpl.swift | 4 +- .../PreSignedRepositoryImpl.swift | 2 + .../RepositoryImpl/SignUpRepositoryImpl.swift | 4 +- .../UserAPIRepositoryImpl.swift | 4 +- .../Domain/Domain.xcodeproj/project.pbxproj | 4 ++ .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 4 +- .../UseCaseImpl/AuthAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/HomeAPIUseCaseImpl.swift | 4 +- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 4 +- .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/PreSignedUseCaseImpl.swift | 2 +- .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/UserAPIUseCaseImpl.swift | 4 +- .../Entity/AdminResponse/AdminStore.swift | 8 ++-- .../AdminResponse/AdminStoreDetail.swift | 40 +++++++++---------- .../AdminResponse/Params/AdminParams.swift | 34 ++++++++++++++++ Poppool/Poppool/Application/AppDelegate.swift | 12 +++++- .../Presentation.xcodeproj/project.pbxproj | 2 + .../PopUpStoreRegisterViewController.swift | 3 ++ 31 files changed, 148 insertions(+), 78 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index 8f7ecdc1..145363c1 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,12 +7,29 @@ objects = { /* Begin PBXBuildFile section */ + 0512596B2DB5629C001342A2 /* RxCocoa-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 0512596D2DB5629E001342A2 /* RxSwift-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 05C1D8302DB53CE300508FFD /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */; }; 05C1D8322DB53CE300508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */; }; 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; }; 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; }; /* End PBXBuildFile section */ +/* Begin PBXCopyFilesBuildPhase section */ + 0512596C2DB5629C001342A2 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 0512596D2DB5629E001342A2 /* RxSwift-Dynamic in Embed Frameworks */, + 0512596B2DB5629C001342A2 /* RxCocoa-Dynamic in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 058CC9182DB5383C0084221A /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -77,6 +94,7 @@ 058CC9142DB5383C0084221A /* Sources */, 058CC9152DB5383C0084221A /* Frameworks */, 058CC9162DB5383C0084221A /* Resources */, + 0512596C2DB5629C001342A2 /* Embed Frameworks */, ); buildRules = ( ); @@ -293,6 +311,7 @@ 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", @@ -325,6 +344,7 @@ 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", diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index ebcfcb70..69b8225b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -1,16 +1,9 @@ -// -// KeyChainService.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/2/24. -// - import Foundation import Security import RxSwift -final class KeyChainService { +public final class KeyChainService { // KeyChain에서 발생할 수 있는 오류를 정의 enum KeyChainError: Error { @@ -22,6 +15,8 @@ final class KeyChainService { // KeyChain 서비스 이름 private let service = "keyChain" + public init() { } + /// KeyChain에서 특정 타입의 토큰을 가져오는 메서드 /// - Parameter type: 가져오려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Returns: 가져온 토큰을 담은 `Single` diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index c28adfdb..3a0327d9 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -395,6 +395,7 @@ 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", @@ -427,6 +428,7 @@ 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", diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift b/Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift index 1a2e4394..5567cb93 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/Provider.swift @@ -1,16 +1,9 @@ -// -// Provider.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/16/24. -// - import Foundation import Alamofire import RxSwift -protocol Provider { +public protocol Provider { /// 네트워크 요청을 수행하고 결과를 반환하는 메서드 /// - Parameters: diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index 0a14662f..7f2295bc 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -1,12 +1,17 @@ -import Alamofire import Foundation + +import Infrastructure + +import Alamofire import RxSwift -final class ProviderImpl: Provider { +public final class ProviderImpl: Provider { private let disposeBag = DisposeBag() var timeoutTimer: Timer? + public init(timeoutTimer: Timer? = nil) { self.timeoutTimer = timeoutTimer } + func requestData( with endpoint: E, interceptor: RequestInterceptor? = nil diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift index 73bf0cf3..1a4e68f0 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift @@ -5,14 +5,14 @@ import DomainInterface import Alamofire import RxSwift -final class AdminRepositoryImpl: AdminRepository { +public final class AdminRepositoryImpl: AdminRepository { // MARK: - Properties private let provider: Provider private let tokenInterceptor = TokenInterceptor() // MARK: - Init - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift index bd991c59..43e16ab5 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class AuthAPIRepositoryImpl: AuthAPIRepository { +public final class AuthAPIRepositoryImpl: AuthAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift index 210c31c0..8c9eb15a 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class CommentAPIRepositoryImpl: CommentAPIRepository { +public final class CommentAPIRepositoryImpl: CommentAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift index fe8e4b44..a76953cc 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class HomeAPIRepositoryImpl: HomeAPIRepository { +public final class HomeAPIRepositoryImpl: HomeAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index b9f8c297..bad1882a 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class MapDirectionRepositoryImpl: MapDirectionRepository { +public final class MapDirectionRepositoryImpl: MapDirectionRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 9ac2c055..6d80fff7 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -5,11 +5,11 @@ import Infrastructure import RxSwift -final class MapRepositoryImpl: MapRepository { +public final class MapRepositoryImpl: MapRepository { private let provider: Provider - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift index 37600666..cb22fc00 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class PopUpAPIRepositoryImpl: PopUpAPIRepository { +public final class PopUpAPIRepositoryImpl: PopUpAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift index 4f6eec21..8bcf66d5 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift @@ -8,6 +8,8 @@ public final class PreSignedRepositoryImpl: PreSignedRepository { private let service = PreSignedService() + public init() { } + public func tryUpload(presignedURLRequest: [(filePath: String, image: UIImage)]) -> Single { return service.tryUpload(datas: presignedURLRequest.map { PreSignedService.PresignedURLRequest( diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift index a3b4f7fe..10225783 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class SignUpRepositoryImpl: SignUpRepository { +public final class SignUpRepositoryImpl: SignUpRepository { private let provider: Provider - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift index 31ca397c..55ff8a35 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -4,12 +4,12 @@ import DomainInterface import RxSwift -final class UserAPIRepositoryImpl: UserAPIRepository { +public final class UserAPIRepositoryImpl: UserAPIRepository { private let provider: Provider private let tokenInterceptor = TokenInterceptor() - init(provider: Provider) { + public init(provider: Provider) { self.provider = provider } diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index 8b19b2ae..88ed89bd 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -417,6 +417,7 @@ 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", @@ -449,6 +450,7 @@ 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", @@ -481,6 +483,7 @@ 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", @@ -513,6 +516,7 @@ 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", diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift index 52c25f65..e3976ba6 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class AdminUseCaseImpl: AdminUseCase { +public final class AdminUseCaseImpl: AdminUseCase { private let repository: AdminRepository - init(repository: AdminRepository) { + public init(repository: AdminRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift index 81aabcdc..ae809033 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class AuthAPIUseCaseImpl: AuthAPIUseCase { +public final class AuthAPIUseCaseImpl: AuthAPIUseCase { private let repository: AuthAPIRepository - init(repository: AuthAPIRepository) { + public init(repository: AuthAPIRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index 695e669f..43b4dec4 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class CommentAPIUseCaseImpl: CommentAPIUseCase { +public final class CommentAPIUseCaseImpl: CommentAPIUseCase { private let repository: CommentAPIRepository - init(repository: CommentAPIRepository) { + public init(repository: CommentAPIRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index 17ee1e6f..b8a8eaf8 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class HomeAPIUseCaseImpl: HomeAPIUseCase { +public final class HomeAPIUseCaseImpl: HomeAPIUseCase { private let repository: HomeAPIRepository - init(repository: HomeAPIRepository) { + public init(repository: HomeAPIRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index a6b7d558..60157303 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class MapUseCaseImpl: MapUseCase { +public final class MapUseCaseImpl: MapUseCase { private let repository: MapRepository - init(repository: MapRepository) { + public init(repository: MapRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index b437cb38..cbb38408 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -4,11 +4,11 @@ import DomainInterface import RxSwift -final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { +public final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { private let repository: PopUpAPIRepository - init(repository: PopUpAPIRepository) { + public init(repository: PopUpAPIRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift index 493f2ea8..c84a23bf 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PreSignedUseCaseImpl.swift @@ -7,7 +7,7 @@ import RxSwift public final class PreSignedUseCaseImpl: PreSignedUseCase { private let repository: PreSignedRepository - init(repository: PreSignedRepository) { + public init(repository: PreSignedRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index baf641ca..74714236 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -4,10 +4,10 @@ import DomainInterface import RxSwift -final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { +public final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { private let repository: SignUpRepository - init(repository: SignUpRepository) { + public init(repository: SignUpRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index f33df46e..a3ea8525 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -4,10 +4,10 @@ import DomainInterface import RxSwift -final class UserAPIUseCaseImpl: UserAPIUseCase { +public final class UserAPIUseCaseImpl: UserAPIUseCase { private let repository: UserAPIRepository - init(repository: UserAPIRepository) { + public init(repository: UserAPIRepository) { self.repository = repository } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift index 54f2a211..636685a6 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -1,8 +1,8 @@ import Foundation public struct AdminStore { - let id: Int64 - let name: String - let categoryName: String - let mainImageUrl: String + public let id: Int64 + public let name: String + public let categoryName: String + public let mainImageUrl: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index 62c26415..3cb81611 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -1,26 +1,26 @@ import Foundation public struct AdminStoreDetail { - let id: Int64 - let name: String - let categoryId: Int64 - let categoryName: String - let description: String - let address: String - let startDate: String - let endDate: String - let createUserId: String - let createDateTime: String - let mainImageUrl: String - let bannerYn: Bool - let images: [StoreImage] - let latitude: Double - let longitude: Double - let markerTitle: String - let markerSnippet: String + public let id: Int64 + public let name: String + public let categoryId: Int64 + public let categoryName: String + public let description: String + public let address: String + public let startDate: String + public let endDate: String + public let createUserId: String + public let createDateTime: String + public let mainImageUrl: String + public let bannerYn: Bool + public let images: [StoreImage] + public let latitude: Double + public let longitude: Double + public let markerTitle: String + public let markerSnippet: String - struct StoreImage { - let id: Int64 - let imageUrl: String + public struct StoreImage { + public let id: Int64 + public let imageUrl: String } } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift index cbae967f..c92c1fa6 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -1,6 +1,22 @@ import Foundation public struct CreateStoreParams { + public init(name: String, categoryId: Int64, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { + self.name = name + self.categoryId = categoryId + self.desc = desc + self.address = address + self.startDate = startDate + self.endDate = endDate + self.mainImageUrl = mainImageUrl + self.imageUrlList = imageUrlList + self.latitude = latitude + self.longitude = longitude + self.markerTitle = markerTitle + self.markerSnippet = markerSnippet + self.startDateBeforeEndDate = startDateBeforeEndDate + } + public let name: String public let categoryId: Int64 public let desc: String @@ -17,6 +33,24 @@ public struct CreateStoreParams { } public struct UpdateStoreParams { + public init(id: Int64, name: String, categoryId: Int64, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], imagesToDelete: [Int64], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { + self.id = id + self.name = name + self.categoryId = categoryId + self.desc = desc + self.address = address + self.startDate = startDate + self.endDate = endDate + self.mainImageUrl = mainImageUrl + self.imageUrlList = imageUrlList + self.imagesToDelete = imagesToDelete + self.latitude = latitude + self.longitude = longitude + self.markerTitle = markerTitle + self.markerSnippet = markerSnippet + self.startDateBeforeEndDate = startDateBeforeEndDate + } + public let id: Int64 public let name: String public let categoryId: Int64 diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 471f00c9..048389c4 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,7 +1,14 @@ +import UIKit import CoreLocation + +import Data +import Domain +import DomainInterface +import Presentation +import Infrastructure + import KakaoSDKCommon import NMapsMap -import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -45,6 +52,7 @@ extension AppDelegate { DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } DIContainer.register(MapDirectionRepository.self) { return MapDirectionRepositoryImpl(provider: provider) } + DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -55,6 +63,7 @@ extension AppDelegate { @Dependency var homeAPIRepository: HomeAPIRepository @Dependency var authAPIRepository: AuthAPIRepository @Dependency var signUpRepository: SignUpRepository + @Dependency var preSignedRepository: PreSignedRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -65,5 +74,6 @@ extension AppDelegate { DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } DIContainer.register(SignUpAPIUseCase.self) { return SignUpAPIUseCaseImpl(repository: signUpRepository) } + DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 48d6a932..faf1c80f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -311,6 +311,7 @@ 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", @@ -343,6 +344,7 @@ 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", diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 199d4c26..387022a5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -2,6 +2,9 @@ import CoreLocation import PhotosUI import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift From 356d3decdc0c81ca3e9b0df31b5e2bf35b06f206 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:25:33 +0900 Subject: [PATCH 161/393] =?UTF-8?q?refactor/#112:=20Provider=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=A0=91=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90=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 --- .../Infrastructure/Service/KeyChainService.swift | 4 ++-- .../Data/Data/Network/Common/Requestable.swift | 11 +++-------- .../Data/Data/Network/Common/Responsable.swift | 9 +-------- .../Data/Data/Network/EndPoint/Endpoint.swift | 9 +-------- .../Data/Network/EndPoint/MultipartEndPoint.swift | 13 ++++--------- .../Data/Data/Network/Provider/ProviderImpl.swift | 6 +++--- 6 files changed, 14 insertions(+), 38 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index 69b8225b..b9545cf0 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -62,7 +62,7 @@ public final class KeyChainService { /// - Parameter type: 저장하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Parameter value: 저장할 토큰의 값 /// - Returns: 완료 시 `Completable` - func saveToken(type: TokenType, value: String) -> Result { + public func saveToken(type: TokenType, value: String) -> Result { // allowLossyConversion은 인코딩 과정에서 손실이 되는 것을 허용할 것인지 설정 guard let convertValue = value.data(using: .utf8, allowLossyConversion: false) else { return .failure(KeyChainError.dataConversionError(message: "Failed to convert value to Data.")) @@ -123,7 +123,7 @@ public final class KeyChainService { } } -enum TokenType: String { +public enum TokenType: String { case accessToken // 액세스 토큰 case refreshToken // 리프레시 토큰 } diff --git a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift index 9933fba3..2709e94c 100644 --- a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift +++ b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift @@ -1,15 +1,10 @@ -// -// Requestable.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/16/24. -// - import Foundation +import Infrastructure + import Alamofire -protocol Requestable { +public protocol Requestable { var baseURL: String { get } var path: String { get } var method: HTTPMethod { get } diff --git a/Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift index c4743d30..9f7c310c 100644 --- a/Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift +++ b/Poppool/DataLayer/Data/Data/Network/Common/Responsable.swift @@ -1,12 +1,5 @@ -// -// Responsable.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/16/24. -// - import Foundation -protocol Responsable { +public protocol Responsable { associatedtype Response } diff --git a/Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift index fcc83980..4e835db9 100644 --- a/Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/EndPoint/Endpoint.swift @@ -1,15 +1,8 @@ -// -// Endpoint.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/16/24. -// - import Foundation import Alamofire -protocol RequesteResponsable: Requestable, Responsable where Response: Decodable {} +public protocol RequesteResponsable: Requestable, Responsable where Response: Decodable {} class Endpoint: RequesteResponsable { typealias Response = R diff --git a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift index 75b4ef2e..00a5bf3c 100644 --- a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift @@ -1,15 +1,10 @@ -// -// MultipartEndPoint.swift -// MomsVillage -// -// Created by SeoJunYoung on 10/25/24. -// - import UIKit +import Infrastructure + import Alamofire -class MultipartEndPoint: URLRequestConvertible { +public class MultipartEndPoint: URLRequestConvertible { var baseURL: String var path: String var method: HTTPMethod @@ -36,7 +31,7 @@ class MultipartEndPoint: URLRequestConvertible { self.headers = headers } - func asURLRequest() throws -> URLRequest { + public func asURLRequest() throws -> URLRequest { let url = try baseURL.asURL().appendingPathComponent(path) var request = URLRequest(url: url) Logger.log(message: "\(request) URL 생성", category: .network) diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index 7f2295bc..e3d925ed 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -12,7 +12,7 @@ public final class ProviderImpl: Provider { public init(timeoutTimer: Timer? = nil) { self.timeoutTimer = timeoutTimer } - func requestData( + public func requestData( with endpoint: E, interceptor: RequestInterceptor? = nil ) -> Observable where R == E.Response { @@ -87,7 +87,7 @@ public final class ProviderImpl: Provider { } } - func request( + public func request( with request: E, interceptor: RequestInterceptor? = nil ) -> Completable { @@ -146,7 +146,7 @@ public final class ProviderImpl: Provider { } // multipart 업로드는 기존 코드와 동일 - func uploadImages( + public func uploadImages( with request: MultipartEndPoint, interceptor: RequestInterceptor? = nil ) -> Completable { From 23b19117677cfc7010efc6fad4c201a7b5678654 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:40:40 +0900 Subject: [PATCH 162/393] =?UTF-8?q?refactor/#112:=20SceneDelegate=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=AC=B8=EC=A0=9C=EB=90=98=EB=8D=98=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Extension/UIColor+.swift | 2 +- .../AuthResponse/PostTokenReissueResponse.swift | 8 ++++---- Poppool/Poppool/Application/SceneDelegate.swift | 2 ++ .../Scene/Splash/SplashController.swift | 11 +++++++++-- .../Utills/Controllers/BaseViewController.swift | 15 +++++---------- 5 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift index 1ea48e23..72662faf 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift @@ -7,7 +7,7 @@ import UIKit -extension UIColor { +public extension UIColor { // 무채색 컬러 static let g50 = UIColor(hexCode: "F2F5F7") diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift index d661cfba..3639847f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift @@ -1,8 +1,8 @@ import Foundation public struct PostTokenReissueResponse { - var accessToken: String? - var refreshToken: String? - var accessTokenExpiresAt: String? - var refreshTokenExpiresAt: String? + public var accessToken: String? + public var refreshToken: String? + public var accessTokenExpiresAt: String? + public var refreshTokenExpiresAt: String? } diff --git a/Poppool/Poppool/Application/SceneDelegate.swift b/Poppool/Poppool/Application/SceneDelegate.swift index 5a7be40e..df8f5bb8 100644 --- a/Poppool/Poppool/Application/SceneDelegate.swift +++ b/Poppool/Poppool/Application/SceneDelegate.swift @@ -1,5 +1,7 @@ import UIKit +import Presentation + import KakaoSDKAuth import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 80cc2877..4aece5de 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -1,12 +1,19 @@ import UIKit +import Infrastructure +import DomainInterface + import ReactorKit import RxCocoa import RxSwift import SnapKit -final class SplashController: BaseViewController { +public final class SplashController: BaseViewController { + public override init() { } + + required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + // MARK: - Properties var disposeBag = DisposeBag() @@ -20,7 +27,7 @@ final class SplashController: BaseViewController { // MARK: - Life Cycle extension SplashController { - override func viewDidLoad() { + public override func viewDidLoad() { super.viewDidLoad() setUp() setRootview() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift index 2184b672..19da325b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift @@ -1,16 +1,11 @@ -// -// BaseViewController.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/9/24. -// - import UIKit +import Infrastructure + import RxCocoa import RxSwift -class BaseViewController: UIViewController { +public class BaseViewController: UIViewController { var systemStatusBarIsDark: BehaviorRelay = .init(value: true) var systemStatusBarDisposeBag = DisposeBag() @@ -29,14 +24,14 @@ class BaseViewController: UIViewController { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + public override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .white self.navigationController?.navigationBar.isHidden = true systemStatusBarIsDarkBind() } - override func viewWillAppear(_ animated: Bool) { + public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) systemStatusBarIsDark.accept(systemStatusBarIsDark.value) } From 57d0a27f3c58c5788dbc410402a63c09f14331d3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:41:06 +0900 Subject: [PATCH 163/393] =?UTF-8?q?fix/#112:=20PutCommentImageDataRequestD?= =?UTF-8?q?TO=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CommentAPIRepositoryImpl.swift | 15 ++++++--- .../UseCaseImpl/CommentAPIUseCaseImpl.swift | 9 +++--- .../Repository/CommentAPIRepository.swift | 32 +++++++++---------- 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift index 8c9eb15a..142b61c0 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/CommentAPIRepositoryImpl.swift @@ -13,20 +13,27 @@ public final class CommentAPIRepositoryImpl: CommentAPIRepository { self.provider = provider } - func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { + public func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { let requestDTO = PostCommentRequestDTO(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList) let endPoint = CommentAPIEndPoint.postCommentAdd(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { + public func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { let requestDTO = DeleteCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId) let endPoint = CommentAPIEndPoint.deleteComment(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [PutCommentImageDataRequestDTO]?) -> Completable { - let requestDTO = PutCommentRequestDTO(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList) + public func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [String?]?) -> Completable { + let dtoList: [PutCommentImageDataRequestDTO]? = imageUrlList?.compactMap { $0 }.map { PutCommentImageDataRequestDTO(imageUrl: $0) } + + let requestDTO = PutCommentRequestDTO( + popUpStoreId: popUpStoreId, + commentId: commentId, + content: content, + imageUrlList: dtoList + ) let endPoint = CommentAPIEndPoint.editComment(request: requestDTO) return provider.request(with: endPoint, interceptor: tokenInterceptor) } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift index 43b4dec4..479fbdd0 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/CommentAPIUseCaseImpl.swift @@ -12,16 +12,15 @@ public final class CommentAPIUseCaseImpl: CommentAPIUseCase { self.repository = repository } - func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { + public func postCommentAdd(popUpStoreId: Int64, content: String?, commentType: String?, imageUrlList: [String?]) -> Completable { return repository.postCommentAdd(popUpStoreId: popUpStoreId, content: content, commentType: commentType, imageUrlList: imageUrlList) } - func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { + public func deleteComment(popUpStoreId: Int64, commentId: Int64) -> Completable { return repository.deleteComment(popUpStoreId: popUpStoreId, commentId: commentId) } - func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [String?]?) -> Completable { - let dtoList: [PutCommentImageDataRequestDTO]? = imageUrlList?.compactMap { $0 }.map { PutCommentImageDataRequestDTO(imageUrl: $0) } - return repository.editComment(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: dtoList) + public func editComment(popUpStoreId: Int64, commentId: Int64, content: String?, imageUrlList: [String?]?) -> Completable { + return repository.editComment(popUpStoreId: popUpStoreId, commentId: commentId, content: content, imageUrlList: imageUrlList) } } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift index 3562db2c..64fc0fbd 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CommentAPIRepository.swift @@ -3,22 +3,22 @@ import Foundation import RxSwift public protocol CommentAPIRepository { - func postCommentAdd( - popUpStoreId: Int64, - content: String?, - commentType: String?, - imageUrlList: [String?] - ) -> Completable + func postCommentAdd( + popUpStoreId: Int64, + content: String?, + commentType: String?, + imageUrlList: [String?] + ) -> Completable - func deleteComment( - popUpStoreId: Int64, - commentId: Int64 - ) -> Completable + func deleteComment( + popUpStoreId: Int64, + commentId: Int64 + ) -> Completable - func editComment( - popUpStoreId: Int64, - commentId: Int64, - content: String?, - imageUrlList: [PutCommentImageDataRequestDTO]? - ) -> Completable + func editComment( + popUpStoreId: Int64, + commentId: Int64, + content: String?, + imageUrlList: [String?]? + ) -> Completable } From c36a9654a1e47f32c2ad0bd3d42adce30daa15bc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:44:54 +0900 Subject: [PATCH 164/393] =?UTF-8?q?fix/#112:=20GetPopUpDirectionResponseDT?= =?UTF-8?q?O=20=EC=9E=98=EB=AA=BB=EB=90=9C=20=EC=82=AC=EC=9A=A9=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 --- .../GetPopUpDirectionResponseDTO.swift | 23 ++------------- .../MapDirectionRepositoryImpl.swift | 2 +- .../GetPopUpDirectionResponse.swift | 29 +++++++++++++++++++ .../Repository/MapDirectionRepository.swift | 2 +- 4 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift diff --git a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift index cd67dfee..15c4c206 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/GetPopUpDirectionResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetPopUpDirectionResponseDTO.swift -// Poppool -// -// Created by 김기현 on 1/23/25. -// - import Foundation +import DomainInterface + struct GetPopUpDirectionResponseDTO: Decodable { let id: Int64 let categoryName: String @@ -36,17 +31,3 @@ struct GetPopUpDirectionResponseDTO: Decodable { ) } } - -struct GetPopUpDirectionResponse { - let id: Int64 - let categoryName: String - let name: String - let address: String - let startDate: String - let endDate: String - let latitude: Double - let longitude: Double - let markerId: Int64 - let markerTitle: String - let markerSnippet: String -} diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index bad1882a..b8bdcedc 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -13,7 +13,7 @@ public final class MapDirectionRepositoryImpl: MapDirectionRepository { self.provider = provider } - func getPopUpDirection(popUpStoreId: Int64) -> Observable { + public func getPopUpDirection(popUpStoreId: Int64) -> Observable { let endpoint = FindDirectionEndPoint.fetchDirection(popUpStoreId: popUpStoreId) // print("🌎 [Repository]: 요청 생성 - \(endpoint)") return provider.requestData(with: endpoint, interceptor: TokenInterceptor()) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift new file mode 100644 index 00000000..6686d6d3 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift @@ -0,0 +1,29 @@ +import Foundation + +public struct GetPopUpDirectionResponse { + public init(id: Int64, categoryName: String, name: String, address: String, startDate: String, endDate: String, latitude: Double, longitude: Double, markerId: Int64, markerTitle: String, markerSnippet: String) { + self.id = id + self.categoryName = categoryName + self.name = name + self.address = address + self.startDate = startDate + self.endDate = endDate + self.latitude = latitude + self.longitude = longitude + self.markerId = markerId + self.markerTitle = markerTitle + self.markerSnippet = markerSnippet + } + + let id: Int64 + let categoryName: String + let name: String + let address: String + let startDate: String + let endDate: String + let latitude: Double + let longitude: Double + let markerId: Int64 + let markerTitle: String + let markerSnippet: String +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift index b933470a..ba17d266 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapDirectionRepository.swift @@ -3,5 +3,5 @@ import Foundation import RxSwift public protocol MapDirectionRepository { - func getPopUpDirection(popUpStoreId: Int64) -> Observable + func getPopUpDirection(popUpStoreId: Int64) -> Observable } From 49f53b7f51773fe0250c234c3d3a5324e4cc2a55 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:49:36 +0900 Subject: [PATCH 165/393] =?UTF-8?q?fix/#112:=20FindDirectionRepositoryImpl?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=EA=B0=92=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/RepositoryImpl/MapDirectionRepositoryImpl.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift index b8bdcedc..cccd8f73 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapDirectionRepositoryImpl.swift @@ -15,13 +15,6 @@ public final class MapDirectionRepositoryImpl: MapDirectionRepository { public func getPopUpDirection(popUpStoreId: Int64) -> Observable { let endpoint = FindDirectionEndPoint.fetchDirection(popUpStoreId: popUpStoreId) -// print("🌎 [Repository]: 요청 생성 - \(endpoint)") - return provider.requestData(with: endpoint, interceptor: TokenInterceptor()) - .do(onNext: { _ in -// print("✅ [Repository]: 응답 수신 - \(response)") - }, onError: { error in - print("❌ [Repository]: 요청 실패 - \(error)") - }) + return provider.requestData(with: endpoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - } From 07cc2608746e68db6e25a3d14a54527831305f2a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 02:57:24 +0900 Subject: [PATCH 166/393] =?UTF-8?q?refactor/#112:=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 44 +++++++++++++------ .../HomeAPIRepositoryImpl.swift | 8 ++-- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 17 +++---- .../UseCaseImpl/AuthAPIUseCaseImpl.swift | 4 +- .../UseCaseImpl/HomeAPIUseCaseImpl.swift | 8 ++-- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 9 ++-- .../UseCaseImpl/PopUpAPIUseCaseImpl.swift | 8 ++-- .../UseCaseImpl/SignUpAPIUseCaseImpl.swift | 6 +-- .../UseCaseImpl/UserAPIUseCaseImpl.swift | 42 +++++++++--------- .../Entity/MapResponse/MapPopUpStore.swift | 24 +++++----- .../GetWithdrawlListResponse.swift | 4 +- 11 files changed, 96 insertions(+), 78 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index 145363c1..789f6440 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,10 +7,10 @@ objects = { /* Begin PBXBuildFile section */ - 0512596B2DB5629C001342A2 /* RxCocoa-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 0512596D2DB5629E001342A2 /* RxSwift-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 05C1D8302DB53CE300508FFD /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */; }; - 05C1D8322DB53CE300508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */; }; + 05125B662DB56C21001342A2 /* KakaoSDKAuth in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 05125B672DB56C21001342A2 /* KakaoSDKUser in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; + 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */; }; + 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */; }; 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; }; 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; }; /* End PBXBuildFile section */ @@ -22,8 +22,8 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 0512596D2DB5629E001342A2 /* RxSwift-Dynamic in Embed Frameworks */, - 0512596B2DB5629C001342A2 /* RxCocoa-Dynamic in Embed Frameworks */, + 05125B672DB56C21001342A2 /* KakaoSDKUser in Embed Frameworks */, + 05125B662DB56C21001342A2 /* KakaoSDKAuth in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -47,9 +47,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */, 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */, - 05C1D8322DB53CE300508FFD /* RxSwift-Dynamic in Frameworks */, - 05C1D8302DB53CE300508FFD /* RxCocoa-Dynamic in Frameworks */, + 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */, 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -57,10 +57,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 05125B6A2DB56C32001342A2 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 058CC90E2DB5383C0084221A = { isa = PBXGroup; children = ( 058CC91A2DB5383C0084221A /* Infrastructure */, + 05125B6A2DB56C32001342A2 /* Frameworks */, 058CC9192DB5383C0084221A /* Products */, ); sourceTree = ""; @@ -105,10 +113,10 @@ ); name = Infrastructure; packageProductDependencies = ( - 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */, - 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */, 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */, 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */, + 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */, + 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */, ); productName = Infrastructure; productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; @@ -323,11 +331,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Infrastructure; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -356,11 +368,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Infrastructure; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -407,12 +423,12 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05C1D82F2DB53CE300508FFD /* RxCocoa-Dynamic */ = { + 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = "RxCocoa-Dynamic"; }; - 05C1D8312DB53CE300508FFD /* RxSwift-Dynamic */ = { + 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = "RxSwift-Dynamic"; diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift index a76953cc..c9020451 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/HomeAPIRepositoryImpl.swift @@ -13,25 +13,25 @@ public final class HomeAPIRepositoryImpl: HomeAPIRepository { self.provider = provider } - func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func fetchHome(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchHome(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func fetchCustomPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchCustomPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func fetchNewPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchNewPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) } - func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func fetchPopularPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = HomeSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = HomeAPIEndpoint.fetchPopularPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map({ $0.toDomain() }) diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift index e3976ba6..de49b176 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -1,6 +1,7 @@ import Foundation import DomainInterface +import Infrastructure import RxSwift @@ -12,15 +13,15 @@ public final class AdminUseCaseImpl: AdminUseCase { self.repository = repository } - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { + public func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { return repository.fetchStoreList(query: query, page: page, size: size) } - func fetchStoreDetail(id: Int64) -> Observable { + public func fetchStoreDetail(id: Int64) -> Observable { return repository.fetchStoreDetail(id: id) } - func createStore(params: CreateStoreParams) -> Completable { + public func createStore(params: CreateStoreParams) -> Completable { Logger.log(message: "createStore 호출 - 스토어명: \(params.name)", category: .debug) return repository.createStore(params: params) .do(onError: { error in @@ -30,7 +31,7 @@ public final class AdminUseCaseImpl: AdminUseCase { }) } - func updateStore(params: UpdateStoreParams) -> Completable { + public func updateStore(params: UpdateStoreParams) -> Completable { Logger.log(message: """ Updating store with location: Latitude: \(params.latitude) @@ -44,20 +45,20 @@ public final class AdminUseCaseImpl: AdminUseCase { }) } - func deleteStore(id: Int64) -> Completable { + public func deleteStore(id: Int64) -> Completable { return repository.deleteStore(id: id) } // Notice - func createNotice(params: CreateNoticeParams) -> Completable { + public func createNotice(params: CreateNoticeParams) -> Completable { return repository.createNotice(params: params) } - func updateNotice(params: UpdateNoticeParams) -> Completable { + public func updateNotice(params: UpdateNoticeParams) -> Completable { return repository.updateNotice(params: params) } - func deleteNotice(id: Int64) -> Completable { + public func deleteNotice(id: Int64) -> Completable { return repository.deleteNotice(id: id) } } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift index ae809033..6ec0781d 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AuthAPIUseCaseImpl.swift @@ -12,11 +12,11 @@ public final class AuthAPIUseCaseImpl: AuthAPIUseCase { self.repository = repository } - func postTryLogin(userCredential: Encodable, socialType: String) -> Observable { + public func postTryLogin(userCredential: Encodable, socialType: String) -> Observable { return repository.tryLogIn(userCredential: userCredential, socialType: socialType) } - func postTokenReissue() -> Observable { + public func postTokenReissue() -> Observable { return repository.postTokenReissue() } } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift index b8a8eaf8..be56469f 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/HomeAPIUseCaseImpl.swift @@ -12,7 +12,7 @@ public final class HomeAPIUseCaseImpl: HomeAPIUseCase { self.repository = repository } - func fetchHome( + public func fetchHome( page: Int32?, size: Int32?, sort: String? @@ -20,7 +20,7 @@ public final class HomeAPIUseCaseImpl: HomeAPIUseCase { return repository.fetchHome(page: page, size: size, sort: sort) } - func fetchCustomPopUp( + public func fetchCustomPopUp( page: Int32?, size: Int32?, sort: String? @@ -28,7 +28,7 @@ public final class HomeAPIUseCaseImpl: HomeAPIUseCase { return repository.fetchCustomPopUp(page: page, size: size, sort: sort) } - func fetchNewPopUp( + public func fetchNewPopUp( page: Int32?, size: Int32?, sort: String? @@ -36,7 +36,7 @@ public final class HomeAPIUseCaseImpl: HomeAPIUseCase { return repository.fetchNewPopUp(page: page, size: size, sort: sort) } - func fetchPopularPopUp( + public func fetchPopularPopUp( page: Int32?, size: Int32?, sort: String? diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index 60157303..5253690e 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -1,6 +1,7 @@ import Foundation import DomainInterface +import Infrastructure import RxSwift @@ -12,11 +13,11 @@ public final class MapUseCaseImpl: MapUseCase { self.repository = repository } - func fetchCategories() -> Observable<[CategoryResponse]> { + public func fetchCategories() -> Observable<[CategoryResponse]> { return repository.fetchCategories() } - func fetchStoresInBounds( + public func fetchStoresInBounds( northEastLat: Double, northEastLon: Double, southWestLat: Double, @@ -37,7 +38,7 @@ public final class MapUseCaseImpl: MapUseCase { }) } - func searchStores( + public func searchStores( query: String, categories: [Int64] ) -> Observable<[MapPopUpStore]> { @@ -52,7 +53,7 @@ public final class MapUseCaseImpl: MapUseCase { }) } - func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] { + public func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] { guard !selectedRegions.isEmpty else { return stores } return stores.filter { store in diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index cbb38408..716f1304 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -12,7 +12,7 @@ public final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { self.repository = repository } - func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable { + public func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable { var categoryString: String? if !categories.isEmpty { categoryString = categories.map { String($0) + "," }.reduce("", +) @@ -24,15 +24,15 @@ public final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { } } - func getSearchPopUpList(query: String?) -> Observable { + public func getSearchPopUpList(query: String?) -> Observable { return repository.getSearchPopUpList(categories: nil, page: nil, size: nil, sort: nil, query: query, sortCode: nil) } - func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool? = true) -> Observable { + public func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool? = true) -> Observable { return repository.getPopUpDetail(commentType: commentType, popUpStoreId: popUpStoredId, viewCountYn: isViewCount) } - func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { + public func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { return repository.getPopUpComment(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) } } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index 74714236..1086c284 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -11,7 +11,7 @@ public final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { self.repository = repository } - func trySignUp( + public func trySignUp( nickName: String, gender: String, age: Int32, @@ -31,11 +31,11 @@ public final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { ) } - func checkNickName(nickName: String) -> Observable { + public func checkNickName(nickName: String) -> Observable { return repository.checkNickName(nickName: nickName) } - func fetchCategoryList() -> Observable<[CategoryResponse]> { + public func fetchCategoryList() -> Observable<[CategoryResponse]> { return repository.fetchCategoryList() } } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index a3ea8525..c70de393 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -11,31 +11,31 @@ public final class UserAPIUseCaseImpl: UserAPIUseCase { self.repository = repository } - func postBookmarkPopUp(popUpID: Int64) -> Completable { + public func postBookmarkPopUp(popUpID: Int64) -> Completable { return repository.postBookmarkPopUp(popUpStoreId: popUpID) } - func deleteBookmarkPopUp(popUpID: Int64) -> Completable { + public func deleteBookmarkPopUp(popUpID: Int64) -> Completable { return repository.deleteBookmarkPopUp(popUpStoreId: popUpID) } - func postCommentLike(commentId: Int64) -> Completable { + public func postCommentLike(commentId: Int64) -> Completable { return repository.postCommentLike(commentId: commentId) } - func deleteCommentLike(commentId: Int64) -> Completable { + public func deleteCommentLike(commentId: Int64) -> Completable { return repository.deleteCommentLike(commentId: commentId) } - func postUserBlock(blockedUserId: String?) -> Completable { + public func postUserBlock(blockedUserId: String?) -> Completable { return repository.postUserBlock(blockedUserId: blockedUserId) } - func deleteUserBlock(blockedUserId: String?) -> Completable { + public func deleteUserBlock(blockedUserId: String?) -> Completable { return repository.deleteUserBlock(blockedUserId: blockedUserId) } - func getOtherUserCommentedPopUpList( + public func getOtherUserCommentedPopUpList( commenterId: String?, commentType: String?, page: Int32?, @@ -51,31 +51,31 @@ public final class UserAPIUseCaseImpl: UserAPIUseCase { ) } - func getMyPage() -> Observable { + public func getMyPage() -> Observable { return repository.getMyPage() } - func postLogout() -> Completable { + public func postLogout() -> Completable { return repository.postLogout() } - func getWithdrawlList() -> Observable { + public func getWithdrawlList() -> Observable { return repository.getWithdrawlList() } - func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable { + public func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable { return repository.postWithdrawl(list: surveyList.map { ($0.id, $0.survey)}) } - func getMyProfile() -> Observable { + public func getMyProfile() -> Observable { return repository.getMyProfile() } - func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { + public func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { return repository.putUserTailoredInfo(gender: gender, age: age) } - func putUserCategory( + public func putUserCategory( interestCategoriesToAdd: [Int64], interestCategoriesToDelete: [Int64], interestCategoriesToKeep: [Int64] @@ -87,31 +87,31 @@ public final class UserAPIUseCaseImpl: UserAPIUseCase { ) } - func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable { + public func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable { return repository.putUserProfile(profileImageUrl: profileImageUrl, nickname: nickname, email: email, instagramId: instagramId, intro: intro) } - func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getMyCommentedPopUp(page: page, size: size, sort: sort) } - func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getBlockUserList(page: page, size: size, sort: sort) } - func getNoticeList() -> Observable { + public func getNoticeList() -> Observable { return repository.getNoticeList() } - func getNoticeDetail(noticeID: Int64) -> Observable { + public func getNoticeDetail(noticeID: Int64) -> Observable { return repository.getNoticeDetail(noticeID: noticeID) } - func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getRecentPopUp(page: page, size: size, sort: sort) } - func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { return repository.getBookmarkPopUp(page: page, size: size, sort: sort) } } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift index 5bf58009..b0d5bfc1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift @@ -1,16 +1,16 @@ import Foundation public struct MapPopUpStore: Equatable { - let id: Int64 - let category: String - let name: String - let address: String - let startDate: String - let endDate: String - let latitude: Double - let longitude: Double - let markerId: Int64 - let markerTitle: String - let markerSnippet: String - let mainImageUrl: String? + public let id: Int64 + public let category: String + public let name: String + public let address: String + public let startDate: String + public let endDate: String + public let latitude: Double + public let longitude: Double + public let markerId: Int64 + public let markerTitle: String + public let markerSnippet: String + public let mainImageUrl: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index decf5f72..302881f2 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -5,6 +5,6 @@ public struct GetWithdrawlListResponse { } public struct GetWithdrawlListDataResponse { - var id: Int64 - var survey: String? + public var id: Int64 + public var survey: String? } From 222a73d591b1aa8d9dd8cf68a5cb0590044467f0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 03:06:54 +0900 Subject: [PATCH 167/393] =?UTF-8?q?refactor/#112:=20DomainInterface=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EC=A0=91?= =?UTF-8?q?=EA=B7=BC=EC=A0=9C=EC=96=B4=EC=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Extension/Date?+.swift | 9 +--- .../Infrastructure/Extension/String?+.swift | 2 +- .../ResponseDTO/LoginResponseDTO.swift | 9 +--- .../PostTokenReissueResponseDTO.swift | 9 +--- .../ResponseDTO/BannerPopUpStoreDTO.swift | 9 +--- .../ResponseDTO/GetHomeInfoResponseDTO.swift | 9 +--- .../ResponseDTO/PopUpStoreResponseDTO.swift | 9 +--- .../MapAPI/ResponseDTO/MapPopUpStoreDTO.swift | 2 + .../GetClosePopUpListResponseDTO.swift | 9 +--- .../GetOpenPopUpListResponseDTO.swift | 9 +--- .../GetPopUpCommentResponseDTO.swift | 9 +--- .../GetPopUpDetailResponseDTO.swift | 11 ++--- .../GetSearchPopUpListResponseDTO.swift | 9 +--- .../GetCategoryListResponseDTO.swift | 9 +--- .../GetBlockUserListResponseDTO.swift | 9 +--- .../GetMyCommentedPopUpResponseDTO.swift | 9 +--- .../ResponseDTO/GetMyPageResponseDTO.swift | 9 +--- .../ResponseDTO/GetMyProfileResponseDTO.swift | 9 +--- .../GetNoticeDetailResponseDTO.swift | 9 +--- .../GetNoticeListResponseDTO.swift | 9 +--- ...herUserCommentedPopUpListResponseDTO.swift | 9 +--- .../GetRecentPopUpResponseDTO.swift | 10 ++--- .../GetWithdrawlListResponseDTO.swift | 9 +--- .../AuthResponse/CategoryResponse.swift | 5 +++ .../Entity/AuthResponse/LoginResponse.swift | 11 +++++ .../PostTokenReissueResponse.swift | 7 +++ .../HomeResponse/BannerPopUpStore.swift | 6 +++ .../HomeResponse/GetHomeInfoResponse.swift | 15 +++++++ .../Entity/MapResponse/MapPopUpStore.swift | 15 +++++++ .../GetPopUpCommentResponse.swift | 4 ++ .../GetPopUpDetailResponse.swift | 44 +++++++++++++++++++ .../GetSearchBottomPopUpListResponse.swift | 7 +++ .../GetSearchPopUpListResponse.swift | 5 +++ .../PopUpResponse/PopUpStoreResponse.swift | 11 +++++ .../GetBlockUserListResponse.swift | 13 ++++++ .../UserResponse/GetMyCommentResponse.swift | 15 +++++++ .../UserResponse/GetMyPageResponse.swift | 16 +++++++ .../UserResponse/GetMyProfileResponse.swift | 11 +++++ .../GetNoticeDetailResponse.swift | 7 +++ .../UserResponse/GetNoticeListResponse.swift | 10 +++++ ...tOtherUserCommentedPopUpListResponse.swift | 15 +++++++ .../UserResponse/GetRecentPopUpResponse.swift | 17 +++++++ .../GetWithdrawlListResponse.swift | 9 ++++ 43 files changed, 290 insertions(+), 149 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift index 550cd427..c0a294a2 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Date?+.swift @@ -1,13 +1,6 @@ -// -// Date+.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import Foundation -extension Optional where Wrapped == Date { +public extension Optional where Wrapped == Date { /// `yyyy. MM. dd` 형식으로 날짜를 문자열로 변환합니다. /// - Parameter defaultString: 날짜가 nil일 경우 반환할 기본 문자열 (기본값: 빈 문자열 "") /// - Returns: 형식화된 날짜 문자열 또는 기본 문자열 diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift index 5e7c7831..c06feb51 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift @@ -1,6 +1,6 @@ import UIKit -extension Optional where Wrapped == String { +public extension Optional where Wrapped == String { /// ISO 8601 형식의 문자열을 `Date`로 변환하는 메서드 func toDate() -> Date? { guard let self = self else { return nil } // 옵셔널 해제 diff --git a/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift index 198eccd0..d88322d6 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift @@ -1,12 +1,7 @@ -// -// LoginResponseDTO.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation +import DomainInterface + struct LoginResponseDTO: Decodable { var userId: String var grantType: String diff --git a/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift index 3640caf4..73ccf5ea 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift @@ -1,12 +1,7 @@ -// -// PostTokenReissueResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/16/25. -// - import Foundation +import DomainInterface + struct PostTokenReissueResponseDTO: Decodable { var accessToken: String? var refreshToken: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift index f1eacb95..6f2841ac 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/BannerPopUpStoreDTO.swift @@ -1,12 +1,7 @@ -// -// BannerPopUpStoreDTO.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation +import DomainInterface + struct BannerPopUpStoreDTO: Decodable { var id: Int64 var name: String diff --git a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift index d33857b7..351faf9f 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/GetHomeInfoResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetHomeInfoResponseDTO.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation +import DomainInterface + struct GetHomeInfoResponseDTO: Decodable { var bannerPopUpStoreList: [BannerPopUpStoreDTO] var nickname: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift index 830f3423..4cfb0832 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/ResponseDTO/PopUpStoreResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetHomeInfoDataResponseDTO.swift -// Poppool -// -// Created by Porori on 11/26/24. -// - import Foundation +import DomainInterface + struct PopUpStoreResponseDTO: Decodable { let id: Int64 let categoryName: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift index 7f356980..c5d4a7c7 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/ResponseDTO/MapPopUpStoreDTO.swift @@ -1,5 +1,7 @@ import Foundation +import DomainInterface + struct MapPopUpStoreDTO: Codable { let id: Int64 let categoryName: String diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift index eab1fac5..a57fee94 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetClosePopUpListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetClosePopUpListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation +import DomainInterface + struct GetClosePopUpListResponseDTO: Decodable { var closedPopUpStoreList: [PopUpStoreResponseDTO] var loginYn: Bool diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift index a7194b4b..40d983fc 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetOpenPopUpListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetOpenPopUpListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation +import DomainInterface + struct GetOpenPopUpListResponseDTO: Decodable { var openPopUpStoreList: [PopUpStoreResponseDTO] var loginYn: Bool diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift index fef0aa36..3461f17d 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpCommentResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetPopUpCommentResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import Foundation +import DomainInterface + struct GetPopUpCommentResponseDTO: Decodable { let commentList: [GetPopUpDetailCommentResponseDTO] } diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift index e08b6338..53ea4dee 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetPopUpDetailResponseDTO.swift @@ -1,11 +1,8 @@ -// -// GetPopUpDetailResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import Foundation + +import DomainInterface +import Infrastructure + // MARK: - Main Model struct GetPopUpDetailResponseDTO: Decodable { let name: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift index 60495f23..841ca801 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/ResponseDTO/GetSearchPopUpListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetSearchPopUpListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import Foundation +import DomainInterface + struct GetSearchPopUpListResponseDTO: Decodable { var popUpStoreList: [PopUpStoreResponseDTO] var loginYn: Bool diff --git a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift index bf58db7d..f964e045 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetCategoryListResponseDTO.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation +import DomainInterface + // MARK: - GetCategoryListResponseDTO struct GetCategoryListResponseDTO: Codable { let categoryResponseList: [CategoryResponseDTO] diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift index 7d862c5d..a375ca41 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetBlockUserListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetBlockUserListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import Foundation +import DomainInterface + struct GetBlockUserListResponseDTO: Decodable { var blockedUserInfoList: [GetBlockUserListDataResponseDTO] var totalPages: Int32 diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift index cd3b71ea..5d6591e4 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyCommentedPopUpResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetMyCommentedPopUpResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import Foundation +import DomainInterface + struct GetMyCommentedPopUpResponseDTO: Decodable { var popUpInfoList: [GetMyCommentedPopUpDataResponseDTO] } diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift index 11f511c0..64a1f071 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyPageResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetMyPageResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// - import Foundation +import DomainInterface + struct GetMyPageResponseDTO: Decodable { var nickname: String? var profileImageUrl: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift index 5704cf9b..1ffb4133 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetMyProfileResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetMyProfileResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import Foundation +import DomainInterface + struct GetMyProfileResponseDTO: Decodable { var profileImageUrl: String? var nickname: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift index d8e9baba..f50ff39b 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeDetailResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetNoticeDetailResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import Foundation +import DomainInterface + struct GetNoticeDetailResponseDTO: Decodable { var id: Int64 var title: String? diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift index f2314de9..c280a0a7 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetNoticeListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetNoticeListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import Foundation +import DomainInterface + struct GetNoticeListResponseDTO: Decodable { var noticeInfoList: [GetNoticeListDataResponseDTO] } diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift index 455b5d26..382485bc 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetOtherUserCommentedPopUpListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetOtherUserCommentedPopUpListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import Foundation +import DomainInterface + struct GetOtherUserCommentedPopUpListResponseDTO: Decodable { var popUpInfoList: [GetOtherUserCommentedPopUpResponseDTO] } diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift index 20b4f454..8d02dc95 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetRecentPopUpResponseDTO.swift @@ -1,12 +1,8 @@ -// -// GetRecentPopUpResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import Foundation +import DomainInterface +import Infrastructure + struct GetRecentPopUpResponseDTO: Decodable { var popUpInfoList: [GetRecentPopUpDataResponseDTO] var totalPages: Int32 diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift index 862bccb1..d1c01819 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/ResponseDTO/GetWithdrawlListResponseDTO.swift @@ -1,12 +1,7 @@ -// -// GetWithdrawlListResponseDTO.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import Foundation +import DomainInterface + struct GetWithdrawlListResponseDTO: Decodable { var withDrawlSurveyList: [GetWithdrawlListDataResponseDTO] } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index 8951e68d..db311870 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -1,6 +1,11 @@ import Foundation public struct CategoryResponse { + public init(categoryId: Int64, category: String) { + self.categoryId = categoryId + self.category = category + } + let categoryId: Int64 let category: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift index 676a45b3..f7146e48 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift @@ -1,6 +1,17 @@ import Foundation public struct LoginResponse { + public init(userId: String, grantType: String, accessToken: String, refreshToken: String, accessTokenExpiresAt: String, refreshTokenExpiresAt: String, socialType: String, isRegisteredUser: Bool) { + self.userId = userId + self.grantType = grantType + self.accessToken = accessToken + self.refreshToken = refreshToken + self.accessTokenExpiresAt = accessTokenExpiresAt + self.refreshTokenExpiresAt = refreshTokenExpiresAt + self.socialType = socialType + self.isRegisteredUser = isRegisteredUser + } + var userId: String var grantType: String var accessToken: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift index 3639847f..39c7724d 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/PostTokenReissueResponse.swift @@ -1,6 +1,13 @@ import Foundation public struct PostTokenReissueResponse { + public init(accessToken: String? = nil, refreshToken: String? = nil, accessTokenExpiresAt: String? = nil, refreshTokenExpiresAt: String? = nil) { + self.accessToken = accessToken + self.refreshToken = refreshToken + self.accessTokenExpiresAt = accessTokenExpiresAt + self.refreshTokenExpiresAt = refreshTokenExpiresAt + } + public var accessToken: String? public var refreshToken: String? public var accessTokenExpiresAt: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift index 9f572658..e262b2eb 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift @@ -1,6 +1,12 @@ import Foundation public struct BannerPopUpStore { + public init(id: Int64, name: String, mainImageUrl: String) { + self.id = id + self.name = name + self.mainImageUrl = mainImageUrl + } + var id: Int64 var name: String var mainImageUrl: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift index 10a39158..460b2bfc 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift @@ -1,6 +1,21 @@ import Foundation public struct GetHomeInfoResponse { + public init(bannerPopUpStoreList: [BannerPopUpStore], nickname: String? = nil, customPopUpStoreList: [PopUpStoreResponse], customPopUpStoreTotalPages: Int32, customPopUpStoreTotalElements: Int64, popularPopUpStoreList: [PopUpStoreResponse], popularPopUpStoreTotalPages: Int32, popularPopUpStoreTotalElements: Int64, newPopUpStoreList: [PopUpStoreResponse], newPopUpStoreTotalPages: Int32, newPopUpStoreTotalElements: Int64, loginYn: Bool) { + self.bannerPopUpStoreList = bannerPopUpStoreList + self.nickname = nickname + self.customPopUpStoreList = customPopUpStoreList + self.customPopUpStoreTotalPages = customPopUpStoreTotalPages + self.customPopUpStoreTotalElements = customPopUpStoreTotalElements + self.popularPopUpStoreList = popularPopUpStoreList + self.popularPopUpStoreTotalPages = popularPopUpStoreTotalPages + self.popularPopUpStoreTotalElements = popularPopUpStoreTotalElements + self.newPopUpStoreList = newPopUpStoreList + self.newPopUpStoreTotalPages = newPopUpStoreTotalPages + self.newPopUpStoreTotalElements = newPopUpStoreTotalElements + self.loginYn = loginYn + } + var bannerPopUpStoreList: [BannerPopUpStore] var nickname: String? var customPopUpStoreList: [PopUpStoreResponse] diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift index b0d5bfc1..16fcecb3 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/MapResponse/MapPopUpStore.swift @@ -1,6 +1,21 @@ import Foundation public struct MapPopUpStore: Equatable { + public init(id: Int64, category: String, name: String, address: String, startDate: String, endDate: String, latitude: Double, longitude: Double, markerId: Int64, markerTitle: String, markerSnippet: String, mainImageUrl: String?) { + self.id = id + self.category = category + self.name = name + self.address = address + self.startDate = startDate + self.endDate = endDate + self.latitude = latitude + self.longitude = longitude + self.markerId = markerId + self.markerTitle = markerTitle + self.markerSnippet = markerSnippet + self.mainImageUrl = mainImageUrl + } + public let id: Int64 public let category: String public let name: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift index 2d60f542..63a0fd8f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift @@ -1,5 +1,9 @@ import Foundation public struct GetPopUpCommentResponse { + public init(commentList: [GetPopUpDetailCommentResponse]) { + self.commentList = commentList + } + let commentList: [GetPopUpDetailCommentResponse] } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift index c1b7b27a..52a0c1a6 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift @@ -1,6 +1,24 @@ import Foundation public struct GetPopUpDetailResponse { + public init(name: String?, desc: String?, startDate: String?, endDate: String?, startTime: String?, endTime: String?, address: String?, commentCount: Int64, bookmarkYn: Bool, loginYn: Bool, hasCommented: Bool, mainImageUrl: String?, imageList: [GetPopUpDetailImageResponse], commentList: [GetPopUpDetailCommentResponse], similarPopUpStoreList: [GetPopUpDetailSimilarResponse]) { + self.name = name + self.desc = desc + self.startDate = startDate + self.endDate = endDate + self.startTime = startTime + self.endTime = endTime + self.address = address + self.commentCount = commentCount + self.bookmarkYn = bookmarkYn + self.loginYn = loginYn + self.hasCommented = hasCommented + self.mainImageUrl = mainImageUrl + self.imageList = imageList + self.commentList = commentList + self.similarPopUpStoreList = similarPopUpStoreList + } + let name: String? let desc: String? let startDate: String? @@ -19,11 +37,30 @@ public struct GetPopUpDetailResponse { } public struct GetPopUpDetailImageResponse { + public init(id: Int64, imageUrl: String?) { + self.id = id + self.imageUrl = imageUrl + } + let id: Int64 let imageUrl: String? } public struct GetPopUpDetailCommentResponse { + public init(commentId: Int64, creator: String?, nickname: String?, instagramId: String?, profileImageUrl: String?, content: String?, likeYn: Bool, likeCount: Int64, myCommentYn: Bool, createDateTime: String?, commentImageList: [GetPopUpDetailImageResponse]) { + self.commentId = commentId + self.creator = creator + self.nickname = nickname + self.instagramId = instagramId + self.profileImageUrl = profileImageUrl + self.content = content + self.likeYn = likeYn + self.likeCount = likeCount + self.myCommentYn = myCommentYn + self.createDateTime = createDateTime + self.commentImageList = commentImageList + } + let commentId: Int64 let creator: String? let nickname: String? @@ -38,6 +75,13 @@ public struct GetPopUpDetailCommentResponse { } public struct GetPopUpDetailSimilarResponse { + public init(id: Int64, name: String?, mainImageUrl: String?, endDate: String?) { + self.id = id + self.name = name + self.mainImageUrl = mainImageUrl + self.endDate = endDate + } + let id: Int64 let name: String? let mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift index b639b478..2ef7acb1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift @@ -1,6 +1,13 @@ import Foundation public struct GetSearchBottomPopUpListResponse { + public init(popUpStoreList: [PopUpStoreResponse], loginYn: Bool, totalPages: Int32, totalElements: Int64) { + self.popUpStoreList = popUpStoreList + self.loginYn = loginYn + self.totalPages = totalPages + self.totalElements = totalElements + } + var popUpStoreList: [PopUpStoreResponse] var loginYn: Bool var totalPages: Int32 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift index cca24b8a..bc8908a3 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift @@ -1,6 +1,11 @@ import Foundation public struct GetSearchPopUpListResponse { + public init(popUpStoreList: [PopUpStoreResponse], loginYn: Bool) { + self.popUpStoreList = popUpStoreList + self.loginYn = loginYn + } + var popUpStoreList: [PopUpStoreResponse] var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift index 255d95fb..03182907 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift @@ -1,6 +1,17 @@ import Foundation public struct PopUpStoreResponse { + public init(id: Int64, category: String?, name: String?, address: String?, mainImageUrl: String?, startDate: String?, endDate: String?, bookmarkYn: Bool) { + self.id = id + self.category = category + self.name = name + self.address = address + self.mainImageUrl = mainImageUrl + self.startDate = startDate + self.endDate = endDate + self.bookmarkYn = bookmarkYn + } + let id: Int64 let category: String? let name: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift index 253acf72..0d92623d 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift @@ -1,12 +1,25 @@ import Foundation public struct GetBlockUserListResponse { + public init(blockedUserInfoList: [GetBlockUserListDataResponse], totalPages: Int32, totalElements: Int32) { + self.blockedUserInfoList = blockedUserInfoList + self.totalPages = totalPages + self.totalElements = totalElements + } + var blockedUserInfoList: [GetBlockUserListDataResponse] var totalPages: Int32 var totalElements: Int32 } public struct GetBlockUserListDataResponse { + public init(userId: String? = nil, profileImageUrl: String? = nil, nickname: String? = nil, instagramId: String? = nil) { + self.userId = userId + self.profileImageUrl = profileImageUrl + self.nickname = nickname + self.instagramId = instagramId + } + var userId: String? var profileImageUrl: String? var nickname: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift index 9edacc86..2f35c79a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift @@ -1,10 +1,25 @@ import Foundation public struct GetMyCommentedPopUpResponse { + public init(popUpInfoList: [GetMyCommentedPopUpDataResponse]) { + self.popUpInfoList = popUpInfoList + } + var popUpInfoList: [GetMyCommentedPopUpDataResponse] } public struct GetMyCommentedPopUpDataResponse { + public init(popUpStoreId: Int64, popUpStoreName: String? = nil, desc: String? = nil, mainImageUrl: String? = nil, startDate: String? = nil, endDate: String? = nil, address: String? = nil, closedYn: Bool) { + self.popUpStoreId = popUpStoreId + self.popUpStoreName = popUpStoreName + self.desc = desc + self.mainImageUrl = mainImageUrl + self.startDate = startDate + self.endDate = endDate + self.address = address + self.closedYn = closedYn + } + var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift index 34faf968..81723476 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift @@ -1,6 +1,16 @@ import Foundation public struct GetMyPageResponse { + public init(nickname: String? = nil, profileImageUrl: String? = nil, intro: String? = nil, instagramId: String? = nil, loginYn: Bool, adminYn: Bool, myCommentedPopUpList: [GetMyPagePopUpResponse]) { + self.nickname = nickname + self.profileImageUrl = profileImageUrl + self.intro = intro + self.instagramId = instagramId + self.loginYn = loginYn + self.adminYn = adminYn + self.myCommentedPopUpList = myCommentedPopUpList + } + var nickname: String? var profileImageUrl: String? var intro: String? @@ -11,6 +21,12 @@ public struct GetMyPageResponse { } public struct GetMyPagePopUpResponse { + public init(popUpStoreId: Int64, popUpStoreName: String? = nil, mainImageUrl: String? = nil) { + self.popUpStoreId = popUpStoreId + self.popUpStoreName = popUpStoreName + self.mainImageUrl = mainImageUrl + } + var popUpStoreId: Int64 var popUpStoreName: String? var mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift index 77f0be1b..2a3b0981 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift @@ -1,6 +1,17 @@ import Foundation public struct GetMyProfileResponse { + public init(profileImageUrl: String? = nil, nickname: String? = nil, email: String? = nil, instagramId: String? = nil, intro: String? = nil, gender: String? = nil, age: Int32, interestCategoryList: [CategoryResponse]) { + self.profileImageUrl = profileImageUrl + self.nickname = nickname + self.email = email + self.instagramId = instagramId + self.intro = intro + self.gender = gender + self.age = age + self.interestCategoryList = interestCategoryList + } + var profileImageUrl: String? var nickname: String? var email: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift index dce77077..02cdd67c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift @@ -1,6 +1,13 @@ import Foundation public struct GetNoticeDetailResponse { + public init(id: Int64, title: String? = nil, content: String? = nil, createDateTime: String? = nil) { + self.id = id + self.title = title + self.content = content + self.createDateTime = createDateTime + } + var id: Int64 var title: String? var content: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift index cb0cbfb3..43ed66a1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift @@ -1,10 +1,20 @@ import Foundation public struct GetNoticeListResponse { + public init(noticeInfoList: [GetNoticeListDataResponse]) { + self.noticeInfoList = noticeInfoList + } + var noticeInfoList: [GetNoticeListDataResponse] } public struct GetNoticeListDataResponse { + public init(id: Int64, title: String? = nil, createdDateTime: String? = nil) { + self.id = id + self.title = title + self.createdDateTime = createdDateTime + } + var id: Int64 var title: String? var createdDateTime: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift index af25688a..5545ee3d 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift @@ -1,10 +1,25 @@ import Foundation public struct GetOtherUserCommentedPopUpListResponse { + public init(popUpInfoList: [GetOtherUserCommentedPopUpResponse]) { + self.popUpInfoList = popUpInfoList + } + var popUpInfoList: [GetOtherUserCommentedPopUpResponse] } public struct GetOtherUserCommentedPopUpResponse { + public init(popUpStoreId: Int64, popUpStoreName: String? = nil, desc: String? = nil, mainImageUrl: String? = nil, startDate: String? = nil, endDate: String? = nil, address: String? = nil, closedYn: Bool) { + self.popUpStoreId = popUpStoreId + self.popUpStoreName = popUpStoreName + self.desc = desc + self.mainImageUrl = mainImageUrl + self.startDate = startDate + self.endDate = endDate + self.address = address + self.closedYn = closedYn + } + var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift index 22b7e924..9a463f09 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift @@ -1,12 +1,29 @@ import Foundation public struct GetRecentPopUpResponse { + public init(popUpInfoList: [GetRecentPopUpDataResponse], totalPages: Int32, totalElements: Int32) { + self.popUpInfoList = popUpInfoList + self.totalPages = totalPages + self.totalElements = totalElements + } + var popUpInfoList: [GetRecentPopUpDataResponse] var totalPages: Int32 var totalElements: Int32 } public struct GetRecentPopUpDataResponse { + public init(popUpStoreId: Int64, popUpStoreName: String? = nil, desc: String? = nil, mainImageUrl: String? = nil, startDate: String? = nil, endDate: String? = nil, address: String? = nil, closeYn: Bool) { + self.popUpStoreId = popUpStoreId + self.popUpStoreName = popUpStoreName + self.desc = desc + self.mainImageUrl = mainImageUrl + self.startDate = startDate + self.endDate = endDate + self.address = address + self.closeYn = closeYn + } + var popUpStoreId: Int64 var popUpStoreName: String? var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index 302881f2..cc8d63ca 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -1,10 +1,19 @@ import Foundation public struct GetWithdrawlListResponse { + public init(withDrawlSurveyList: [GetWithdrawlListDataResponse]) { + self.withDrawlSurveyList = withDrawlSurveyList + } + var withDrawlSurveyList: [GetWithdrawlListDataResponse] } public struct GetWithdrawlListDataResponse { + public init(id: Int64, survey: String? = nil) { + self.id = id + self.survey = survey + } + public var id: Int64 public var survey: String? } From cfd37622b144dd46685433444cf3d1b554b36568 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 03:12:14 +0900 Subject: [PATCH 168/393] =?UTF-8?q?refactor/#112:=20=EC=A0=91=EA=B7=BC=20?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Network/API/HomeAPI/HomeAPIEndpoint.swift | 2 + .../PreSignedAPI/PreSignedAPIEndPoint.swift | 9 +--- .../API/SignUpAPI/SignUpAPIEndpoint.swift | 9 +--- .../RepositoryImpl/AdminRepositoryImpl.swift | 16 +++---- .../AuthAPIRepositoryImpl.swift | 4 +- .../RepositoryImpl/MapRepositoryImpl.swift | 6 +-- .../PopUpAPIRepositoryImpl.swift | 12 +++--- .../RepositoryImpl/SignUpRepositoryImpl.swift | 6 +-- .../UserAPIRepositoryImpl.swift | 42 +++++++++---------- .../Entity/AdminResponse/AdminStore.swift | 7 ++++ .../AdminResponse/AdminStoreDetail.swift | 25 +++++++++++ .../AdminResponse/Params/AdminParams.swift | 16 +++---- 12 files changed, 89 insertions(+), 65 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift index 1d38114f..82dd7985 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/HomeAPI/HomeAPIEndpoint.swift @@ -1,5 +1,7 @@ import Foundation +import Infrastructure + struct HomeAPIEndpoint { static func fetchHome( diff --git a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift index 556bf5d2..21d930f2 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift @@ -1,12 +1,7 @@ -// -// PreSignedAPIEndPoint.swift -// Poppool -// -// Created by SeoJunYoung on 11/29/24. -// - import Foundation +import Infrastructure + struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) diff --git a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift index f827b0f0..36527751 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/SignUpAPIEndpoint.swift @@ -1,12 +1,7 @@ -// -// SignUpAPIEndpoint.swift -// Poppool -// -// Created by Porori on 11/25/24. -// - import Foundation +import Infrastructure + struct SignUpAPIEndpoint { /// 닉네임 중복을 확인합니다. diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift index 1a4e68f0..da3f0d33 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift @@ -17,7 +17,7 @@ public final class AdminRepositoryImpl: AdminRepository { } // MARK: - Store Methods - func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { + public func fetchStoreList(query: String?, page: Int, size: Int) -> Observable<[AdminStore]> { let endpoint = AdminAPIEndpoint.fetchStoreList( query: query, page: page, @@ -34,7 +34,7 @@ public final class AdminRepositoryImpl: AdminRepository { } } - func fetchStoreDetail(id: Int64) -> Observable { + public func fetchStoreDetail(id: Int64) -> Observable { let endpoint = AdminAPIEndpoint.fetchStoreDetail(id: id) return provider.requestData( with: endpoint, @@ -74,7 +74,7 @@ public final class AdminRepositoryImpl: AdminRepository { } } - func createStore(params: CreateStoreParams) -> Completable { + public func createStore(params: CreateStoreParams) -> Completable { let dto = CreatePopUpStoreRequestDTO( name: params.name, categoryId: params.categoryId, @@ -94,7 +94,7 @@ public final class AdminRepositoryImpl: AdminRepository { return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func updateStore(params: UpdateStoreParams) -> Completable { + public func updateStore(params: UpdateStoreParams) -> Completable { let dto = UpdatePopUpStoreRequestDTO( popUpStore: UpdatePopUpStoreRequestDTO.PopUpStore( id: params.id, @@ -122,13 +122,13 @@ public final class AdminRepositoryImpl: AdminRepository { return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func deleteStore(id: Int64) -> Completable { + public func deleteStore(id: Int64) -> Completable { let endpoint = AdminAPIEndpoint.deleteStore(id: id) return provider.request(with: endpoint, interceptor: tokenInterceptor) } // MARK: - Notice Methods - func createNotice(params: CreateNoticeParams) -> Completable { + public func createNotice(params: CreateNoticeParams) -> Completable { let dto = CreateNoticeRequestDTO( title: params.title, content: params.content, @@ -138,7 +138,7 @@ public final class AdminRepositoryImpl: AdminRepository { return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func updateNotice(params: UpdateNoticeParams) -> Completable { + public func updateNotice(params: UpdateNoticeParams) -> Completable { let dto = UpdateNoticeRequestDTO( title: params.title, content: params.content, @@ -149,7 +149,7 @@ public final class AdminRepositoryImpl: AdminRepository { return provider.request(with: endpoint, interceptor: tokenInterceptor) } - func deleteNotice(id: Int64) -> Completable { + public func deleteNotice(id: Int64) -> Completable { let endpoint = AdminAPIEndpoint.deleteNotice(id: id) return provider.request(with: endpoint, interceptor: tokenInterceptor) } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift index 43e16ab5..0449cdf0 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AuthAPIRepositoryImpl.swift @@ -13,7 +13,7 @@ public final class AuthAPIRepositoryImpl: AuthAPIRepository { self.provider = provider } - func tryLogIn(userCredential: Encodable, socialType: String) -> Observable { + public func tryLogIn(userCredential: Encodable, socialType: String) -> Observable { let endPoint = AuthAPIEndPoint.auth_tryLogin(with: userCredential, path: socialType) return provider .requestData(with: endPoint, interceptor: nil) @@ -22,7 +22,7 @@ public final class AuthAPIRepositoryImpl: AuthAPIRepository { } } - func postTokenReissue() -> Observable { + public func postTokenReissue() -> Observable { let endPoint = AuthAPIEndPoint.postTokenReissue() return provider.requestData(with: endPoint, interceptor: tokenInterceptor) .map { responseDTO in diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 6d80fff7..236aad21 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -13,7 +13,7 @@ public final class MapRepositoryImpl: MapRepository { self.provider = provider } - func fetchStoresInBounds( + public func fetchStoresInBounds( northEastLat: Double, northEastLon: Double, southWestLat: Double, @@ -33,7 +33,7 @@ public final class MapRepositoryImpl: MapRepository { .map { $0.popUpStoreList.map { $0.toDomain() } } } - func searchStores( + public func searchStores( query: String, categories: [Int64] ) -> Observable<[MapPopUpStore]> { @@ -47,7 +47,7 @@ public final class MapRepositoryImpl: MapRepository { .map { $0.popUpStoreList.map { $0.toDomain() } } } - func fetchCategories() -> Observable<[CategoryResponse]> { + public func fetchCategories() -> Observable<[CategoryResponse]> { Logger.log(message: "카테고리 목록 요청을 시작합니다.", category: .network) return provider.requestData( diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift index cb22fc00..782c7880 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PopUpAPIRepositoryImpl.swift @@ -13,13 +13,13 @@ public final class PopUpAPIRepositoryImpl: PopUpAPIRepository { self.provider = provider } - func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { + public func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { let request = PostBookmarkPopUpRequestDTO(popUpStoreId: popUpStoreId) let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getClosePopUpList( + public func getClosePopUpList( categories: String?, page: Int32?, size: Int32?, @@ -32,7 +32,7 @@ public final class PopUpAPIRepositoryImpl: PopUpAPIRepository { return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getOpenPopUpList( + public func getOpenPopUpList( categories: String?, page: Int32?, size: Int32?, @@ -45,7 +45,7 @@ public final class PopUpAPIRepositoryImpl: PopUpAPIRepository { return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getSearchPopUpList( + public func getSearchPopUpList( categories: String?, page: Int32?, size: Int32?, @@ -58,13 +58,13 @@ public final class PopUpAPIRepositoryImpl: PopUpAPIRepository { return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getPopUpDetail(commentType: String?, popUpStoreId: Int64, viewCountYn: Bool?) -> Observable { + public func getPopUpDetail(commentType: String?, popUpStoreId: Int64, viewCountYn: Bool?) -> Observable { let request = GetPopUpDetailRequestDTO(commentType: commentType, popUpStoreId: popUpStoreId, viewCountYn: viewCountYn) let endPoint = PopUpAPIEndPoint.getPopUpDetail(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { + public func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable { let request = GetPopUpCommentRequestDTO(commentType: commentType, page: page, size: size, sort: sort, popUpStoreId: popUpStoreId) let endPoint = PopUpAPIEndPoint.getPopUpComment(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift index 10225783..f8009fbb 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -12,19 +12,19 @@ public final class SignUpRepositoryImpl: SignUpRepository { self.provider = provider } - func checkNickName(nickName: String) -> Observable { + public func checkNickName(nickName: String) -> Observable { let endPoint = SignUpAPIEndpoint.signUp_checkNickName(with: .init(nickName: nickName)) return provider.requestData(with: endPoint, interceptor: TokenInterceptor()) } - func fetchCategoryList() -> Observable<[CategoryResponse]> { + public func fetchCategoryList() -> Observable<[CategoryResponse]> { let endPoint = SignUpAPIEndpoint.signUp_getCategoryList() return provider.requestData(with: endPoint, interceptor: TokenInterceptor()).map { responseDTO in return responseDTO.categoryResponseList.map({ $0.toDomain() }) } } - func trySignUp( + public func trySignUp( nickName: String, gender: String, age: Int32, diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift index 55ff8a35..2e8d9c98 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -13,37 +13,37 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { self.provider = provider } - func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { + public func postBookmarkPopUp(popUpStoreId: Int64) -> Completable { let endPoint = UserAPIEndPoint.postBookmarkPopUp(request: .init(popUpStoreId: popUpStoreId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteBookmarkPopUp(popUpStoreId: Int64) -> Completable { + public func deleteBookmarkPopUp(popUpStoreId: Int64) -> Completable { let endPoint = UserAPIEndPoint.deleteBookmarkPopUp(request: .init(popUpStoreId: popUpStoreId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func postCommentLike(commentId: Int64) -> Completable { + public func postCommentLike(commentId: Int64) -> Completable { let endPoint = UserAPIEndPoint.postCommentLike(request: .init(commentId: commentId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteCommentLike(commentId: Int64) -> Completable { + public func deleteCommentLike(commentId: Int64) -> Completable { let endPoint = UserAPIEndPoint.deleteCommentLike(request: .init(commentId: commentId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func postUserBlock(blockedUserId: String?) -> Completable { + public func postUserBlock(blockedUserId: String?) -> Completable { let endPoint = UserAPIEndPoint.postUserBlock(request: .init(blockedUserId: blockedUserId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func deleteUserBlock(blockedUserId: String?) -> Completable { + public func deleteUserBlock(blockedUserId: String?) -> Completable { let endPoint = UserAPIEndPoint.deleteUserBlock(request: .init(blockedUserId: blockedUserId)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getOtherUserCommentList( + public func getOtherUserCommentList( commenterId: String?, commentType: String?, page: Int32?, @@ -55,37 +55,37 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getMyPage() -> Observable { + public func getMyPage() -> Observable { let endPoint = UserAPIEndPoint.getMyPage() return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func postLogout() -> Completable { + public func postLogout() -> Completable { let endPoint = UserAPIEndPoint.postLogout() return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getWithdrawlList() -> Observable { + public func getWithdrawlList() -> Observable { let endPoint = UserAPIEndPoint.getWithdrawlList() return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func postWithdrawl(list: [(Int64, String?)]) -> Completable { + public func postWithdrawl(list: [(Int64, String?)]) -> Completable { let endPoint = UserAPIEndPoint.postWithdrawl(request: .init(checkedSurveyList: list.map { .init(id: $0.0, survey: $0.1)})) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getMyProfile() -> Observable { + public func getMyProfile() -> Observable { let endPoint = UserAPIEndPoint.getMyProfile() return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { + public func putUserTailoredInfo(gender: String?, age: Int32) -> Completable { let endPoint = UserAPIEndPoint.putUserTailoredInfo(request: .init(gender: gender, age: age)) return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func putUserCategory( + public func putUserCategory( interestCategoriesToAdd: [Int64], interestCategoriesToDelete: [Int64], interestCategoriesToKeep: [Int64] @@ -99,7 +99,7 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func putUserProfile( + public func putUserProfile( profileImageUrl: String?, nickname: String?, email: String?, @@ -111,7 +111,7 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { return provider.request(with: endPoint, interceptor: tokenInterceptor) } - func getMyCommentedPopUp( + public func getMyCommentedPopUp( page: Int32?, size: Int32?, sort: String? @@ -121,29 +121,29 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBlockUserList(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getNoticeList() -> Observable { + public func getNoticeList() -> Observable { let endPoint = UserAPIEndPoint.getNoticeList() return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getNoticeDetail(noticeID: Int64) -> Observable { + public func getNoticeDetail(noticeID: Int64) -> Observable { let endPoint = UserAPIEndPoint.getNoticeDetail(noticeID: noticeID) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getRecentPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getRecentPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } } - func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { + public func getBookmarkPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable { let request = UserSortedRequestDTO(page: page, size: size, sort: sort) let endPoint = UserAPIEndPoint.getBookmarkPopUp(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor).map { $0.toDomain() } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift index 636685a6..e1d0c9f9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -1,6 +1,13 @@ import Foundation public struct AdminStore { + public init(id: Int64, name: String, categoryName: String, mainImageUrl: String) { + self.id = id + self.name = name + self.categoryName = categoryName + self.mainImageUrl = mainImageUrl + } + public let id: Int64 public let name: String public let categoryName: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index 3cb81611..353eda79 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -1,6 +1,26 @@ import Foundation public struct AdminStoreDetail { + public init(id: Int64, name: String, categoryId: Int64, categoryName: String, description: String, address: String, startDate: String, endDate: String, createUserId: String, createDateTime: String, mainImageUrl: String, bannerYn: Bool, images: [StoreImage], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String) { + self.id = id + self.name = name + self.categoryId = categoryId + self.categoryName = categoryName + self.description = description + self.address = address + self.startDate = startDate + self.endDate = endDate + self.createUserId = createUserId + self.createDateTime = createDateTime + self.mainImageUrl = mainImageUrl + self.bannerYn = bannerYn + self.images = images + self.latitude = latitude + self.longitude = longitude + self.markerTitle = markerTitle + self.markerSnippet = markerSnippet + } + public let id: Int64 public let name: String public let categoryId: Int64 @@ -20,6 +40,11 @@ public struct AdminStoreDetail { public let markerSnippet: String public struct StoreImage { + public init(id: Int64, imageUrl: String) { + self.id = id + self.imageUrl = imageUrl + } + public let id: Int64 public let imageUrl: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift index c92c1fa6..029b9793 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -69,15 +69,15 @@ public struct UpdateStoreParams { } public struct CreateNoticeParams { - let title: String - let content: String - let imageUrlList: [String] + public let title: String + public let content: String + public let imageUrlList: [String] } public struct UpdateNoticeParams { - let id: Int64 - let title: String - let content: String - let imageUrlList: [String] - let imagesToDelete: [Int64] + public let id: Int64 + public let title: String + public let content: String + public let imageUrlList: [String] + public let imagesToDelete: [Int64] } From 1e10d7ae41495b195f1f51930476b1d43e40372e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 03:16:18 +0900 Subject: [PATCH 169/393] =?UTF-8?q?refactor/#112:=20=EC=9D=BC=EB=B6=80=20E?= =?UTF-8?q?xtension=EC=9D=84=20PresentationLayer=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/DesignSystem}/Extension/UIApplication+.swift | 0 .../DesignSystem}/Extension/UICollectionReusableView+.swift | 0 .../DesignSystem}/Extension/UICollectionViewCell+.swift | 0 .../Presentation/DesignSystem}/Extension/UIColor+.swift | 0 .../Presentation/DesignSystem}/Extension/UIFont+.swift | 2 +- .../Presentation/DesignSystem}/Extension/UILabel+.swift | 0 .../DesignSystem}/Extension/UINavigationController+.swift | 0 .../Presentation/DesignSystem}/Extension/UITableViewCell+.swift | 0 .../Presentation/DesignSystem}/Extension/UITextField+.swift | 0 .../Presentation/DesignSystem}/Extension/UIView+.swift | 0 10 files changed, 1 insertion(+), 1 deletion(-) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UIApplication+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UICollectionReusableView+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UICollectionViewCell+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UIColor+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UIFont+.swift (94%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UILabel+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UINavigationController+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UITableViewCell+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UITextField+.swift (100%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/DesignSystem}/Extension/UIView+.swift (100%) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIApplication+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIApplication+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIApplication+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIApplication+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionReusableView+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionReusableView+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionReusableView+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionReusableView+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionViewCell+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UICollectionViewCell+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIColor+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIColor+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIColor+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIFont+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift similarity index 94% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIFont+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift index f8eaab2d..3720eb67 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIFont+.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift @@ -2,7 +2,7 @@ import Foundation import UIKit -extension UIFont { +public extension UIFont { static func korFont(style: FontStyle, size: CGFloat) -> UIFont? { return UIFont(name: "GothicA1\(style.rawValue)", size: size) } diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UILabel+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UILabel+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UILabel+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UILabel+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UINavigationController+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UINavigationController+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UINavigationController+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UINavigationController+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITableViewCell+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITableViewCell+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITableViewCell+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITableViewCell+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITextField+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITextField+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UITextField+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITextField+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIView+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIView+.swift similarity index 100% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIView+.swift rename to Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIView+.swift From 0650a7bcbab97700e35d48fda999019e5983ba60 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 16:07:38 +0900 Subject: [PATCH 170/393] =?UTF-8?q?refactor/#112:=20=EB=B9=A0=EC=A0=B8?= =?UTF-8?q?=EC=9E=88=EB=8D=98=20import,=20public=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Extension/String?+.swift | 2 +- .../Extension/UIImageView+.swift | 2 +- .../Service/AppleLoginService.swift | 17 +++++-------- .../Service/KakaoLoginService.swift | 6 +++-- .../Service/KeyChainService.swift | 2 +- .../Service/UserDefaultService.swift | 6 +++-- .../API/AdminAPI/AdminAPIEndpoint.swift | 2 ++ .../API/MapAPI/FindDirectionEndPoint.swift | 9 ++----- .../Entity/AuthResponse/LoginResponse.swift | 10 ++++---- .../HomeResponse/GetHomeInfoResponse.swift | 14 +++++------ .../PopUpResponse/PopUpStoreResponse.swift | 16 ++++++------ .../GetNoticeDetailResponse.swift | 6 ++--- .../UserResponse/GetRecentPopUpResponse.swift | 22 ++++++++-------- .../Presentation.xcodeproj/project.pbxproj | 25 +++++++++++++++++++ .../AdminBottomSheetView.swift | 5 +++- .../CommentDetailController.swift | 8 ------ .../Scene/Home/List/HomeListReactor.swift | 10 +++----- .../Scene/Login/Main/LoginReactor.swift | 8 ++---- .../Map/MapPopupCardView/PopupCardCell.swift | 2 ++ .../Bookmark/Main/MyPageBookmarkReactor.swift | 10 +++----- .../Detail/MyPageNoticeDetailReactor.swift | 7 +----- .../Interfaces/Sectionable/Sectionable.swift | 9 ++----- 22 files changed, 97 insertions(+), 101 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift index c06feb51..c6bb485a 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/String?+.swift @@ -41,7 +41,7 @@ extension String { } extension String { /// ISO 8601 형식의 문자열을 `Date`로 변환하는 메서드 - func toDate() -> Date? { + public func toDate() -> Date? { let dateFormatter = DateFormatter() dateFormatter.locale = Locale(identifier: "en_US_POSIX") diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift index a1a0b349..cacd212c 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift @@ -1,6 +1,6 @@ import UIKit -extension UIImageView { +public extension UIImageView { func setPPImage(path: String?) { guard let path = path else { self.image = UIImage(named: "image_default") diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift index ecee526f..280851be 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift @@ -1,14 +1,9 @@ -// -// AppleLoginService.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/20/24. -// - import AuthenticationServices import RxSwift -final class AppleLoginService: NSObject, AuthServiceable { +public final class AppleLoginService: NSObject, AuthServiceable { + + public override init() { } // 사용자 자격 증명 정보를 방출할 subject private var authServiceResponse: PublishSubject = .init() @@ -34,7 +29,7 @@ final class AppleLoginService: NSObject, AuthServiceable { extension AppleLoginService: ASAuthorizationControllerPresentationContextProviding, ASAuthorizationControllerDelegate { // 인증 컨트롤러의 프레젠테이션 앵커를 반환하는 함수 - func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { let scenes = UIApplication.shared.connectedScenes let windowSecne = scenes.first as? UIWindowScene guard let window = windowSecne?.windows.first else { @@ -50,7 +45,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi } // 인증 성공 시 호출되는 함수 - func authorizationController( + public func authorizationController( controller: ASAuthorizationController, didCompleteWithAuthorization authorization: ASAuthorization ) { @@ -81,7 +76,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi } } // 인증 실패 시 호출되는 함수 - func authorizationController( + public func authorizationController( controller: ASAuthorizationController, didCompleteWithError error: Error ) { diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift index 382133e9..176395e7 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift @@ -2,7 +2,9 @@ import KakaoSDKAuth import KakaoSDKUser import RxSwift -final class KakaoLoginService: AuthServiceable { +public final class KakaoLoginService: AuthServiceable { + + public init() { } var disposeBag = DisposeBag() @@ -22,7 +24,7 @@ final class KakaoLoginService: AuthServiceable { } } - func fetchUserCredential() -> Observable { + public func fetchUserCredential() -> Observable { return Observable.create { [weak self] observer in guard let self else { Logger.log( diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index b9545cf0..bd6a0c09 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -98,7 +98,7 @@ public final class KeyChainService { /// KeyChain에서 특정 타입의 토큰을 삭제하는 메서드 /// - Parameter type: 삭제하려는 토큰의 타입 (`accessToken` 또는 `refreshToken`) /// - Returns: 완료 시 `Completable` - func deleteToken(type: TokenType) -> Result { + public func deleteToken(type: TokenType) -> Result { // 1. query 작성 let keyChainQuery: NSDictionary = [ kSecClass: kSecClassGenericPassword, diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index ff64c2a6..a938f31c 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -9,7 +9,9 @@ import Foundation import RxSwift -final class UserDefaultService { +public final class UserDefaultService { + + public init() { } /// Userdefault 데이터 저장 메서드 /// - Parameters: @@ -17,7 +19,7 @@ final class UserDefaultService { /// - value: 저장하는 데이터 값 i.e) access token 등 /// - to: 로컬 데이터베이스 타입 - DatabaseType /// - Returns: 별도 안내 없음 - func save(key: String, value: String) { + public func save(key: String, value: String) { UserDefaults.standard.set(value, forKey: key) } diff --git a/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift index 40305bfe..1b7175d1 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/AdminAPI/AdminAPIEndpoint.swift @@ -1,5 +1,7 @@ import Foundation +import Infrastructure + struct EmptyResponse: Decodable {} struct AdminAPIEndpoint { diff --git a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift index 184c0040..e9680478 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/MapAPI/FindDirectionEndPoint.swift @@ -1,12 +1,7 @@ -// -// FindDirectionEndPoint.swift -// Poppool -// -// Created by 김기현 on 1/23/25. -// - import Foundation +import Infrastructure + struct FindDirectionEndPoint { // MARK: - Direction static func fetchDirection( diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift index f7146e48..94e6f175 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/LoginResponse.swift @@ -12,12 +12,12 @@ public struct LoginResponse { self.isRegisteredUser = isRegisteredUser } - var userId: String + public var userId: String var grantType: String - var accessToken: String - var refreshToken: String + public var accessToken: String + public var refreshToken: String var accessTokenExpiresAt: String var refreshTokenExpiresAt: String - var socialType: String - var isRegisteredUser: Bool + public var socialType: String + public var isRegisteredUser: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift index 460b2bfc..f64eac49 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift @@ -18,14 +18,14 @@ public struct GetHomeInfoResponse { var bannerPopUpStoreList: [BannerPopUpStore] var nickname: String? - var customPopUpStoreList: [PopUpStoreResponse] - var customPopUpStoreTotalPages: Int32 + public var customPopUpStoreList: [PopUpStoreResponse] + public var customPopUpStoreTotalPages: Int32 var customPopUpStoreTotalElements: Int64 - var popularPopUpStoreList: [PopUpStoreResponse] - var popularPopUpStoreTotalPages: Int32 + public var popularPopUpStoreList: [PopUpStoreResponse] + public var popularPopUpStoreTotalPages: Int32 var popularPopUpStoreTotalElements: Int64 - var newPopUpStoreList: [PopUpStoreResponse] - var newPopUpStoreTotalPages: Int32 + public var newPopUpStoreList: [PopUpStoreResponse] + public var newPopUpStoreTotalPages: Int32 var newPopUpStoreTotalElements: Int64 - var loginYn: Bool + public var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift index 03182907..5c487da6 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/PopUpStoreResponse.swift @@ -12,12 +12,12 @@ public struct PopUpStoreResponse { self.bookmarkYn = bookmarkYn } - let id: Int64 - let category: String? - let name: String? - let address: String? - let mainImageUrl: String? - let startDate: String? - let endDate: String? - let bookmarkYn: Bool + public let id: Int64 + public let category: String? + public let name: String? + public let address: String? + public let mainImageUrl: String? + public let startDate: String? + public let endDate: String? + public let bookmarkYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift index 02cdd67c..532a7a5c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift @@ -9,7 +9,7 @@ public struct GetNoticeDetailResponse { } var id: Int64 - var title: String? - var content: String? - var createDateTime: String? + public var title: String? + public var content: String? + public var createDateTime: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift index 9a463f09..46d0e070 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift @@ -7,9 +7,9 @@ public struct GetRecentPopUpResponse { self.totalElements = totalElements } - var popUpInfoList: [GetRecentPopUpDataResponse] - var totalPages: Int32 - var totalElements: Int32 + public var popUpInfoList: [GetRecentPopUpDataResponse] + public var totalPages: Int32 + public var totalElements: Int32 } public struct GetRecentPopUpDataResponse { @@ -24,12 +24,12 @@ public struct GetRecentPopUpDataResponse { self.closeYn = closeYn } - var popUpStoreId: Int64 - var popUpStoreName: String? - var desc: String? - var mainImageUrl: String? - var startDate: String? - var endDate: String? - var address: String? - var closeYn: Bool + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var desc: String? + public var mainImageUrl: String? + public var startDate: String? + public var endDate: String? + public var address: String? + public var closeYn: Bool } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index faf1c80f..68a0a032 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B942DB62295001342A2 /* RxSwift-Dynamic */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; @@ -48,6 +49,7 @@ buildActionMask = 2147483647; files = ( 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, + 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -113,6 +115,7 @@ ); name = Presentation; packageProductDependencies = ( + 05125B942DB62295001342A2 /* RxSwift-Dynamic */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -142,6 +145,9 @@ ); mainGroup = 058CC8FA2DB537960084221A; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9052DB537960084221A /* Products */; projectDirPath = ""; @@ -386,6 +392,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05125B942DB62295001342A2 /* RxSwift-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxSwift-Dynamic"; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8FB2DB537960084221A /* Project object */; } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index b23047dc..69a94cbe 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -1,8 +1,11 @@ +import UIKit + +import Infrastructure + import ReactorKit import RxCocoa import RxSwift import SnapKit -import UIKit final class AdminBottomSheetView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift index 49d47a25..6c8b4cbc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift @@ -1,10 +1,3 @@ -// -// CommentDetailController.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit import PanModal @@ -67,7 +60,6 @@ private extension CommentDetailController { // MARK: - Methods extension CommentDetailController { func bind(reactor: Reactor) { - rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift index 776ded93..6e0e002e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift @@ -1,12 +1,8 @@ -// -// HomeListReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/2/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index 6065f763..0ca12f9a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -1,9 +1,5 @@ -// -// LoginReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/24/24. -// +import Infrastructure +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift index 5d2f8f11..7da20209 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapPopupCardView/PopupCardCell.swift @@ -1,5 +1,7 @@ import UIKit +import DomainInterface + import SnapKit final class PopupCardCell: UICollectionViewCell { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 084782dc..56ecea39 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -1,12 +1,8 @@ -// -// MyPageBookmarkReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift index f868b9ed..cb16df03 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift @@ -1,9 +1,4 @@ -// -// MyPageNoticeDetailReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift index 158f9092..7153b6a7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift @@ -1,12 +1,7 @@ -// -// Sectionable.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/29/24. -// - import UIKit +import Infrastructure + import RxSwift import SnapKit From 765644292a6937c32b3449de7f5986350f3e0357 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 18:51:20 +0900 Subject: [PATCH 171/393] =?UTF-8?q?refactor/#112:=20LoginService=20?= =?UTF-8?q?=EC=9D=98=EC=A1=B4=EC=84=B1=20=ED=9D=90=EB=A6=84=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 우선적으로 Service를 Presentation으로 이동 - Entity를 DomainInterface로 이동 --- .../Infrastructure.xcodeproj/project.pbxproj | 29 ------- .../Data/Data.xcodeproj/project.pbxproj | 25 ++++++ .../AuthResponse/AuthServiceResponse.swift | 15 ++++ .../Presentation.xcodeproj/project.pbxproj | 84 +++++++++++++++++++ .../Utills}/Service/AppleLoginService.swift | 3 + .../Utills}/Service/AuthServiceable.swift | 16 +--- .../Utills}/Service/KakaoLoginService.swift | 3 + 7 files changed, 132 insertions(+), 43 deletions(-) create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/Utills}/Service/AppleLoginService.swift (98%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/Utills}/Service/AuthServiceable.swift (55%) rename Poppool/{CoreLayer/Infrastructure/Infrastructure => PresentationLayer/Presentation/Presentation/Utills}/Service/KakaoLoginService.swift (98%) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index 789f6440..b3db38a2 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,12 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 05125B662DB56C21001342A2 /* KakaoSDKAuth in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; - 05125B672DB56C21001342A2 /* KakaoSDKUser in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */; }; 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */; }; - 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */; }; - 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -22,8 +18,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 05125B672DB56C21001342A2 /* KakaoSDKUser in Embed Frameworks */, - 05125B662DB56C21001342A2 /* KakaoSDKAuth in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -48,9 +42,7 @@ buildActionMask = 2147483647; files = ( 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */, - 05C1D8372DB53D3A00508FFD /* KakaoSDKUser in Frameworks */, 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */, - 05C1D8352DB53D3A00508FFD /* KakaoSDKAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -113,8 +105,6 @@ ); name = Infrastructure; packageProductDependencies = ( - 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */, - 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */, 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */, 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */, ); @@ -148,7 +138,6 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */, - 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9192DB5383C0084221A /* Products */; @@ -412,14 +401,6 @@ minimumVersion = 6.9.0; }; }; - 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.24.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -433,16 +414,6 @@ package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = "RxSwift-Dynamic"; }; - 05C1D8342DB53D3A00508FFD /* KakaoSDKAuth */ = { - isa = XCSwiftPackageProductDependency; - package = 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKAuth; - }; - 05C1D8362DB53D3A00508FFD /* KakaoSDKUser */ = { - isa = XCSwiftPackageProductDependency; - package = 05C1D8332DB53D3A00508FFD /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKUser; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC90F2DB5383C0084221A /* Project object */; diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 3a0327d9..2398ebd8 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC93C52DB62B0000771CB3 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -134,6 +135,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC93C52DB62B0000771CB3 /* RxSwift-Dynamic in Frameworks */, 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, ); @@ -197,6 +199,7 @@ ); name = Data; packageProductDependencies = ( + 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */, ); productName = Data; productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; @@ -226,6 +229,9 @@ ); mainGroup = 058CC8D22DB5376A0084221A; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 058CC8DD2DB5376A0084221A /* Products */; projectDirPath = ""; @@ -470,6 +476,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxSwift-Dynamic"; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8D32DB5376A0084221A /* Project object */; } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift new file mode 100644 index 00000000..e6aa5fa0 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift @@ -0,0 +1,15 @@ +import Foundation + +public struct AuthServiceResponse: Encodable { + public init(idToken: String? = nil, authorizationCode: String? = nil, kakaoUserId: Int64? = nil, kakaoAccessToken: String? = nil) { + self.idToken = idToken + self.authorizationCode = authorizationCode + self.kakaoUserId = kakaoUserId + self.kakaoAccessToken = kakaoAccessToken + } + + var idToken: String? + var authorizationCode: String? + var kakaoUserId: Int64? + var kakaoAccessToken: String? +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 68a0a032..b457e926 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -8,10 +8,16 @@ /* Begin PBXBuildFile section */ 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B942DB62295001342A2 /* RxSwift-Dynamic */; }; + 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; + 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */; }; + 05125B9D2DB626FA001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */; }; + 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */; }; + 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -48,9 +54,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */, + 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, + 05125B9D2DB626FA001342A2 /* RxCocoa-Dynamic in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, + 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, + 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */, + 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -116,6 +128,12 @@ name = Presentation; packageProductDependencies = ( 05125B942DB62295001342A2 /* RxSwift-Dynamic */, + 05125B972DB626E3001342A2 /* ReactorKit */, + 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */, + 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */, + 05125BA02DB6275C001342A2 /* PanModal */, + 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */, + 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -147,6 +165,10 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */, + 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */, + 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */, + 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */, + 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9052DB537960084221A /* Products */; @@ -402,6 +424,38 @@ minimumVersion = 6.9.0; }; }; + 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactorKit/ReactorKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.7.1; + }; + }; + 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/slackhq/PanModal"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.7; + }; + }; + 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.24.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -410,6 +464,36 @@ package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; productName = "RxSwift-Dynamic"; }; + 05125B972DB626E3001342A2 /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = "SnapKit-Dynamic"; + }; + 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = "RxCocoa-Dynamic"; + }; + 05125BA02DB6275C001342A2 /* PanModal */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */; + productName = PanModal; + }; + 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8FB2DB537960084221A /* Project object */; diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift similarity index 98% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift index 280851be..91a42cc0 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AppleLoginService.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift @@ -1,3 +1,6 @@ +import Infrastructure +import DomainInterface + import AuthenticationServices import RxSwift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AuthServiceable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AuthServiceable.swift similarity index 55% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AuthServiceable.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AuthServiceable.swift index cd20f3ee..ca1cc728 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/AuthServiceable.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AuthServiceable.swift @@ -1,11 +1,6 @@ -// -// AuthService.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/22/24. -// +import Foundation -import UIKit +import DomainInterface import RxSwift @@ -15,13 +10,6 @@ protocol AuthServiceable: AnyObject { func fetchUserCredential() -> Observable } -struct AuthServiceResponse: Encodable { - var idToken: String? - var authorizationCode: String? - var kakaoUserId: Int64? - var kakaoAccessToken: String? -} - enum AuthError: Error { case notInstalled case unknownError(description: String?) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift similarity index 98% rename from Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift index 176395e7..a7efe24e 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KakaoLoginService.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift @@ -1,3 +1,6 @@ +import Infrastructure +import DomainInterface + import KakaoSDKAuth import KakaoSDKUser import RxSwift From 9df71dd7218af7fd06c72a757c7361a1eaa990bf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 18:56:13 +0900 Subject: [PATCH 172/393] =?UTF-8?q?refactor/#112:=20import,=20public=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 --- .../Service/UserDefaultService.swift | 2 +- .../GetPopUpDirectionResponse.swift | 8 ++++---- .../GetSearchPopUpListResponse.swift | 4 ++-- .../UserResponse/GetNoticeListResponse.swift | 8 ++++---- ...etOtherUserCommentedPopUpListResponse.swift | 18 +++++++++--------- .../Scene/Admin/AdminReactor.swift | 3 +++ .../OtherUserCommentReactor.swift | 10 +++------- .../Scene/Login/Sub/SubLoginReactor.swift | 8 ++------ .../FindMap/MapGuideView/MapGuideReactor.swift | 6 +++++- .../Notice/List/MyPageNoticeReactor.swift | 9 ++------- .../AfterSearch/SearchResultReactor.swift | 10 +++------- .../Utills/Common/ClusteringModels.swift | 2 ++ .../Sectionable/SectionSupplementaryItem.swift | 9 ++------- 13 files changed, 42 insertions(+), 55 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index a938f31c..e91bc37b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -38,7 +38,7 @@ public final class UserDefaultService { /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 /// - from: 로컬 데이터베이스 타입 - DatabaseType /// - Returns: 찾은 데이터 - String 타입 - func fetch(key: String) -> String? { + public func fetch(key: String) -> String? { if let token = UserDefaults.standard.string(forKey: key) { return token } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift index 6686d6d3..8aa4daff 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift @@ -17,12 +17,12 @@ public struct GetPopUpDirectionResponse { let id: Int64 let categoryName: String - let name: String - let address: String + public let name: String + public let address: String let startDate: String let endDate: String - let latitude: Double - let longitude: Double + public let latitude: Double + public let longitude: Double let markerId: Int64 let markerTitle: String let markerSnippet: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift index bc8908a3..e628592f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift @@ -6,6 +6,6 @@ public struct GetSearchPopUpListResponse { self.loginYn = loginYn } - var popUpStoreList: [PopUpStoreResponse] - var loginYn: Bool + public var popUpStoreList: [PopUpStoreResponse] + public var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift index 43ed66a1..121592eb 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift @@ -5,7 +5,7 @@ public struct GetNoticeListResponse { self.noticeInfoList = noticeInfoList } - var noticeInfoList: [GetNoticeListDataResponse] + public var noticeInfoList: [GetNoticeListDataResponse] } public struct GetNoticeListDataResponse { @@ -15,7 +15,7 @@ public struct GetNoticeListDataResponse { self.createdDateTime = createdDateTime } - var id: Int64 - var title: String? - var createdDateTime: String? + public var id: Int64 + public var title: String? + public var createdDateTime: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift index 5545ee3d..ab1710d1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift @@ -5,7 +5,7 @@ public struct GetOtherUserCommentedPopUpListResponse { self.popUpInfoList = popUpInfoList } - var popUpInfoList: [GetOtherUserCommentedPopUpResponse] + public var popUpInfoList: [GetOtherUserCommentedPopUpResponse] } public struct GetOtherUserCommentedPopUpResponse { @@ -20,12 +20,12 @@ public struct GetOtherUserCommentedPopUpResponse { self.closedYn = closedYn } - var popUpStoreId: Int64 - var popUpStoreName: String? - var desc: String? - var mainImageUrl: String? - var startDate: String? - var endDate: String? - var address: String? - var closedYn: Bool + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var desc: String? + public var mainImageUrl: String? + public var startDate: String? + public var endDate: String? + public var address: String? + public var closedYn: Bool } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift index 4e9d8bc9..33da143f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift @@ -1,3 +1,6 @@ +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index ed2ebb51..800567f0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -1,12 +1,8 @@ -// -// OtherUserCommentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 71711306..d257b177 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -1,9 +1,5 @@ -// -// SubLoginReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// +import Infrastructure +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index 085787de..a51aa8ab 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -1,7 +1,11 @@ import CoreLocation +import UIKit + +import DomainInterface +import Infrastructure + import ReactorKit import RxSwift -import UIKit final class MapGuideReactor: Reactor { // MARK: - Actions diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index c5620e42..8af96c6a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index 0122bc4d..6c117497 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -1,12 +1,8 @@ -// -// SearchResultReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift index b0f80b7d..84da1db2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift @@ -1,3 +1,5 @@ +import DomainInterface + import NMapsMap enum MapZoomLevel { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index 2837db0c..c37a5e4b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -1,12 +1,7 @@ -// -// SectionSupplementaryItem.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/29/24. -// - import UIKit +import Infrastructure + /// `SectionSupplementaryItem` 구조체는 섹션에 추가될 Supplementary View에 대한 정보를 정의합니다. /// 제네릭 타입 `View`는 `UICollectionReusableView`와 `InOutputable` 프로토콜을 준수해야 합니다. struct SectionSupplementaryItem: SectionSupplementaryItemable { From 5794c3f83eafd14c52a6c3fb91b7093b571141f2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 18:56:13 +0900 Subject: [PATCH 173/393] =?UTF-8?q?refactor/#112:=20import,=20public=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 --- .../Service/UserDefaultService.swift | 2 +- .../API/PopUpAPI/PopUpAPIEndPoint.swift | 9 ++---- .../Network/API/UserAPI/UserAPIEndPoint.swift | 9 ++---- .../GetPopUpDirectionResponse.swift | 8 ++--- .../GetSearchPopUpListResponse.swift | 4 +-- .../UserResponse/GetNoticeListResponse.swift | 8 ++--- ...tOtherUserCommentedPopUpListResponse.swift | 18 +++++------ .../Presentation.xcodeproj/project.pbxproj | 32 +++++++++---------- .../Scene/Admin/AdminReactor.swift | 3 ++ .../OtherUserCommentReactor.swift | 10 ++---- .../Scene/Login/Sub/SubLoginReactor.swift | 8 ++--- .../MapGuideView/MapGuideReactor.swift | 6 +++- .../Notice/List/MyPageNoticeReactor.swift | 9 ++---- .../AfterSearch/SearchResultReactor.swift | 10 ++---- .../Utills/Common/ClusteringModels.swift | 2 ++ .../SectionSupplementaryItem.swift | 9 ++---- 16 files changed, 62 insertions(+), 85 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index a938f31c..e91bc37b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -38,7 +38,7 @@ public final class UserDefaultService { /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 /// - from: 로컬 데이터베이스 타입 - DatabaseType /// - Returns: 찾은 데이터 - String 타입 - func fetch(key: String) -> String? { + public func fetch(key: String) -> String? { if let token = UserDefaults.standard.string(forKey: key) { return token } diff --git a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift index 6fc60f13..70a968f6 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PopUpAPI/PopUpAPIEndPoint.swift @@ -1,12 +1,7 @@ -// -// PopUpAPIEndPoint.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import Foundation +import Infrastructure + import RxSwift struct PopUpAPIEndPoint { diff --git a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift index eca9ac14..d063d383 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/UserAPI/UserAPIEndPoint.swift @@ -1,12 +1,7 @@ -// -// UserAPIEndPoint.swift -// Poppool -// -// Created by SeoJunYoung on 12/3/24. -// - import Foundation +import Infrastructure + import RxSwift struct UserAPIEndPoint { diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift index 6686d6d3..8aa4daff 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift @@ -17,12 +17,12 @@ public struct GetPopUpDirectionResponse { let id: Int64 let categoryName: String - let name: String - let address: String + public let name: String + public let address: String let startDate: String let endDate: String - let latitude: Double - let longitude: Double + public let latitude: Double + public let longitude: Double let markerId: Int64 let markerTitle: String let markerSnippet: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift index bc8908a3..e628592f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift @@ -6,6 +6,6 @@ public struct GetSearchPopUpListResponse { self.loginYn = loginYn } - var popUpStoreList: [PopUpStoreResponse] - var loginYn: Bool + public var popUpStoreList: [PopUpStoreResponse] + public var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift index 43ed66a1..121592eb 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift @@ -5,7 +5,7 @@ public struct GetNoticeListResponse { self.noticeInfoList = noticeInfoList } - var noticeInfoList: [GetNoticeListDataResponse] + public var noticeInfoList: [GetNoticeListDataResponse] } public struct GetNoticeListDataResponse { @@ -15,7 +15,7 @@ public struct GetNoticeListDataResponse { self.createdDateTime = createdDateTime } - var id: Int64 - var title: String? - var createdDateTime: String? + public var id: Int64 + public var title: String? + public var createdDateTime: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift index 5545ee3d..ab1710d1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift @@ -5,7 +5,7 @@ public struct GetOtherUserCommentedPopUpListResponse { self.popUpInfoList = popUpInfoList } - var popUpInfoList: [GetOtherUserCommentedPopUpResponse] + public var popUpInfoList: [GetOtherUserCommentedPopUpResponse] } public struct GetOtherUserCommentedPopUpResponse { @@ -20,12 +20,12 @@ public struct GetOtherUserCommentedPopUpResponse { self.closedYn = closedYn } - var popUpStoreId: Int64 - var popUpStoreName: String? - var desc: String? - var mainImageUrl: String? - var startDate: String? - var endDate: String? - var address: String? - var closedYn: Bool + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var desc: String? + public var mainImageUrl: String? + public var startDate: String? + public var endDate: String? + public var address: String? + public var closedYn: Bool } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index b457e926..3c4595ff 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -7,10 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B942DB62295001342A2 /* RxSwift-Dynamic */; }; 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */; }; - 05125B9D2DB626FA001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */; }; 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -18,6 +16,8 @@ 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */; }; 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; + 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D22DB6536200771CB3 /* RxCocoa */; }; + 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D42DB6536200771CB3 /* RxSwift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -54,14 +54,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */, 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, - 05125B952DB62295001342A2 /* RxSwift-Dynamic in Frameworks */, 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, - 05125B9D2DB626FA001342A2 /* RxCocoa-Dynamic in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */, + 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */, 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -127,13 +127,13 @@ ); name = Presentation; packageProductDependencies = ( - 05125B942DB62295001342A2 /* RxSwift-Dynamic */, 05125B972DB626E3001342A2 /* ReactorKit */, 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */, - 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */, 05125BA02DB6275C001342A2 /* PanModal */, 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */, 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, + 05EC93D22DB6536200771CB3 /* RxCocoa */, + 05EC93D42DB6536200771CB3 /* RxSwift */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -459,11 +459,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05125B942DB62295001342A2 /* RxSwift-Dynamic */ = { - isa = XCSwiftPackageProductDependency; - package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; - }; 05125B972DB626E3001342A2 /* ReactorKit */ = { isa = XCSwiftPackageProductDependency; package = 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */; @@ -474,11 +469,6 @@ package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = "SnapKit-Dynamic"; }; - 05125B9C2DB626FA001342A2 /* RxCocoa-Dynamic */ = { - isa = XCSwiftPackageProductDependency; - package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxCocoa-Dynamic"; - }; 05125BA02DB6275C001342A2 /* PanModal */ = { isa = XCSwiftPackageProductDependency; package = 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */; @@ -494,6 +484,16 @@ package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; productName = KakaoSDKUser; }; + 05EC93D22DB6536200771CB3 /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 05EC93D42DB6536200771CB3 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8FB2DB537960084221A /* Project object */; diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift index 4e9d8bc9..33da143f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift @@ -1,3 +1,6 @@ +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index ed2ebb51..800567f0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -1,12 +1,8 @@ -// -// OtherUserCommentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 71711306..d257b177 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -1,9 +1,5 @@ -// -// SubLoginReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// +import Infrastructure +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index 085787de..a51aa8ab 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -1,7 +1,11 @@ import CoreLocation +import UIKit + +import DomainInterface +import Infrastructure + import ReactorKit import RxSwift -import UIKit final class MapGuideReactor: Reactor { // MARK: - Actions diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index c5620e42..8af96c6a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index 0122bc4d..6c117497 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -1,12 +1,8 @@ -// -// SearchResultReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift index b0f80b7d..84da1db2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringModels.swift @@ -1,3 +1,5 @@ +import DomainInterface + import NMapsMap enum MapZoomLevel { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index 2837db0c..c37a5e4b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -1,12 +1,7 @@ -// -// SectionSupplementaryItem.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/29/24. -// - import UIKit +import Infrastructure + /// `SectionSupplementaryItem` 구조체는 섹션에 추가될 Supplementary View에 대한 정보를 정의합니다. /// 제네릭 타입 `View`는 `UICollectionReusableView`와 `InOutputable` 프로토콜을 준수해야 합니다. struct SectionSupplementaryItem: SectionSupplementaryItemable { From 89b938f05fd2cc21a411cc9aebd52bac3edbf4db Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 19:25:07 +0900 Subject: [PATCH 174/393] =?UTF-8?q?refactor/#112:=20PreSignedUseCase=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EC=9E=90=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../OtherUserComment/OtherUserCommentReactor.swift | 3 ++- .../Scene/Home/List/HomeListReactor.swift | 3 ++- .../Search/AfterSearch/SearchResultReactor.swift | 3 ++- .../Scene/Search/BeforeSearch/SearchReactor.swift | 13 +++++-------- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index 800567f0..8d8506db 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -107,7 +107,8 @@ final class OtherUserCommentReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift index 6e0e002e..f958730d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift @@ -150,7 +150,8 @@ final class HomeListReactor: Reactor { popUpID: cardSections.inputDataList[row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index 6c117497..cb8c25b1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -128,7 +128,8 @@ final class SearchResultReactor: Reactor { popUpID: searchListSection.inputDataList[indexPath.row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 9e90a637..17cd6119 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -1,12 +1,8 @@ -// -// SearchReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -231,7 +227,8 @@ final class SearchReactor: Reactor { popUpID: searchListSection.inputDataList[indexPath.row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .setSearchKeyWord(let text): From eeccce62a6c4ff499d60d086af45535007d5d014 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 20:30:03 +0900 Subject: [PATCH 175/393] =?UTF-8?q?refactor/#112:=20import,=20public=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 37 ++++++++--- .../Infrastructure/Extension/Reactive+.swift | 9 +-- .../HomeResponse/BannerPopUpStore.swift | 6 +- .../HomeResponse/GetHomeInfoResponse.swift | 4 +- .../GetPopUpDetailResponse.swift | 64 +++++++++---------- .../GetBlockUserListResponse.swift | 14 ++-- .../UserResponse/GetMyCommentResponse.swift | 18 +++--- .../UserResponse/GetMyPageResponse.swift | 20 +++--- .../GetWithdrawlListResponse.swift | 2 +- .../AdminBottomSheetViewController.swift | 5 +- .../PopUpStoreRegisterViewController.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 12 ++-- .../Scene/Home/Main/HomeReactor.swift | 24 +++++-- .../Scene/Login/Main/LoginController.swift | 2 + .../FullScreenMapViewController.swift | 6 +- .../Scene/Map/MapView/MapReactor.swift | 4 ++ .../Scene/Map/MapView/MapViewController.swift | 9 ++- .../Scene/Map/MapView/MarkerTooltipView.swift | 5 +- .../MyPage/Block/BlockUserManageReactor.swift | 9 +-- .../Bookmark/Main/MyPageBookmarkReactor.swift | 3 +- .../Scene/MyPage/Main/MyPageReactor.swift | 17 +++-- .../MyComment/Main/MyCommentReactor.swift | 13 ++-- .../MyPage/Recent/MyPageRecentReactor.swift | 13 ++-- .../WithdrawlReasonReactor.swift | 10 +-- .../Scene/SignUp/Main/SignUpMainReactor.swift | 8 +-- .../SignUp/Step2/SignUpStep2Reactor.swift | 10 +-- .../TabbarController/TabbarController.swift | 10 +-- .../Controllers/BaseTabmanController.swift | 9 +-- .../Utills/ToastMaker/ToastMaker.swift | 10 +-- 29 files changed, 182 insertions(+), 173 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index b3db38a2..70ccf6fe 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */; }; - 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */; }; + 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; + 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DA2DB6605100771CB3 /* RxSwift */; }; + 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -41,8 +42,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */, - 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */, + 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */, + 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, + 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,8 +107,9 @@ ); name = Infrastructure; packageProductDependencies = ( - 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */, - 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */, + 05EC93D82DB6605100771CB3 /* RxCocoa */, + 05EC93DA2DB6605100771CB3 /* RxSwift */, + 05EC93DD2DB6612100771CB3 /* RxGesture */, ); productName = Infrastructure; productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; @@ -138,6 +141,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */, + 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9192DB5383C0084221A /* Products */; @@ -401,18 +405,31 @@ minimumVersion = 6.9.0; }; }; + 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.4; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */ = { + 05EC93D82DB6605100771CB3 /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxCocoa-Dynamic"; + productName = RxCocoa; }; - 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */ = { + 05EC93DA2DB6605100771CB3 /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; + productName = RxSwift; + }; + 05EC93DD2DB6612100771CB3 /* RxGesture */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */; + productName = RxGesture; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift index 74665b05..68a22b6c 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift @@ -1,16 +1,9 @@ -// -// Reactive+.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/26/24. -// - import UIKit import RxCocoa import RxSwift -extension Reactive where Base: UIViewController { +public extension Reactive where Base: UIViewController { var viewDidLoad: ControlEvent { let source = self.methodInvoked(#selector(Base.viewDidLoad)).map( { _ in }) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift index e262b2eb..2c5efa15 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift @@ -7,7 +7,7 @@ public struct BannerPopUpStore { self.mainImageUrl = mainImageUrl } - var id: Int64 - var name: String - var mainImageUrl: String + public var id: Int64 + public var name: String + public var mainImageUrl: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift index f64eac49..b3ee4e93 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift @@ -16,8 +16,8 @@ public struct GetHomeInfoResponse { self.loginYn = loginYn } - var bannerPopUpStoreList: [BannerPopUpStore] - var nickname: String? + public var bannerPopUpStoreList: [BannerPopUpStore] + public var nickname: String? public var customPopUpStoreList: [PopUpStoreResponse] public var customPopUpStoreTotalPages: Int32 var customPopUpStoreTotalElements: Int64 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift index 52a0c1a6..be18c5c7 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift @@ -19,21 +19,21 @@ public struct GetPopUpDetailResponse { self.similarPopUpStoreList = similarPopUpStoreList } - let name: String? - let desc: String? - let startDate: String? - let endDate: String? - let startTime: String? - let endTime: String? - let address: String? - let commentCount: Int64 - let bookmarkYn: Bool - let loginYn: Bool - let hasCommented: Bool - let mainImageUrl: String? - let imageList: [GetPopUpDetailImageResponse] - let commentList: [GetPopUpDetailCommentResponse] - let similarPopUpStoreList: [GetPopUpDetailSimilarResponse] + public let name: String? + public let desc: String? + public let startDate: String? + public let endDate: String? + public let startTime: String? + public let endTime: String? + public let address: String? + public let commentCount: Int64 + public let bookmarkYn: Bool + public let loginYn: Bool + public let hasCommented: Bool + public let mainImageUrl: String? + public let imageList: [GetPopUpDetailImageResponse] + public let commentList: [GetPopUpDetailCommentResponse] + public let similarPopUpStoreList: [GetPopUpDetailSimilarResponse] } public struct GetPopUpDetailImageResponse { @@ -42,8 +42,8 @@ public struct GetPopUpDetailImageResponse { self.imageUrl = imageUrl } - let id: Int64 - let imageUrl: String? + public let id: Int64 + public let imageUrl: String? } public struct GetPopUpDetailCommentResponse { @@ -61,17 +61,17 @@ public struct GetPopUpDetailCommentResponse { self.commentImageList = commentImageList } - let commentId: Int64 - let creator: String? - let nickname: String? - let instagramId: String? - let profileImageUrl: String? - let content: String? - let likeYn: Bool - let likeCount: Int64 - let myCommentYn: Bool - let createDateTime: String? - let commentImageList: [GetPopUpDetailImageResponse] + public let commentId: Int64 + public let creator: String? + public let nickname: String? + public let instagramId: String? + public let profileImageUrl: String? + public let content: String? + public let likeYn: Bool + public let likeCount: Int64 + public let myCommentYn: Bool + public let createDateTime: String? + public let commentImageList: [GetPopUpDetailImageResponse] } public struct GetPopUpDetailSimilarResponse { @@ -82,8 +82,8 @@ public struct GetPopUpDetailSimilarResponse { self.endDate = endDate } - let id: Int64 - let name: String? - let mainImageUrl: String? - let endDate: String? + public let id: Int64 + public let name: String? + public let mainImageUrl: String? + public let endDate: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift index 0d92623d..a1495ca9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift @@ -7,9 +7,9 @@ public struct GetBlockUserListResponse { self.totalElements = totalElements } - var blockedUserInfoList: [GetBlockUserListDataResponse] - var totalPages: Int32 - var totalElements: Int32 + public var blockedUserInfoList: [GetBlockUserListDataResponse] + public var totalPages: Int32 + public var totalElements: Int32 } public struct GetBlockUserListDataResponse { @@ -20,8 +20,8 @@ public struct GetBlockUserListDataResponse { self.instagramId = instagramId } - var userId: String? - var profileImageUrl: String? - var nickname: String? - var instagramId: String? + public var userId: String? + public var profileImageUrl: String? + public var nickname: String? + public var instagramId: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift index 2f35c79a..0f1ecacf 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift @@ -5,7 +5,7 @@ public struct GetMyCommentedPopUpResponse { self.popUpInfoList = popUpInfoList } - var popUpInfoList: [GetMyCommentedPopUpDataResponse] + public var popUpInfoList: [GetMyCommentedPopUpDataResponse] } public struct GetMyCommentedPopUpDataResponse { @@ -20,12 +20,12 @@ public struct GetMyCommentedPopUpDataResponse { self.closedYn = closedYn } - var popUpStoreId: Int64 - var popUpStoreName: String? - var desc: String? - var mainImageUrl: String? - var startDate: String? - var endDate: String? - var address: String? - var closedYn: Bool + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var desc: String? + public var mainImageUrl: String? + public var startDate: String? + public var endDate: String? + public var address: String? + public var closedYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift index 81723476..b7d1dd73 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift @@ -11,13 +11,13 @@ public struct GetMyPageResponse { self.myCommentedPopUpList = myCommentedPopUpList } - var nickname: String? - var profileImageUrl: String? - var intro: String? - var instagramId: String? - var loginYn: Bool - var adminYn: Bool - var myCommentedPopUpList: [GetMyPagePopUpResponse] + public var nickname: String? + public var profileImageUrl: String? + public var intro: String? + public var instagramId: String? + public var loginYn: Bool + public var adminYn: Bool + public var myCommentedPopUpList: [GetMyPagePopUpResponse] } public struct GetMyPagePopUpResponse { @@ -27,7 +27,7 @@ public struct GetMyPagePopUpResponse { self.mainImageUrl = mainImageUrl } - var popUpStoreId: Int64 - var popUpStoreName: String? - var mainImageUrl: String? + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var mainImageUrl: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index cc8d63ca..636ee8f9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -5,7 +5,7 @@ public struct GetWithdrawlListResponse { self.withDrawlSurveyList = withDrawlSurveyList } - var withDrawlSurveyList: [GetWithdrawlListDataResponse] + public var withDrawlSurveyList: [GetWithdrawlListDataResponse] } public struct GetWithdrawlListDataResponse { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 4f3a1a7b..89ee0c06 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -1,8 +1,11 @@ +import UIKit + +import Infrastructure + import ReactorKit import RxCocoa import RxSwift import SnapKit -import UIKit final class AdminBottomSheetViewController: BaseViewController, View { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 387022a5..6d4523e8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -32,7 +32,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { let reactor = PopUpStoreRegisterReactor( adminUseCase: DIContainer.resolve(AdminUseCase.self), - preSignedUseCase: DIContainer.resolve(PreSignedUseCasae.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self), editingStore: editingStore ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 4335cc05..71a11147 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -161,7 +161,8 @@ final class DetailReactor: Reactor { commentController.reactor = NormalCommentAddReactor( popUpID: popUpID, popUpName: popUpName ?? "", - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(commentController, animated: true) } else { @@ -198,7 +199,8 @@ final class DetailReactor: Reactor { popUpName: popUpName, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) } else { @@ -232,7 +234,8 @@ final class DetailReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): @@ -548,7 +551,8 @@ extension DetailReactor { popUpID: self.popUpID, popUpName: popUpName, comment: comment, - commentAPIUseCase: self.commentAPIUseCase + commentAPIUseCase: self.commentAPIUseCase, + preSignedUseCase: self.preSignedUseCase ) controller?.navigationController?.pushViewController(editController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift index 228abf0a..b27415c1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift @@ -1,5 +1,8 @@ import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -281,7 +284,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -300,7 +304,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -318,7 +323,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 12: @@ -336,7 +342,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: @@ -351,7 +358,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -370,7 +378,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -388,7 +397,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift index 47912023..df5d00de 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift @@ -7,6 +7,8 @@ import UIKit +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 82393654..b6f8989a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,9 +1,13 @@ import CoreLocation +import UIKit + +import DomainInterface +import Infrastructure + import NMapsMap import RxCocoa import RxSwift import SnapKit -import UIKit class FullScreenMapViewController: MapViewController { // MARK: - Properties diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift index d5d5d9b8..bf79b064 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift @@ -1,4 +1,8 @@ import CoreLocation + +import DomainInterface +import Infrastructure + import ReactorKit import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 243ad3f6..1c275561 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1,4 +1,9 @@ +import UIKit import CoreLocation + +import DomainInterface +import Infrastructure + import FloatingPanel import NMapsMap import ReactorKit @@ -6,7 +11,6 @@ import RxCocoa import RxGesture import RxSwift import SnapKit -import UIKit class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NMFMapViewTouchDelegate, NMFMapViewCameraDelegate, UIGestureRecognizerDelegate { typealias Reactor = MapReactor @@ -126,7 +130,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM popUpID: Int64(store.id), userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), popUpAPIUseCase: self?.popUpAPIUseCase ?? DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) self?.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift index a1c2bffe..ed8ce855 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DomainInterface + +import SnapKit + final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { // MARK: - Properties diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index e0439e64..7d8bc987 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -1,12 +1,7 @@ -// -// BlockUserManageReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 56ecea39..fd82c499 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -179,7 +179,8 @@ final class MyPageBookmarkReactor: Reactor { popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .presentModal(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 3150edd4..3fe8c1d9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -1,14 +1,11 @@ -// -// MyPageReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// +import UIKit + +import DomainInterface +import Infrastructure import ReactorKit import RxCocoa import RxSwift -import UIKit final class MyPageReactor: Reactor { @@ -183,7 +180,8 @@ final class MyPageReactor: Reactor { let nextController = ProfileEditController() nextController.reactor = ProfileEditReactor( userAPIUseCase: userAPIUseCase, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) @@ -262,7 +260,8 @@ final class MyPageReactor: Reactor { popUpID: popUpID, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index a41eb076..2b4911db 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -1,12 +1,8 @@ -// -// MyCommentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/8/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -111,7 +107,8 @@ final class MyCommentReactor: Reactor { popUpID: popUpID, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 06318f0a..94b3f651 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -1,12 +1,8 @@ -// -// MyPageRecentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -130,7 +126,8 @@ final class MyPageRecentReactor: Reactor { popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } 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 4748166b..8cb10ad6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -1,12 +1,8 @@ -// -// WithdrawlReasonReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index adeaa99e..c4f3db3a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -1,9 +1,5 @@ -// -// SignUpMainReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// +import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift index 24f21cfb..3c700b01 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift @@ -1,12 +1,8 @@ -// -// SignUpStep2Reactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift index 4be350da..8bee4e8b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift @@ -1,12 +1,8 @@ -// -// TabbarController.swift -// Poppool -// -// Created by SeoJunYoung on 12/1/24. -// - import UIKit +import DomainInterface +import Infrastructure + class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { private let waveLayer = CAShapeLayer() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift index 639a66e5..42a777dd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift @@ -1,12 +1,7 @@ -// -// BaseTabmanController.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/25/24. -// - import UIKit +import Infrastructure + import Pageboy import Tabman diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift index 924c7b0e..7c6f29be 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift @@ -1,12 +1,8 @@ -// -// ToastMaker.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface +import Infrastructure + import RxCocoa import RxSwift import SnapKit From 190941a7777aa19257814d5c141741afdc6ba707 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 20:30:03 +0900 Subject: [PATCH 176/393] =?UTF-8?q?refactor/#112:=20import,=20public=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 37 ++++++++--- .../Infrastructure/Extension/Reactive+.swift | 9 +-- .../HomeResponse/BannerPopUpStore.swift | 6 +- .../HomeResponse/GetHomeInfoResponse.swift | 4 +- .../GetPopUpCommentResponse.swift | 2 +- .../GetPopUpDetailResponse.swift | 64 +++++++++---------- .../GetPopUpDirectionResponse.swift | 14 ++-- .../GetBlockUserListResponse.swift | 14 ++-- .../UserResponse/GetMyCommentResponse.swift | 18 +++--- .../UserResponse/GetMyPageResponse.swift | 20 +++--- .../GetWithdrawlListResponse.swift | 2 +- .../AdminBottomSheetViewController.swift | 5 +- .../PopUpStoreRegisterViewController.swift | 2 +- .../CommentList/CommentListReactor.swift | 3 +- .../InstaCommentAddController.swift | 9 +-- .../Scene/Detail/DetailReactor.swift | 12 ++-- .../Scene/Home/Main/HomeReactor.swift | 24 +++++-- .../Scene/Login/Main/LoginController.swift | 2 + .../FullScreenMapViewController.swift | 6 +- .../MapGuideView/MapGuideViewController.swift | 6 +- .../Scene/Map/MapView/MapReactor.swift | 8 ++- .../Scene/Map/MapView/MapViewController.swift | 9 ++- .../Scene/Map/MapView/MarkerTooltipView.swift | 5 +- .../Map/StoreListView/StoreListReactor.swift | 4 ++ .../StoreListViewController.swift | 9 ++- .../MyPage/Block/BlockUserManageReactor.swift | 9 +-- .../Bookmark/Main/MyPageBookmarkReactor.swift | 3 +- .../Scene/MyPage/Main/MyPageReactor.swift | 17 +++-- .../MyComment/Main/MyCommentReactor.swift | 13 ++-- .../Main/ProfileEditController.swift | 9 +-- .../MyPage/Recent/MyPageRecentReactor.swift | 13 ++-- .../WithdrawlReasonReactor.swift | 10 +-- .../SearchCategoryReactor.swift | 9 +-- .../Search/Main/SearchMainController.swift | 10 +-- .../SignUp/Main/SignUpMainController.swift | 10 +-- .../Scene/SignUp/Main/SignUpMainReactor.swift | 8 +-- .../SignUp/Step2/SignUpStep2Reactor.swift | 10 +-- .../SignUp/Step3/SignUpStep3Reactor.swift | 9 +-- .../Scene/Splash/SplashController.swift | 4 -- .../TabbarController/TabbarController.swift | 10 +-- .../Utills/Common/ClusteringManager.swift | 3 + .../Controllers/BaseTabmanController.swift | 9 +-- .../Controllers/BaseViewController.swift | 4 +- .../Utills/ToastMaker/ToastMaker.swift | 10 +-- 44 files changed, 228 insertions(+), 236 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index b3db38a2..70ccf6fe 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */; }; - 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */; }; + 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; + 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DA2DB6605100771CB3 /* RxSwift */; }; + 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -41,8 +42,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05125B732DB56C5E001342A2 /* RxSwift-Dynamic in Frameworks */, - 05125B702DB56C5E001342A2 /* RxCocoa-Dynamic in Frameworks */, + 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */, + 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, + 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -105,8 +107,9 @@ ); name = Infrastructure; packageProductDependencies = ( - 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */, - 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */, + 05EC93D82DB6605100771CB3 /* RxCocoa */, + 05EC93DA2DB6605100771CB3 /* RxSwift */, + 05EC93DD2DB6612100771CB3 /* RxGesture */, ); productName = Infrastructure; productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; @@ -138,6 +141,7 @@ minimizedProjectReferenceProxies = 1; packageReferences = ( 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */, + 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9192DB5383C0084221A /* Products */; @@ -401,18 +405,31 @@ minimumVersion = 6.9.0; }; }; + 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.4; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05125B6F2DB56C5E001342A2 /* RxCocoa-Dynamic */ = { + 05EC93D82DB6605100771CB3 /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxCocoa-Dynamic"; + productName = RxCocoa; }; - 05125B722DB56C5E001342A2 /* RxSwift-Dynamic */ = { + 05EC93DA2DB6605100771CB3 /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; + productName = RxSwift; + }; + 05EC93DD2DB6612100771CB3 /* RxGesture */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */; + productName = RxGesture; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift index 74665b05..68a22b6c 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Reactive+.swift @@ -1,16 +1,9 @@ -// -// Reactive+.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/26/24. -// - import UIKit import RxCocoa import RxSwift -extension Reactive where Base: UIViewController { +public extension Reactive where Base: UIViewController { var viewDidLoad: ControlEvent { let source = self.methodInvoked(#selector(Base.viewDidLoad)).map( { _ in }) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift index e262b2eb..2c5efa15 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/BannerPopUpStore.swift @@ -7,7 +7,7 @@ public struct BannerPopUpStore { self.mainImageUrl = mainImageUrl } - var id: Int64 - var name: String - var mainImageUrl: String + public var id: Int64 + public var name: String + public var mainImageUrl: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift index f64eac49..b3ee4e93 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/HomeResponse/GetHomeInfoResponse.swift @@ -16,8 +16,8 @@ public struct GetHomeInfoResponse { self.loginYn = loginYn } - var bannerPopUpStoreList: [BannerPopUpStore] - var nickname: String? + public var bannerPopUpStoreList: [BannerPopUpStore] + public var nickname: String? public var customPopUpStoreList: [PopUpStoreResponse] public var customPopUpStoreTotalPages: Int32 var customPopUpStoreTotalElements: Int64 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift index 63a0fd8f..5f8e5ae9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift @@ -5,5 +5,5 @@ public struct GetPopUpCommentResponse { self.commentList = commentList } - let commentList: [GetPopUpDetailCommentResponse] + public let commentList: [GetPopUpDetailCommentResponse] } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift index 52a0c1a6..be18c5c7 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift @@ -19,21 +19,21 @@ public struct GetPopUpDetailResponse { self.similarPopUpStoreList = similarPopUpStoreList } - let name: String? - let desc: String? - let startDate: String? - let endDate: String? - let startTime: String? - let endTime: String? - let address: String? - let commentCount: Int64 - let bookmarkYn: Bool - let loginYn: Bool - let hasCommented: Bool - let mainImageUrl: String? - let imageList: [GetPopUpDetailImageResponse] - let commentList: [GetPopUpDetailCommentResponse] - let similarPopUpStoreList: [GetPopUpDetailSimilarResponse] + public let name: String? + public let desc: String? + public let startDate: String? + public let endDate: String? + public let startTime: String? + public let endTime: String? + public let address: String? + public let commentCount: Int64 + public let bookmarkYn: Bool + public let loginYn: Bool + public let hasCommented: Bool + public let mainImageUrl: String? + public let imageList: [GetPopUpDetailImageResponse] + public let commentList: [GetPopUpDetailCommentResponse] + public let similarPopUpStoreList: [GetPopUpDetailSimilarResponse] } public struct GetPopUpDetailImageResponse { @@ -42,8 +42,8 @@ public struct GetPopUpDetailImageResponse { self.imageUrl = imageUrl } - let id: Int64 - let imageUrl: String? + public let id: Int64 + public let imageUrl: String? } public struct GetPopUpDetailCommentResponse { @@ -61,17 +61,17 @@ public struct GetPopUpDetailCommentResponse { self.commentImageList = commentImageList } - let commentId: Int64 - let creator: String? - let nickname: String? - let instagramId: String? - let profileImageUrl: String? - let content: String? - let likeYn: Bool - let likeCount: Int64 - let myCommentYn: Bool - let createDateTime: String? - let commentImageList: [GetPopUpDetailImageResponse] + public let commentId: Int64 + public let creator: String? + public let nickname: String? + public let instagramId: String? + public let profileImageUrl: String? + public let content: String? + public let likeYn: Bool + public let likeCount: Int64 + public let myCommentYn: Bool + public let createDateTime: String? + public let commentImageList: [GetPopUpDetailImageResponse] } public struct GetPopUpDetailSimilarResponse { @@ -82,8 +82,8 @@ public struct GetPopUpDetailSimilarResponse { self.endDate = endDate } - let id: Int64 - let name: String? - let mainImageUrl: String? - let endDate: String? + public let id: Int64 + public let name: String? + public let mainImageUrl: String? + public let endDate: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift index 8aa4daff..60157404 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDirectionResponse.swift @@ -15,15 +15,15 @@ public struct GetPopUpDirectionResponse { self.markerSnippet = markerSnippet } - let id: Int64 - let categoryName: String + public let id: Int64 + public let categoryName: String public let name: String public let address: String - let startDate: String - let endDate: String + public let startDate: String + public let endDate: String public let latitude: Double public let longitude: Double - let markerId: Int64 - let markerTitle: String - let markerSnippet: String + public let markerId: Int64 + public let markerTitle: String + public let markerSnippet: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift index 0d92623d..a1495ca9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift @@ -7,9 +7,9 @@ public struct GetBlockUserListResponse { self.totalElements = totalElements } - var blockedUserInfoList: [GetBlockUserListDataResponse] - var totalPages: Int32 - var totalElements: Int32 + public var blockedUserInfoList: [GetBlockUserListDataResponse] + public var totalPages: Int32 + public var totalElements: Int32 } public struct GetBlockUserListDataResponse { @@ -20,8 +20,8 @@ public struct GetBlockUserListDataResponse { self.instagramId = instagramId } - var userId: String? - var profileImageUrl: String? - var nickname: String? - var instagramId: String? + public var userId: String? + public var profileImageUrl: String? + public var nickname: String? + public var instagramId: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift index 2f35c79a..0f1ecacf 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyCommentResponse.swift @@ -5,7 +5,7 @@ public struct GetMyCommentedPopUpResponse { self.popUpInfoList = popUpInfoList } - var popUpInfoList: [GetMyCommentedPopUpDataResponse] + public var popUpInfoList: [GetMyCommentedPopUpDataResponse] } public struct GetMyCommentedPopUpDataResponse { @@ -20,12 +20,12 @@ public struct GetMyCommentedPopUpDataResponse { self.closedYn = closedYn } - var popUpStoreId: Int64 - var popUpStoreName: String? - var desc: String? - var mainImageUrl: String? - var startDate: String? - var endDate: String? - var address: String? - var closedYn: Bool + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var desc: String? + public var mainImageUrl: String? + public var startDate: String? + public var endDate: String? + public var address: String? + public var closedYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift index 81723476..b7d1dd73 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift @@ -11,13 +11,13 @@ public struct GetMyPageResponse { self.myCommentedPopUpList = myCommentedPopUpList } - var nickname: String? - var profileImageUrl: String? - var intro: String? - var instagramId: String? - var loginYn: Bool - var adminYn: Bool - var myCommentedPopUpList: [GetMyPagePopUpResponse] + public var nickname: String? + public var profileImageUrl: String? + public var intro: String? + public var instagramId: String? + public var loginYn: Bool + public var adminYn: Bool + public var myCommentedPopUpList: [GetMyPagePopUpResponse] } public struct GetMyPagePopUpResponse { @@ -27,7 +27,7 @@ public struct GetMyPagePopUpResponse { self.mainImageUrl = mainImageUrl } - var popUpStoreId: Int64 - var popUpStoreName: String? - var mainImageUrl: String? + public var popUpStoreId: Int64 + public var popUpStoreName: String? + public var mainImageUrl: String? } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index cc8d63ca..636ee8f9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -5,7 +5,7 @@ public struct GetWithdrawlListResponse { self.withDrawlSurveyList = withDrawlSurveyList } - var withDrawlSurveyList: [GetWithdrawlListDataResponse] + public var withDrawlSurveyList: [GetWithdrawlListDataResponse] } public struct GetWithdrawlListDataResponse { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 4f3a1a7b..89ee0c06 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -1,8 +1,11 @@ +import UIKit + +import Infrastructure + import ReactorKit import RxCocoa import RxSwift import SnapKit -import UIKit final class AdminBottomSheetViewController: BaseViewController, View { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 387022a5..6d4523e8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -32,7 +32,7 @@ final class PopUpStoreRegisterViewController: BaseViewController { let reactor = PopUpStoreRegisterReactor( adminUseCase: DIContainer.resolve(AdminUseCase.self), - preSignedUseCase: DIContainer.resolve(PreSignedUseCasae.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self), editingStore: editingStore ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index d8141f79..0707c1ba 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -314,7 +314,8 @@ final class CommentListReactor: Reactor { popUpID: self.popUpID, popUpName: popUpName, comment: comment, - commentAPIUseCase: self.commentAPIUseCase + commentAPIUseCase: self.commentAPIUseCase, + preSignedUseCase: self.preSignedUseCase ) controller?.navigationController?.pushViewController(editController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift index 68c6327b..0c03cf24 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift @@ -1,12 +1,7 @@ -// -// InstaCommentAddController.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import UIKit +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 4335cc05..71a11147 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -161,7 +161,8 @@ final class DetailReactor: Reactor { commentController.reactor = NormalCommentAddReactor( popUpID: popUpID, popUpName: popUpName ?? "", - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(commentController, animated: true) } else { @@ -198,7 +199,8 @@ final class DetailReactor: Reactor { popUpName: popUpName, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) } else { @@ -232,7 +234,8 @@ final class DetailReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: commentAPIUseCase + commentAPIUseCase: commentAPIUseCase, + preSignedUseCase: preSignedUseCase ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): @@ -548,7 +551,8 @@ extension DetailReactor { popUpID: self.popUpID, popUpName: popUpName, comment: comment, - commentAPIUseCase: self.commentAPIUseCase + commentAPIUseCase: self.commentAPIUseCase, + preSignedUseCase: self.preSignedUseCase ) controller?.navigationController?.pushViewController(editController, animated: true) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift index 228abf0a..b27415c1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift @@ -1,5 +1,8 @@ import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -281,7 +284,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -300,7 +304,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -318,7 +323,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 12: @@ -336,7 +342,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: @@ -351,7 +358,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) } @@ -370,7 +378,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) case 7: @@ -388,7 +397,8 @@ final class HomeReactor: Reactor { popUpID: id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) currentController.navigationController?.pushViewController(controller, animated: true) default: diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift index 47912023..df5d00de 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift @@ -7,6 +7,8 @@ import UIKit +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 82393654..b6f8989a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,9 +1,13 @@ import CoreLocation +import UIKit + +import DomainInterface +import Infrastructure + import NMapsMap import RxCocoa import RxSwift import SnapKit -import UIKit class FullScreenMapViewController: MapViewController { // MARK: - Properties diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift index 9f45b079..d583d286 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideViewController.swift @@ -1,9 +1,13 @@ import CoreLocation +import UIKit + +import DomainInterface +import Infrastructure + import NMapsMap import ReactorKit import RxSwift import SnapKit -import UIKit final class MapGuideViewController: UIViewController, View { // MARK: - Properties diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift index d5d5d9b8..dd6d5db1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift @@ -1,4 +1,8 @@ import CoreLocation + +import DomainInterface +import Infrastructure + import ReactorKit import RxSwift @@ -227,9 +231,7 @@ final class MapReactor: Reactor { }, onSubscribe: { } ) - .map { dto in - let response = dto.toDomain() - + .map { response in return MapPopUpStore( id: response.id, category: response.categoryName, diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 243ad3f6..1c275561 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1,4 +1,9 @@ +import UIKit import CoreLocation + +import DomainInterface +import Infrastructure + import FloatingPanel import NMapsMap import ReactorKit @@ -6,7 +11,6 @@ import RxCocoa import RxGesture import RxSwift import SnapKit -import UIKit class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NMFMapViewTouchDelegate, NMFMapViewCameraDelegate, UIGestureRecognizerDelegate { typealias Reactor = MapReactor @@ -126,7 +130,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM popUpID: Int64(store.id), userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), popUpAPIUseCase: self?.popUpAPIUseCase ?? DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) self?.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift index a1c2bffe..ed8ce855 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MarkerTooltipView.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DomainInterface + +import SnapKit + final class MarkerTooltipView: UIView, UIGestureRecognizerDelegate { // MARK: - Properties diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index 5204520c..054506ce 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -1,4 +1,8 @@ import Foundation + +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift index 9ab19b90..92dba9d9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListViewController.swift @@ -1,10 +1,14 @@ +import UIKit + +import DomainInterface +import Infrastructure + import FloatingPanel import ReactorKit import RxCocoa import RxDataSources import RxSwift import SnapKit -import UIKit final class StoreListViewController: UIViewController, View { typealias Reactor = StoreListReactor @@ -120,7 +124,8 @@ final class StoreListViewController: UIViewController, View { popUpID: Int64(store.id), userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) owner.navigationController?.isNavigationBarHidden = false diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index e0439e64..7d8bc987 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -1,12 +1,7 @@ -// -// BlockUserManageReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 56ecea39..fd82c499 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -179,7 +179,8 @@ final class MyPageBookmarkReactor: Reactor { popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .presentModal(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 3150edd4..3fe8c1d9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -1,14 +1,11 @@ -// -// MyPageReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// +import UIKit + +import DomainInterface +import Infrastructure import ReactorKit import RxCocoa import RxSwift -import UIKit final class MyPageReactor: Reactor { @@ -183,7 +180,8 @@ final class MyPageReactor: Reactor { let nextController = ProfileEditController() nextController.reactor = ProfileEditReactor( userAPIUseCase: userAPIUseCase, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) @@ -262,7 +260,8 @@ final class MyPageReactor: Reactor { popUpID: popUpID, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index a41eb076..2b4911db 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -1,12 +1,8 @@ -// -// MyCommentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/8/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -111,7 +107,8 @@ final class MyCommentReactor: Reactor { popUpID: popUpID, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) case .moveToRecentScene(let controller): diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index 95801cc2..9dc41725 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -1,13 +1,8 @@ -// -// ProfileEditController.swift -// Poppool -// -// Created by SeoJunYoung on 1/4/25. -// - import PhotosUI import UIKit +import Infrastructure + import ReactorKit import RxCocoa import RxGesture diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 06318f0a..94b3f651 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -1,12 +1,8 @@ -// -// MyPageRecentReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift @@ -130,7 +126,8 @@ final class MyPageRecentReactor: Reactor { popUpID: listSection.inputDataList[row].id, userAPIUseCase: userAPIUseCase, popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self) + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) ) controller.navigationController?.pushViewController(nextController, animated: true) } 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 4748166b..8cb10ad6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -1,12 +1,8 @@ -// -// WithdrawlReasonReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift index 66554ab7..267f5dcb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift @@ -1,12 +1,7 @@ -// -// SearchCategoryReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift index 91840c9c..7eb6542e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift @@ -1,12 +1,8 @@ -// -// SearchMainController.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DomainInterface +import Infrastructure + import Pageboy import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift index 9e31c30e..e72b9a4c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift @@ -1,12 +1,8 @@ -// -// SignUpMainController.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface +import Infrastructure + import Pageboy import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index adeaa99e..c4f3db3a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -1,9 +1,5 @@ -// -// SignUpMainReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// +import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift index 24f21cfb..3c700b01 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Reactor.swift @@ -1,12 +1,8 @@ -// -// SignUpStep2Reactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface +import Infrastructure + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 142a0124..078e6d52 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -1,12 +1,7 @@ -// -// SignUpStep3Reactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 4aece5de..e28d8ac6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -9,10 +9,6 @@ import RxSwift import SnapKit public final class SplashController: BaseViewController { - - public override init() { } - - required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } // MARK: - Properties var disposeBag = DisposeBag() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift index 4be350da..8bee4e8b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift @@ -1,12 +1,8 @@ -// -// TabbarController.swift -// Poppool -// -// Created by SeoJunYoung on 12/1/24. -// - import UIKit +import DomainInterface +import Infrastructure + class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { private let waveLayer = CAShapeLayer() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift index ad3b265f..23bebd96 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift @@ -1,3 +1,6 @@ +import DomainInterface +import Infrastructure + import NMapsMap class ClusteringManager { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift index 639a66e5..42a777dd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift @@ -1,12 +1,7 @@ -// -// BaseTabmanController.swift -// MomsVillage -// -// Created by SeoJunYoung on 9/25/24. -// - import UIKit +import Infrastructure + import Pageboy import Tabman diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift index 19da325b..03eeded3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift @@ -10,7 +10,7 @@ public class BaseViewController: UIViewController { var systemStatusBarIsDark: BehaviorRelay = .init(value: true) var systemStatusBarDisposeBag = DisposeBag() - init() { + public init() { super.init(nibName: nil, bundle: nil) Logger.log( message: "\(self) init", @@ -20,7 +20,7 @@ public class BaseViewController: UIViewController { ) } - required init?(coder: NSCoder) { + public required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift index 924c7b0e..7c6f29be 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/ToastMaker.swift @@ -1,12 +1,8 @@ -// -// ToastMaker.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DomainInterface +import Infrastructure + import RxCocoa import RxSwift import SnapKit From d845683ded38e3c734b1ec8c4c40d7aa2de07662 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 20:43:54 +0900 Subject: [PATCH 177/393] =?UTF-8?q?remove/#112:=20InstaComment=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InstaCommentAddController.swift | 102 --------- .../InstaComment/InstaCommentAddReactor.swift | 153 ------------- .../View/InstaCommentAddView.swift | 93 -------- .../InstaGuideChildSection.swift | 42 ---- .../InstaGuideChildSectionCell.swift | 103 --------- .../InstaGuideSection/InstaGuideSection.swift | 41 ---- .../InstaGuideSectionCell.swift | 206 ------------------ 7 files changed, 740 deletions(-) delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift deleted file mode 100644 index 0c03cf24..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddController.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import Infrastructure - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit -import SwiftSoup - -final class InstaCommentAddController: BaseViewController, View { - - typealias Reactor = InstaCommentAddReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = InstaCommentAddView() - private var sections: [any Sectionable] = [] -} - -// MARK: - Life Cycle -extension InstaCommentAddController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } -} - -// MARK: - SetUp -private extension InstaCommentAddController { - func setUp() { - if let layout = reactor?.compositionalLayout { - mainView.contentCollectionView.collectionViewLayout = layout - } - mainView.contentCollectionView.delegate = self - mainView.contentCollectionView.dataSource = self - mainView.contentCollectionView.register(InstaGuideSectionCell.self, forCellWithReuseIdentifier: InstaGuideSectionCell.identifiers) - view.backgroundColor = .g50 - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension InstaCommentAddController { - func bind(reactor: Reactor) { - - SceneDelegate.appDidBecomeActive - .subscribe { _ in - if let url = UIPasteboard.general.string { -// guard let url = URL(string: url) else { return } -// self.crawl(url: url) -// self.fetchHTML(url: url) - } else { - print("Clipboard is empty or does not contain text") - } - } - .disposed(by: disposeBag) - - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.instaButton.rx.tap - .map { Reactor.Action.instaButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .withUnretained(self) - .subscribe { (owner, state) in - owner.sections = state.sections - owner.mainView.contentCollectionView.reloadData() - } - .disposed(by: disposeBag) - } -} - -// MARK: - UICollectionViewDelegate, UICollectionViewDataSource -extension InstaCommentAddController: UICollectionViewDelegate, UICollectionViewDataSource { - func numberOfSections(in collectionView: UICollectionView) -> Int { - return sections.count - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return sections[section].dataCount - } - - func collectionView( - _ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - guard let reactor = reactor else { return cell } - - return cell - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift deleted file mode 100644 index c9d0e127..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/InstaCommentAddReactor.swift +++ /dev/null @@ -1,153 +0,0 @@ -// -// InstaCommentAddReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import ReactorKit -import RxCocoa -import RxSwift - -final class InstaCommentAddReactor: Reactor { - - // MARK: - Reactor - enum Action { - case viewWillAppear - case instaButtonTapped - } - - enum Mutation { - case loadView - case moveToInsta - } - - struct State { - var sections: [any Sectionable] = [] - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - lazy var compositionalLayout: UICollectionViewCompositionalLayout = { - UICollectionViewCompositionalLayout { [weak self] section, env in - guard let self = self else { - return NSCollectionLayoutSection(group: NSCollectionLayoutGroup( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .fractionalHeight(1) - )) - ) - } - return getSection()[section].getSection(section: section, env: env) - } - }() - - private let guideSection = InstaGuideSection(inputDataList: [ - .init( - imageList: [ - UIImage(named: "icon_instaGuide_0"), - UIImage(named: "icon_instaGuide_1"), - UIImage(named: "icon_instaGuide_2"), - UIImage(named: "icon_instaGuide_3") - ], - title: [ - { - let title = "아래 인스타그램 열기\n버튼을 터치해 앱 열기" - let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.korFont(style: .bold, size: 20)! - attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) - attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "인스타그램 열기")) - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 1.2 - attributedTitle.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: title.count)) - return attributedTitle - }(), - { - let title = "원하는 피드의 이미지로 이동 후\n공유하기 > 링크복사 터치하기" - let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.korFont(style: .bold, size: 20)! - attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) - attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "공유하기 > 링크복사")) - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 1.2 - attributedTitle.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: title.count)) - return attributedTitle - }(), - { - let title = "아래 이미지 영역을 터치해\n팝풀 앱으로 돌아오기" - let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.korFont(style: .bold, size: 20)! - attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) - attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "팝풀 앱")) - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 1.2 - attributedTitle.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: title.count)) - return attributedTitle - }(), - { - let title = "복사된 인스타 피드 이미지와\n함께할 글을 입력 후 등록하기" - let attributedTitle = NSMutableAttributedString(string: title) - let koreanFont = UIFont.korFont(style: .bold, size: 20)! - attributedTitle.addAttribute(.font, value: koreanFont, range: NSRange(location: 0, length: title.count)) - attributedTitle.addAttribute(.foregroundColor, value: UIColor.blu500.cgColor, range: (title as NSString).range(of: "글을 입력 후 등록")) - let paragraphStyle = NSMutableParagraphStyle() - paragraphStyle.lineHeightMultiple = 1.2 - attributedTitle.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: title.count)) - return attributedTitle - }() - ] - ) - ]) - - // MARK: - init - init() { - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - return Observable.just(.loadView) - case .instaButtonTapped: - return Observable.just(.moveToInsta) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .loadView: - newState.sections = getSection() - case .moveToInsta: - openInstagram() - - } - return newState - } - - func getSection() -> [any Sectionable] { - return [ - guideSection - ] - } - - func openInstagram() { - // Instagram 앱의 URL Scheme - let instagramURL = URL(string: "instagram://app")! - - if UIApplication.shared.canOpenURL(instagramURL) { - // Instagram 앱 열기 - UIApplication.shared.open(instagramURL, options: [:], completionHandler: nil) - } else { - // Instagram 앱이 설치되지 않은 경우 - let appStoreURL = URL(string: "https://apps.apple.com/app/instagram/id389801252")! - UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift deleted file mode 100644 index c7c9faa0..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaCommentAddView.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// InstaCommentAddView.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import SnapKit - -final class InstaCommentAddView: UIView { - - // MARK: - Components - let headerView: PPReturnHeaderView = { - let view = PPReturnHeaderView() - view.headerLabel.setLineHeightText(text: "코멘트 작성하기", font: .korFont(style: .regular, size: 15)) - return view - }() - - let instaButton: UIButton = { - let button = UIButton() - button.setTitleColor(.white, for: .normal) - button.backgroundColor = .g900 - button.layer.cornerRadius = 4 - - let title = "Instagram 열기" - let attributedTitle = NSMutableAttributedString(string: title) - - let englishFont = UIFont.engFont(style: .medium, size: 15)! - attributedTitle.addAttribute(.font, value: englishFont, range: (title as NSString).range(of: "Instagram")) - - let koreanFont = UIFont.korFont(style: .medium, size: 15)! - attributedTitle.addAttribute(.font, value: koreanFont, range: (title as NSString).range(of: "열기")) - - button.setAttributedTitle(attributedTitle, for: .normal) - - return button - }() - - private let instaImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_instagram") - return view - }() - - let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - view.backgroundColor = .g50 - return view - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension InstaCommentAddView { - - func setUpConstraints() { - self.addSubview(headerView) - headerView.snp.makeConstraints { make in - make.top.leading.trailing.equalToSuperview() - } - - self.addSubview(instaButton) - instaButton.snp.makeConstraints { make in - make.leading.trailing.bottom.equalToSuperview().inset(20) - make.height.equalTo(52) - } - - instaButton.addSubview(instaImageView) - instaImageView.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.centerY.equalToSuperview() - make.size.equalTo(22) - } - - self.addSubview(contentCollectionView) - contentCollectionView.snp.makeConstraints { make in - make.top.equalTo(headerView.snp.bottom) - make.leading.trailing.equalToSuperview() - make.bottom.equalTo(instaButton.snp.top) - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift deleted file mode 100644 index cb88aa60..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSection.swift +++ /dev/null @@ -1,42 +0,0 @@ -// -// InstaGuideChildSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import RxSwift - -struct InstaGuideChildSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = InstaGuideChildSectionCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .estimated(500) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .estimated(500) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - - // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .paging - return section - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift deleted file mode 100644 index 5a4bff39..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideChildSection/InstaGuideChildSectionCell.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// InstaGuideChildSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import RxSwift -import SnapKit - -final class InstaGuideChildSectionCell: UICollectionViewCell { - - // MARK: - Components - - let disposeBag = DisposeBag() - - private let indexTrailgView: UIView = { - let view = UIView() - view.backgroundColor = .g900 - view.clipsToBounds = true - view.layer.cornerRadius = 4 - return view - }() - - private let indexLabel: UILabel = { - let label = UILabel() - label.font = .engFont(style: .medium, size: 16) - label.textColor = .w100 - label.textAlignment = .center - return label - }() - - private let titleLabel: UILabel = { - let label = UILabel() - label.numberOfLines = 2 - return label - }() - - private let imageView: UIImageView = { - let view = UIImageView() - view.layer.cornerRadius = 8 - view.clipsToBounds = true - return view - }() - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } -} - -// MARK: - SetUp -private extension InstaGuideChildSectionCell { - func setUpConstraints() { - contentView.addSubview(indexTrailgView) - indexTrailgView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(36) - make.leading.equalToSuperview().inset(20) - make.width.equalTo(40) - make.height.equalTo(33) - } - - indexTrailgView.addSubview(indexLabel) - indexLabel.snp.makeConstraints { make in - make.center.equalToSuperview() - } - - contentView.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview().inset(20) - make.top.equalTo(indexTrailgView.snp.bottom).offset(16) - } - contentView.addSubview(imageView) - imageView.snp.makeConstraints { make in - make.size.equalTo(335) - make.centerX.equalToSuperview() - make.top.equalTo(titleLabel.snp.bottom).offset(28) - make.bottom.equalToSuperview() - } - } -} - -extension InstaGuideChildSectionCell: Inputable { - struct Input { - var image: UIImage? - var title: NSMutableAttributedString? - var index: Int - } - - func injection(with input: Input) { - indexLabel.text = "#\(input.index + 1)" - titleLabel.attributedText = input.title - imageView.image = input.image - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift deleted file mode 100644 index 13bfca92..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSection.swift +++ /dev/null @@ -1,41 +0,0 @@ -// -// InstaGuideSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import RxSwift - -struct InstaGuideSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = InstaGuideSectionCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .estimated(600) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(600) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - - // 섹션 생성 - - return NSCollectionLayoutSection(group: group) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift deleted file mode 100644 index d3c3371e..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/InstaComment/View/InstaGuideSection/InstaGuideSection/InstaGuideSectionCell.swift +++ /dev/null @@ -1,206 +0,0 @@ -// -// InstaGuideSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - -import UIKit - -import RxSwift -import SnapKit - -final class InstaGuideSectionCell: UICollectionViewCell { - - // MARK: - Components - - let disposeBag = DisposeBag() - - private var autoScrollTimer: Timer? - - private lazy var contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: compositionalLayout) - view.isScrollEnabled = false - view.backgroundColor = .g50 - return view - }() - - var pageControl: UIPageControl = { - let controller = UIPageControl() - controller.currentPage = 0 - controller.preferredIndicatorImage = UIImage(systemName: "circle") - controller.preferredCurrentPageIndicatorImage = UIImage(systemName: "circle.fill") - controller.pageIndicatorTintColor = .pb30 - controller.currentPageIndicatorTintColor = .g600 - controller.isUserInteractionEnabled = false - controller.transform = CGAffineTransform(scaleX: 0.8, y: 0.8) - return controller - }() - - let stopButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "icon_banner_stopButton_gray"), for: .normal) - return button - }() - - private var isAutoBannerPlay: Bool = false - - private var imageSection = InstaGuideChildSection(inputDataList: []) - - lazy var compositionalLayout: UICollectionViewCompositionalLayout = { - UICollectionViewCompositionalLayout { [weak self] section, env in - guard let self = self else { - return NSCollectionLayoutSection(group: NSCollectionLayoutGroup( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .fractionalHeight(1) - )) - ) - } - return getSection()[section].getSection(section: section, env: env) - } - }() - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUp() - setUpConstraints() - bind() - } - - required init?(coder: NSCoder) { - fatalError() - } - - override func prepareForReuse() { - super.prepareForReuse() - stopAutoScroll() - } - - deinit { - stopAutoScroll() - } -} - -// MARK: - SetUp -private extension InstaGuideSectionCell { - func setUp() { - contentCollectionView.delegate = self - contentCollectionView.dataSource = self - - contentCollectionView.register( - InstaGuideChildSectionCell.self, - forCellWithReuseIdentifier: InstaGuideChildSectionCell.identifiers - ) - imageSection.currentPage - .withUnretained(self) - .subscribe { (owner, page) in - owner.pageControl.currentPage = page - } - .disposed(by: disposeBag) - } - - func setUpConstraints() { - contentView.addSubview(contentCollectionView) - contentCollectionView.snp.makeConstraints { make in - make.top.leading.trailing.equalToSuperview() - make.height.equalTo(504) - } - - contentView.addSubview(pageControl) - pageControl.snp.makeConstraints { make in - make.top.equalTo(contentCollectionView.snp.bottom) - make.centerX.equalToSuperview() - make.bottom.equalToSuperview() - } - - contentView.addSubview(stopButton) - stopButton.snp.makeConstraints { make in - make.size.equalTo(8) - make.centerY.equalTo(pageControl.snp.centerY) - make.leading.equalTo(pageControl.snp.trailing).offset(-36) - } - } - - func getSection() -> [any Sectionable] { - return [imageSection] - } - - func startAutoScroll(interval: TimeInterval = 3.0) { - stopAutoScroll() // 기존 타이머를 중지 - isAutoBannerPlay = true - autoScrollTimer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) { [weak self] _ in - self?.scrollToNextItem() - } - } - - // 자동 스크롤 중지 함수 - func stopAutoScroll() { - isAutoBannerPlay = false - autoScrollTimer?.invalidate() - autoScrollTimer = nil - } - - // 다음 배너로 스크롤 - private func scrollToNextItem() { - - let visibleIndexPaths = contentCollectionView.indexPathsForVisibleItems.sorted() - guard let currentIndex = visibleIndexPaths.first else { return } - - let nextIndex = IndexPath( - item: (currentIndex.item + 1) % imageSection.dataCount, - section: currentIndex.section - ) - - contentCollectionView.scrollToItem(at: nextIndex, at: .centeredHorizontally, animated: true) - pageControl.currentPage = nextIndex.item - } - - func bind() { - stopButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - if owner.isAutoBannerPlay { - owner.stopAutoScroll() - } else { - owner.startAutoScroll() - } - } - .disposed(by: disposeBag) - } -} - -extension InstaGuideSectionCell: Inputable { - struct Input { - var imageList: [UIImage?] - var title: [NSMutableAttributedString?] - } - - func injection(with input: Input) { - pageControl.numberOfPages = input.imageList.count - let datas = zip(input.imageList, input.title).enumerated().map { $0 } - imageSection.inputDataList = datas.map { .init(image: $0.element.0, title: $0.element.1, index: $0.offset)} - contentCollectionView.reloadData() - startAutoScroll() - } -} - -// MARK: - UICollectionViewDelegate, UICollectionViewDataSource -extension InstaGuideSectionCell: UICollectionViewDelegate, UICollectionViewDataSource { - - func numberOfSections(in collectionView: UICollectionView) -> Int { - return getSection().count - } - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return getSection()[section].dataCount - } - - func collectionView( - _ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - return getSection()[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - } -} From ecc17d9010d0c3e5d73861d7b8fa13b0965e9c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 21 Apr 2025 21:02:17 +0900 Subject: [PATCH 178/393] =?UTF-8?q?refactor/#112=20:=20authorizationCode?= =?UTF-8?q?=20=EC=A0=91=EA=B7=BC=EB=A0=88=EB=B2=A8=20Mutation=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Entity/AuthResponse/AuthServiceResponse.swift | 8 ++++---- .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift index e6aa5fa0..5150eb84 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift @@ -8,8 +8,8 @@ public struct AuthServiceResponse: Encodable { self.kakaoAccessToken = kakaoAccessToken } - var idToken: String? - var authorizationCode: String? - var kakaoUserId: Int64? - var kakaoAccessToken: String? + public var idToken: String? + public var authorizationCode: String? + public var kakaoUserId: Int64? + public var kakaoAccessToken: String? } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index d257b177..216e4257 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -124,7 +124,8 @@ final class SubLoginReactor: Reactor { return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) - .map { [weak controller] (owner, loginResponse) in + + .map { [weak controller] (owner:SubLoginReactor, loginResponse:LoginResponse) -> Mutation in guard let controller = controller else { return .loadView } owner.userDefaultService.save(key: "userID", value: loginResponse.userId) owner.userDefaultService.save(key: "socialType", value: loginResponse.socialType) From b145fd23a32a6584e2cd9cede0ca88315650d791 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 21 Apr 2025 21:07:22 +0900 Subject: [PATCH 179/393] =?UTF-8?q?refactor/#112:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=20=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 33 +++-- .../Domain/Domain.xcodeproj/project.pbxproj | 32 +++-- .../AuthResponse/AuthServiceResponse.swift | 8 +- .../Presentation.xcodeproj/project.pbxproj | 119 ++++++++++++++++++ .../NormalCommentEditReactor.swift | 13 +- 5 files changed, 178 insertions(+), 27 deletions(-) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 2398ebd8..de8fcec0 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -11,7 +11,8 @@ 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC93C52DB62B0000771CB3 /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */; }; + 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3512DB66AFB00E57EFA /* RxSwift */; }; + 08B2A3552DB66B1D00E57EFA /* AlamofireDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -135,7 +136,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05EC93C52DB62B0000771CB3 /* RxSwift-Dynamic in Frameworks */, + 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */, + 08B2A3552DB66B1D00E57EFA /* AlamofireDynamic in Frameworks */, 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, ); @@ -199,7 +201,8 @@ ); name = Data; packageProductDependencies = ( - 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */, + 08B2A3512DB66AFB00E57EFA /* RxSwift */, + 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */, ); productName = Data; productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; @@ -230,7 +233,8 @@ mainGroup = 058CC8D22DB5376A0084221A; minimizedProjectReferenceProxies = 1; packageReferences = ( - 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */, + 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */, + 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC8DD2DB5376A0084221A /* Products */; @@ -478,7 +482,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { @@ -486,13 +490,26 @@ minimumVersion = 6.9.0; }; }; + 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/Alamofire/Alamofire.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.10.2; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05EC93C42DB62B0000771CB3 /* RxSwift-Dynamic */ = { + 08B2A3512DB66AFB00E57EFA /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */ = { isa = XCSwiftPackageProductDependency; - package = 05EC93C32DB62B0000771CB3 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; + package = 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = AlamofireDynamic; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index d26d7875..c5dc782d 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -13,8 +13,8 @@ 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; }; 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05C1D82A2DB53CC200508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */; }; - 05C1D82C2DB53CD100508FFD /* RxSwift-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */; }; + 08B29EE62DB6696700E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B29EE52DB6696700E57EFA /* RxSwift */; }; + 08B2A34F2DB66AD300E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A34E2DB66AD300E57EFA /* RxSwift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -78,8 +78,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 08B2A34F2DB66AD300E57EFA /* RxSwift in Frameworks */, 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */, - 05C1D82A2DB53CC200508FFD /* RxSwift-Dynamic in Frameworks */, 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -88,7 +88,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05C1D82C2DB53CD100508FFD /* RxSwift-Dynamic in Frameworks */, + 08B29EE62DB6696700E57EFA /* RxSwift in Frameworks */, 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -164,7 +164,7 @@ ); name = Domain; packageProductDependencies = ( - 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */, + 08B2A34E2DB66AD300E57EFA /* RxSwift */, ); productName = Domain; productReference = 058CC8F02DB5377F0084221A /* Domain.framework */; @@ -189,7 +189,7 @@ ); name = DomainInterface; packageProductDependencies = ( - 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */, + 08B29EE52DB6696700E57EFA /* RxSwift */, ); productName = DomainInterface; productReference = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; @@ -498,11 +498,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.DomainInterface; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -532,11 +536,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.DomainInterface; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; @@ -584,15 +592,15 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05C1D8292DB53CC200508FFD /* RxSwift-Dynamic */ = { + 08B29EE52DB6696700E57EFA /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; + productName = RxSwift; }; - 05C1D82B2DB53CD100508FFD /* RxSwift-Dynamic */ = { + 08B2A34E2DB66AD300E57EFA /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = "RxSwift-Dynamic"; + productName = RxSwift; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift index e6aa5fa0..5150eb84 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/AuthServiceResponse.swift @@ -8,8 +8,8 @@ public struct AuthServiceResponse: Encodable { self.kakaoAccessToken = kakaoAccessToken } - var idToken: String? - var authorizationCode: String? - var kakaoUserId: Int64? - var kakaoAccessToken: String? + public var idToken: String? + public var authorizationCode: String? + public var kakaoUserId: Int64? + public var kakaoAccessToken: String? } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 3c4595ff..af4dc626 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -18,6 +18,13 @@ 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D22DB6536200771CB3 /* RxCocoa */; }; 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D42DB6536200771CB3 /* RxSwift */; }; + 08B2A3582DB66B4100E57EFA /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3572DB66B4100E57EFA /* RxKeyboard */; }; + 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */; }; + 08B2A35E2DB66B8600E57EFA /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A35D2DB66B8600E57EFA /* RxDataSources */; }; + 08B2A3612DB66BAB00E57EFA /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3602DB66BAB00E57EFA /* Then */; }; + 08B2A3642DB66BBC00E57EFA /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3632DB66BBC00E57EFA /* Tabman */; }; + 08B2A3672DB66BD400E57EFA /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3662DB66BD400E57EFA /* Pageboy */; }; + 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3692DB66BF200E57EFA /* RxGesture */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -54,15 +61,22 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 08B2A3672DB66BD400E57EFA /* Pageboy in Frameworks */, 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */, + 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */, 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, + 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */, + 08B2A3612DB66BAB00E57EFA /* Then in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */, + 08B2A3642DB66BBC00E57EFA /* Tabman in Frameworks */, 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */, + 08B2A35E2DB66B8600E57EFA /* RxDataSources in Frameworks */, 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */, + 08B2A3582DB66B4100E57EFA /* RxKeyboard in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -134,6 +148,13 @@ 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, 05EC93D22DB6536200771CB3 /* RxCocoa */, 05EC93D42DB6536200771CB3 /* RxSwift */, + 08B2A3572DB66B4100E57EFA /* RxKeyboard */, + 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */, + 08B2A35D2DB66B8600E57EFA /* RxDataSources */, + 08B2A3602DB66BAB00E57EFA /* Then */, + 08B2A3632DB66BBC00E57EFA /* Tabman */, + 08B2A3662DB66BD400E57EFA /* Pageboy */, + 08B2A3692DB66BF200E57EFA /* RxGesture */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -169,6 +190,13 @@ 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */, 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */, 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, + 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */, + 08B2A3592DB66B5A00E57EFA /* XCRemoteSwiftPackageReference "FloatingPanel" */, + 08B2A35C2DB66B8600E57EFA /* XCRemoteSwiftPackageReference "RxDataSources" */, + 08B2A35F2DB66BAB00E57EFA /* XCRemoteSwiftPackageReference "Then" */, + 08B2A3622DB66BBC00E57EFA /* XCRemoteSwiftPackageReference "Tabman" */, + 08B2A3652DB66BD400E57EFA /* XCRemoteSwiftPackageReference "Pageboy" */, + 08B2A3682DB66BF200E57EFA /* XCRemoteSwiftPackageReference "RxGesture" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9052DB537960084221A /* Products */; @@ -456,6 +484,62 @@ minimumVersion = 2.24.0; }; }; + 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.0.1; + }; + }; + 08B2A3592DB66B5A00E57EFA /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/scenee/FloatingPanel"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.8.7; + }; + }; + 08B2A35C2DB66B8600E57EFA /* XCRemoteSwiftPackageReference "RxDataSources" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.2; + }; + }; + 08B2A35F2DB66BAB00E57EFA /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; + 08B2A3622DB66BBC00E57EFA /* XCRemoteSwiftPackageReference "Tabman" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Tabman"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 08B2A3652DB66BD400E57EFA /* XCRemoteSwiftPackageReference "Pageboy" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Pageboy"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.2.0; + }; + }; + 08B2A3682DB66BF200E57EFA /* XCRemoteSwiftPackageReference "RxGesture" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.0.4; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -494,6 +578,41 @@ package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; + 08B2A3572DB66B4100E57EFA /* RxKeyboard */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */; + productName = RxKeyboard; + }; + 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3592DB66B5A00E57EFA /* XCRemoteSwiftPackageReference "FloatingPanel" */; + productName = FloatingPanel; + }; + 08B2A35D2DB66B8600E57EFA /* RxDataSources */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A35C2DB66B8600E57EFA /* XCRemoteSwiftPackageReference "RxDataSources" */; + productName = RxDataSources; + }; + 08B2A3602DB66BAB00E57EFA /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A35F2DB66BAB00E57EFA /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + 08B2A3632DB66BBC00E57EFA /* Tabman */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3622DB66BBC00E57EFA /* XCRemoteSwiftPackageReference "Tabman" */; + productName = Tabman; + }; + 08B2A3662DB66BD400E57EFA /* Pageboy */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3652DB66BD400E57EFA /* XCRemoteSwiftPackageReference "Pageboy" */; + productName = Pageboy; + }; + 08B2A3692DB66BF200E57EFA /* RxGesture */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3682DB66BF200E57EFA /* XCRemoteSwiftPackageReference "RxGesture" */; + productName = RxGesture; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8FB2DB537960084221A /* Project object */; diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 3d5182b7..788444f9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -35,6 +35,13 @@ final class NormalCommentEditReactor: Reactor { var isReloadView: Bool = true var isSaving: Bool = false } + + struct PutCommentImageData { + var imageId: Int64? + var imageUrl: String? + var actionType: String? + } + // MARK: - properties @@ -167,9 +174,9 @@ final class NormalCommentEditReactor: Reactor { } } - var convertAddImages: [PutCommentImageDataRequestDTO] = addImages.map { .init(imageId: nil, imageUrl: pathList[$0.offset], actionType: "ADD")} - var convertKeepImages: [PutCommentImageDataRequestDTO] = keepImages.map { .init(imageId: nil, imageUrl: $0, actionType: "KEEP")} - var convertDeleteImages: [PutCommentImageDataRequestDTO] = deleteImages.map { .init(imageId: $0.1, imageUrl: $0.0, actionType: "DELETE")} + var convertAddImages: [PutCommentImageData] = addImages.map { .init(imageId: nil, imageUrl: pathList[$0.offset], actionType: "ADD")} + var convertKeepImages: [PutCommentImageData] = keepImages.map { .init(imageId: nil, imageUrl: $0, actionType: "KEEP")} + var convertDeleteImages: [PutCommentImageData] = deleteImages.map { .init(imageId: $0.1, imageUrl: $0.0, actionType: "DELETE")} if !addImages.isEmpty { preSignedUseCase.tryUpload(presignedURLRequest: addImages.map { From b96570ff9bad4e121410a24c862ef2fdce6fec19 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Mon, 21 Apr 2025 21:11:59 +0900 Subject: [PATCH 180/393] =?UTF-8?q?fix/#112:=20=EC=9B=90=ED=98=95=EB=B3=B5?= =?UTF-8?q?=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 216e4257..d257b177 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -124,8 +124,7 @@ final class SubLoginReactor: Reactor { return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "apple") } .withUnretained(self) - - .map { [weak controller] (owner:SubLoginReactor, loginResponse:LoginResponse) -> Mutation in + .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) From bf633966915aa315dc46e60fb07ff70e69e8363e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 21:21:55 +0900 Subject: [PATCH 181/393] =?UTF-8?q?fix/#112:=20DIContainer=20=EC=8A=A4?= =?UTF-8?q?=EB=A0=88=EB=93=9C=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/DIContainer/DIContainer.swift | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift index 0ad4234e..66b3e1ef 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DIContainer.swift @@ -57,12 +57,10 @@ public final class DIContainer { private func resolve(_ type: T.Type) -> T { let key = ObjectIdentifier(type) - return resolveQueue.sync { - guard let registration = registrations[key], - let instance = registration() as? T - else { fatalError("\(type) does not registered") } + guard let registration = registrations[key], + let instance = registration() as? T + else { fatalError("\(type) does not registered") } - return instance - } + return instance } } From 609682680f7e6de5c6259e667fe72a62cf88464c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 21:22:59 +0900 Subject: [PATCH 182/393] =?UTF-8?q?chore/#112:=20Static=20library=EB=A5=BC?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20=ED=95=98?= =?UTF-8?q?=EC=97=AC=20@rpath=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 16 ++++++++-------- Poppool/Poppool.xcodeproj/project.pbxproj | 6 ++++++ .../Presentation.xcodeproj/project.pbxproj | 16 ++++++++-------- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index de8fcec0..464d09d8 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -7,12 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3DB2DB66EB500C1E192 /* Alamofire */; }; 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; }; 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3512DB66AFB00E57EFA /* RxSwift */; }; - 08B2A3552DB66B1D00E57EFA /* AlamofireDynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -137,7 +137,7 @@ buildActionMask = 2147483647; files = ( 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */, - 08B2A3552DB66B1D00E57EFA /* AlamofireDynamic in Frameworks */, + 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */, 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, ); @@ -202,7 +202,7 @@ name = Data; packageProductDependencies = ( 08B2A3512DB66AFB00E57EFA /* RxSwift */, - 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */, + 05BDD3DB2DB66EB500C1E192 /* Alamofire */, ); productName = Data; productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; @@ -501,16 +501,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 05BDD3DB2DB66EB500C1E192 /* Alamofire */ = { + isa = XCSwiftPackageProductDependency; + package = 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */; + productName = Alamofire; + }; 08B2A3512DB66AFB00E57EFA /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; - 08B2A3542DB66B1D00E57EFA /* AlamofireDynamic */ = { - isa = XCSwiftPackageProductDependency; - package = 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */; - productName = AlamofireDynamic; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8D32DB5376A0084221A /* Project object */; diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 4c32e118..5c009e1c 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; + 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; }; @@ -44,6 +46,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */, 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */, 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */, 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */, @@ -56,6 +59,7 @@ /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 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; }; 05C1D6092DB53A4900508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -99,6 +103,7 @@ BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, + 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */, 082197A12D426DCB0054094A /* Then in Frameworks */, 05EC93D72DB6599500771CB3 /* RxCocoa in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, @@ -127,6 +132,7 @@ 05C1D6062DB53A4900508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */, 05C1D6072DB53A4900508FFD /* Data.framework */, 05C1D6082DB53A4900508FFD /* Domain.framework */, 05C1D6092DB53A4900508FFD /* Infrastructure.framework */, diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index af4dc626..940425d2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -8,8 +8,8 @@ /* Begin PBXBuildFile section */ 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; - 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */; }; 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; + 05BDD3DA2DB66E6400C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3D92DB66E6400C1E192 /* SnapKit */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; @@ -62,9 +62,9 @@ buildActionMask = 2147483647; files = ( 08B2A3672DB66BD400E57EFA /* Pageboy in Frameworks */, + 05BDD3DA2DB66E6400C1E192 /* SnapKit in Frameworks */, 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */, 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */, - 05125B9B2DB626ED001342A2 /* SnapKit-Dynamic in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */, @@ -142,7 +142,6 @@ name = Presentation; packageProductDependencies = ( 05125B972DB626E3001342A2 /* ReactorKit */, - 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */, 05125BA02DB6275C001342A2 /* PanModal */, 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */, 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, @@ -155,6 +154,7 @@ 08B2A3632DB66BBC00E57EFA /* Tabman */, 08B2A3662DB66BD400E57EFA /* Pageboy */, 08B2A3692DB66BF200E57EFA /* RxGesture */, + 05BDD3D92DB66E6400C1E192 /* SnapKit */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -548,16 +548,16 @@ package = 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */; productName = ReactorKit; }; - 05125B9A2DB626ED001342A2 /* SnapKit-Dynamic */ = { - isa = XCSwiftPackageProductDependency; - package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = "SnapKit-Dynamic"; - }; 05125BA02DB6275C001342A2 /* PanModal */ = { isa = XCSwiftPackageProductDependency; package = 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */; productName = PanModal; }; + 05BDD3D92DB66E6400C1E192 /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */ = { isa = XCSwiftPackageProductDependency; package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; From 8e5772026cc49ec20130ee856ce6c443ffdb23f9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 21:42:24 +0900 Subject: [PATCH 183/393] =?UTF-8?q?chore/#112:=20=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=9E=84=EC=9B=8C=ED=81=AC=20do=20not=20embed=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 18 ----------- .../Domain/Domain.xcodeproj/project.pbxproj | 31 ------------------- .../Presentation.xcodeproj/project.pbxproj | 8 ++--- 3 files changed, 2 insertions(+), 55 deletions(-) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 464d09d8..86ed61dc 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -9,27 +9,10 @@ /* Begin PBXBuildFile section */ 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3DB2DB66EB500C1E192 /* Alamofire */; }; 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; }; - 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; - 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3512DB66AFB00E57EFA /* RxSwift */; }; /* End PBXBuildFile section */ -/* Begin PBXCopyFilesBuildPhase section */ - 05C1D61B2DB53A5600508FFD /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05C1D6182DB53A5600508FFD /* DomainInterface.framework in Embed Frameworks */, - 05C1D61A2DB53A5600508FFD /* Infrastructure.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 058CC8DC2DB5376A0084221A /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D6152DB53A5600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -193,7 +176,6 @@ 058CC8D82DB5376A0084221A /* Sources */, 058CC8D92DB5376A0084221A /* Frameworks */, 058CC8DA2DB5376A0084221A /* Resources */, - 05C1D61B2DB53A5600508FFD /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index c5dc782d..e238744c 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -8,11 +8,8 @@ /* Begin PBXBuildFile section */ 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; }; - 05C1D61F2DB53A6700508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; }; - 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; }; - 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 08B29EE62DB6696700E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B29EE52DB6696700E57EFA /* RxSwift */; }; 08B2A34F2DB66AD300E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A34E2DB66AD300E57EFA /* RxSwift */; }; /* End PBXBuildFile section */ @@ -27,32 +24,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 05C1D6242DB53A6700508FFD /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05C1D61F2DB53A6700508FFD /* DomainInterface.framework in Embed Frameworks */, - 05C1D6232DB53A6700508FFD /* Infrastructure.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 05C1D6282DB53A6E00508FFD /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05C1D6272DB53A6E00508FFD /* Infrastructure.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 058CC8F02DB5377F0084221A /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -152,7 +123,6 @@ 058CC8EC2DB5377F0084221A /* Sources */, 058CC8ED2DB5377F0084221A /* Frameworks */, 058CC8EE2DB5377F0084221A /* Resources */, - 05C1D6242DB53A6700508FFD /* Embed Frameworks */, ); buildRules = ( ); @@ -178,7 +148,6 @@ 05C1D0D72DB538A600508FFD /* Sources */, 05C1D0D82DB538A600508FFD /* Frameworks */, 05C1D0D92DB538A600508FFD /* Resources */, - 05C1D6282DB53A6E00508FFD /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 940425d2..961cac34 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -11,9 +11,7 @@ 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; 05BDD3DA2DB66E6400C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3D92DB66E6400C1E192 /* SnapKit */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; - 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; - 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */; }; 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D22DB6536200771CB3 /* RxCocoa */; }; @@ -28,14 +26,12 @@ /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 05C1D6302DB53A8200508FFD /* Embed Frameworks */ = { + 05BDD5C22DB6744000C1E192 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 05C1D62D2DB53A8200508FFD /* DomainInterface.framework in Embed Frameworks */, - 05C1D62F2DB53A8200508FFD /* Infrastructure.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -130,7 +126,7 @@ 058CC9002DB537960084221A /* Sources */, 058CC9012DB537960084221A /* Frameworks */, 058CC9022DB537960084221A /* Resources */, - 05C1D6302DB53A8200508FFD /* Embed Frameworks */, + 05BDD5C22DB6744000C1E192 /* Embed Frameworks */, ); buildRules = ( ); From e5bbed391f98a95d705d5e63482f5c2a3a8c5d61 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 21 Apr 2025 22:21:25 +0900 Subject: [PATCH 184/393] =?UTF-8?q?chore/#112:=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EA=B0=84=EC=86=8C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 20 +- .../Data/Data.xcodeproj/project.pbxproj | 20 +- .../Domain/Domain.xcodeproj/project.pbxproj | 41 ++- Poppool/Poppool.xcodeproj/project.pbxproj | 258 ++---------------- .../xcshareddata/swiftpm/Package.resolved | 17 +- Poppool/Poppool/Application/AppDelegate.swift | 1 - .../Poppool/Application/SceneDelegate.swift | 1 - .../Presentation.xcodeproj/project.pbxproj | 41 ++- 8 files changed, 113 insertions(+), 286 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index 70ccf6fe..d5ab556c 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D62DB67B4F00B141FF /* RxSwift */; }; 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; - 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DA2DB6605100771CB3 /* RxSwift */; }; 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; /* End PBXBuildFile section */ @@ -42,7 +42,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05EC93DB2DB6605100771CB3 /* RxSwift in Frameworks */, + 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */, 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, ); @@ -108,8 +108,8 @@ name = Infrastructure; packageProductDependencies = ( 05EC93D82DB6605100771CB3 /* RxCocoa */, - 05EC93DA2DB6605100771CB3 /* RxSwift */, 05EC93DD2DB6612100771CB3 /* RxGesture */, + 0522C1D62DB67B4F00B141FF /* RxSwift */, ); productName = Infrastructure; productReference = 058CC9182DB5383C0084221A /* Infrastructure.framework */; @@ -303,8 +303,10 @@ 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"; @@ -323,6 +325,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Infrastructure; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -340,8 +343,10 @@ 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"; @@ -360,6 +365,7 @@ MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Infrastructure; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -416,15 +422,15 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05EC93D82DB6605100771CB3 /* RxCocoa */ = { + 0522C1D62DB67B4F00B141FF /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; + productName = RxSwift; }; - 05EC93DA2DB6605100771CB3 /* RxSwift */ = { + 05EC93D82DB6605100771CB3 /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; + productName = RxCocoa; }; 05EC93DD2DB6612100771CB3 /* RxGesture */ = { isa = XCSwiftPackageProductDependency; diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 86ed61dc..84c04464 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -7,10 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 0522C1DB2DB67C6100B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1DA2DB67C6100B141FF /* RxSwift */; }; 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3DB2DB66EB500C1E192 /* Alamofire */; }; 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; - 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3512DB66AFB00E57EFA /* RxSwift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -119,9 +119,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 08B2A3522DB66AFB00E57EFA /* RxSwift in Frameworks */, 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */, 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, + 0522C1DB2DB67C6100B141FF /* RxSwift in Frameworks */, 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -183,8 +183,8 @@ ); name = Data; packageProductDependencies = ( - 08B2A3512DB66AFB00E57EFA /* RxSwift */, 05BDD3DB2DB66EB500C1E192 /* Alamofire */, + 0522C1DA2DB67C6100B141FF /* RxSwift */, ); productName = Data; productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; @@ -215,8 +215,8 @@ mainGroup = 058CC8D22DB5376A0084221A; minimizedProjectReferenceProxies = 1; packageReferences = ( - 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */, 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */, + 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC8DD2DB5376A0084221A /* Products */; @@ -464,7 +464,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */ = { + 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { @@ -483,16 +483,16 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 0522C1DA2DB67C6100B141FF /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; 05BDD3DB2DB66EB500C1E192 /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */; productName = Alamofire; }; - 08B2A3512DB66AFB00E57EFA /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 08B2A3502DB66AFB00E57EFA /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC8D32DB5376A0084221A /* Project object */; diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index e238744c..94cc85b7 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -7,11 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 0522C1DD2DB67C6E00B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1DC2DB67C6E00B141FF /* RxSwift */; }; + 0522C1DF2DB67C7700B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1DE2DB67C7700B141FF /* RxSwift */; }; 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; }; 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D61D2DB53A6700508FFD /* Infrastructure.framework */; }; 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6252DB53A6E00508FFD /* Infrastructure.framework */; }; - 08B29EE62DB6696700E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B29EE52DB6696700E57EFA /* RxSwift */; }; - 08B2A34F2DB66AD300E57EFA /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A34E2DB66AD300E57EFA /* RxSwift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -24,6 +24,29 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 05BDD5E02DB678D100C1E192 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 05BDD5E42DB678DC00C1E192 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 058CC8F02DB5377F0084221A /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -49,7 +72,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 08B2A34F2DB66AD300E57EFA /* RxSwift in Frameworks */, + 0522C1DD2DB67C6E00B141FF /* RxSwift in Frameworks */, 05C1D61E2DB53A6700508FFD /* DomainInterface.framework in Frameworks */, 05C1D6222DB53A6700508FFD /* Infrastructure.framework in Frameworks */, ); @@ -59,7 +82,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 08B29EE62DB6696700E57EFA /* RxSwift in Frameworks */, + 0522C1DF2DB67C7700B141FF /* RxSwift in Frameworks */, 05C1D6262DB53A6E00508FFD /* Infrastructure.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -123,6 +146,7 @@ 058CC8EC2DB5377F0084221A /* Sources */, 058CC8ED2DB5377F0084221A /* Frameworks */, 058CC8EE2DB5377F0084221A /* Resources */, + 05BDD5E42DB678DC00C1E192 /* Embed Frameworks */, ); buildRules = ( ); @@ -134,7 +158,7 @@ ); name = Domain; packageProductDependencies = ( - 08B2A34E2DB66AD300E57EFA /* RxSwift */, + 0522C1DC2DB67C6E00B141FF /* RxSwift */, ); productName = Domain; productReference = 058CC8F02DB5377F0084221A /* Domain.framework */; @@ -148,6 +172,7 @@ 05C1D0D72DB538A600508FFD /* Sources */, 05C1D0D82DB538A600508FFD /* Frameworks */, 05C1D0D92DB538A600508FFD /* Resources */, + 05BDD5E02DB678D100C1E192 /* Embed Frameworks */, ); buildRules = ( ); @@ -158,7 +183,7 @@ ); name = DomainInterface; packageProductDependencies = ( - 08B29EE52DB6696700E57EFA /* RxSwift */, + 0522C1DE2DB67C7700B141FF /* RxSwift */, ); productName = DomainInterface; productReference = 05C1D0DB2DB538A600508FFD /* DomainInterface.framework */; @@ -561,12 +586,12 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 08B29EE52DB6696700E57EFA /* RxSwift */ = { + 0522C1DC2DB67C6E00B141FF /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; - 08B2A34E2DB66AD300E57EFA /* RxSwift */ = { + 0522C1DE2DB67C7700B141FF /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05C1D8282DB53CC100508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 5c009e1c..3970791e 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D82DB67C5900B141FF /* RxSwift */; }; + 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C3C52DB67D7800B141FF /* RxGesture */; }; 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; @@ -17,26 +19,10 @@ 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; }; 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC93D72DB6599500771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D62DB6599500771CB3 /* RxCocoa */; }; - 082197A12D426DCB0054094A /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 082197A02D426DCB0054094A /* Then */; }; - 083A25D02CF364B70099B58E /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 083A25CF2CF364B70099B58E /* Alamofire */; }; - 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */ = {isa = PBXBuildFile; productRef = 088DE2432D104EE70030FA9E /* SwiftSoup */; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; - 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142B2D994A3A00DFD08F /* KakaoSDK */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */; }; - 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 4E5825662D1951DF00EE83EF /* FloatingPanel */; }; - 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 4EA9989C2D21C404009DC30B /* RxDataSources */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; - BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F12CF35D0D005EECF6 /* SnapKit */; }; - BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41F72CF35D9A005EECF6 /* RxSwift */; }; - BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA41FD2CF35EE7005EECF6 /* ReactorKit */; }; - BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA42002CF35EFE005EECF6 /* RxKeyboard */; }; - BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA42032CF35F76005EECF6 /* PanModal */; }; - BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA42062CF35FA6005EECF6 /* Tabman */; }; - BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA42092CF35FB1005EECF6 /* Pageboy */; }; - BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA420C2CF35FD2005EECF6 /* RxGesture */; }; - BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = BDCA420F2CF35FF5005EECF6 /* Lottie */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -98,31 +84,17 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - BDCA41F82CF35D9A005EECF6 /* RxSwift in Frameworks */, 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */, - BDCA420D2CF35FD2005EECF6 /* RxGesture in Frameworks */, - BDCA42072CF35FA6005EECF6 /* Tabman in Frameworks */, - BDCA42042CF35F76005EECF6 /* PanModal in Frameworks */, 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */, - 082197A12D426DCB0054094A /* Then in Frameworks */, - 05EC93D72DB6599500771CB3 /* RxCocoa in Frameworks */, + 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */, - 083A25D02CF364B70099B58E /* Alamofire in Frameworks */, 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */, 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, - BDCA42102CF35FF5005EECF6 /* Lottie in Frameworks */, - BDCA41FE2CF35EE7005EECF6 /* ReactorKit in Frameworks */, - BDCA41F22CF35D0D005EECF6 /* SnapKit in Frameworks */, - BDCA420A2CF35FB1005EECF6 /* Pageboy in Frameworks */, - 4E5825672D1951DF00EE83EF /* FloatingPanel in Frameworks */, - 4EA9989D2D21C404009DC30B /* RxDataSources in Frameworks */, + 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, - BDCA42012CF35EFE005EECF6 /* RxKeyboard in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, - 088DE2442D104EE70030FA9E /* SwiftSoup in Frameworks */, - 4E15142C2D994A3A00DFD08F /* KakaoSDK in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -181,25 +153,11 @@ ); name = Poppool; packageProductDependencies = ( - BDCA41F12CF35D0D005EECF6 /* SnapKit */, - BDCA41F72CF35D9A005EECF6 /* RxSwift */, - BDCA41FD2CF35EE7005EECF6 /* ReactorKit */, - BDCA42002CF35EFE005EECF6 /* RxKeyboard */, - BDCA42032CF35F76005EECF6 /* PanModal */, - BDCA42062CF35FA6005EECF6 /* Tabman */, - BDCA42092CF35FB1005EECF6 /* Pageboy */, - BDCA420C2CF35FD2005EECF6 /* RxGesture */, - BDCA420F2CF35FF5005EECF6 /* Lottie */, - 083A25CF2CF364B70099B58E /* Alamofire */, - 088DE2432D104EE70030FA9E /* SwiftSoup */, - 4E5825662D1951DF00EE83EF /* FloatingPanel */, - 4EA9989C2D21C404009DC30B /* RxDataSources */, - 082197A02D426DCB0054094A /* Then */, 4E1514292D99480200DFD08F /* NMapsMap */, - 4E15142B2D994A3A00DFD08F /* KakaoSDK */, 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */, 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */, - 05EC93D62DB6599500771CB3 /* RxCocoa */, + 0522C1D82DB67C5900B141FF /* RxSwift */, + 0522C3C52DB67D7800B141FF /* RxGesture */, ); productName = Poppool; productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; @@ -229,22 +187,11 @@ ); mainGroup = BDCA41B42CF35AC0005EECF6; packageReferences = ( - BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */, - BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */, - BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */, - BDCA41FF2CF35EFE005EECF6 /* XCRemoteSwiftPackageReference "RxKeyboard" */, - BDCA42022CF35F76005EECF6 /* XCRemoteSwiftPackageReference "PanModal" */, - BDCA42052CF35FA6005EECF6 /* XCRemoteSwiftPackageReference "Tabman" */, - BDCA42082CF35FB1005EECF6 /* XCRemoteSwiftPackageReference "Pageboy" */, - BDCA420B2CF35FD2005EECF6 /* XCRemoteSwiftPackageReference "RxGesture" */, - BDCA420E2CF35FF5005EECF6 /* XCRemoteSwiftPackageReference "lottie-spm" */, - 083A25CE2CF364B70099B58E /* XCRemoteSwiftPackageReference "Alamofire" */, - 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */, - 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */, - 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */, - 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */, 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, + 05BDD5C32DB674E500C1E192 /* XCRemoteSwiftPackageReference "SnapKit" */, + 05BDD5D62DB6783C00C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */, + 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */, ); preferredProjectObjectVersion = 56; productRefGroup = BDCA41BE2CF35AC0005EECF6 /* Products */; @@ -537,28 +484,28 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */ = { + 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/devxoul/Then"; + repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 3.0.0; + minimumVersion = 4.0.4; }; }; - 083A25CE2CF364B70099B58E /* XCRemoteSwiftPackageReference "Alamofire" */ = { + 05BDD5C32DB674E500C1E192 /* XCRemoteSwiftPackageReference "SnapKit" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Alamofire/Alamofire.git"; + repositoryURL = "https://github.com/SnapKit/SnapKit"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.10.1; + minimumVersion = 5.7.1; }; }; - 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */ = { + 05BDD5D62DB6783C00C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scinfu/SwiftSoup"; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 2.7.6; + minimumVersion = 6.9.0; }; }; 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { @@ -577,22 +524,6 @@ minimumVersion = 3.21.0; }; }; - 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scenee/FloatingPanel.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.8.6; - }; - }; - 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.0.2; - }; - }; 4EE360FB2D91876300D2441D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; @@ -601,110 +532,24 @@ minimumVersion = 3.21.0; }; }; - BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/SnapKit/SnapKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.7.1; - }; - }; - BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactiveX/RxSwift.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 6.8.0; - }; - }; - BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/ReactorKit/ReactorKit.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - BDCA41FF2CF35EFE005EECF6 /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.1; - }; - }; - BDCA42022CF35F76005EECF6 /* XCRemoteSwiftPackageReference "PanModal" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/slackhq/PanModal.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.7; - }; - }; - BDCA42052CF35FA6005EECF6 /* XCRemoteSwiftPackageReference "Tabman" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/uias/Tabman.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 3.2.0; - }; - }; - BDCA42082CF35FB1005EECF6 /* XCRemoteSwiftPackageReference "Pageboy" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/uias/Pageboy.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.2.0; - }; - }; - BDCA420B2CF35FD2005EECF6 /* XCRemoteSwiftPackageReference "RxGesture" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxGesture.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.0.4; - }; - }; - BDCA420E2CF35FF5005EECF6 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/airbnb/lottie-spm.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 4.5.0; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 05EC93D62DB6599500771CB3 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; - 082197A02D426DCB0054094A /* Then */ = { + 0522C1D82DB67C5900B141FF /* RxSwift */ = { isa = XCSwiftPackageProductDependency; - package = 0821979F2D426DCB0054094A /* XCRemoteSwiftPackageReference "Then" */; - productName = Then; - }; - 083A25CF2CF364B70099B58E /* Alamofire */ = { - isa = XCSwiftPackageProductDependency; - package = 083A25CE2CF364B70099B58E /* XCRemoteSwiftPackageReference "Alamofire" */; - productName = Alamofire; + package = 05BDD5D62DB6783C00C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; }; - 088DE2432D104EE70030FA9E /* SwiftSoup */ = { + 0522C3C52DB67D7800B141FF /* RxGesture */ = { isa = XCSwiftPackageProductDependency; - package = 088DE2422D104EE70030FA9E /* XCRemoteSwiftPackageReference "SwiftSoup" */; - productName = SwiftSoup; + package = 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */; + productName = RxGesture; }; 4E1514292D99480200DFD08F /* NMapsMap */ = { isa = XCSwiftPackageProductDependency; package = 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; productName = NMapsMap; }; - 4E15142B2D994A3A00DFD08F /* KakaoSDK */ = { - isa = XCSwiftPackageProductDependency; - productName = KakaoSDK; - }; 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */ = { isa = XCSwiftPackageProductDependency; productName = KakaoSDKAuth; @@ -713,66 +558,11 @@ isa = XCSwiftPackageProductDependency; productName = KakaoSDKCommon; }; - 4E5825662D1951DF00EE83EF /* FloatingPanel */ = { - isa = XCSwiftPackageProductDependency; - package = 4E5825652D1951DF00EE83EF /* XCRemoteSwiftPackageReference "FloatingPanel" */; - productName = FloatingPanel; - }; - 4EA9989C2D21C404009DC30B /* RxDataSources */ = { - isa = XCSwiftPackageProductDependency; - package = 4EA9989B2D21C404009DC30B /* XCRemoteSwiftPackageReference "RxDataSources" */; - productName = RxDataSources; - }; 4EE360FC2D91876300D2441D /* NMapsMap */ = { isa = XCSwiftPackageProductDependency; package = 4EE360FB2D91876300D2441D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; productName = NMapsMap; }; - BDCA41F12CF35D0D005EECF6 /* SnapKit */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F02CF35D0D005EECF6 /* XCRemoteSwiftPackageReference "SnapKit" */; - productName = SnapKit; - }; - BDCA41F72CF35D9A005EECF6 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41F62CF35D9A005EECF6 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; - BDCA41FD2CF35EE7005EECF6 /* ReactorKit */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41FC2CF35EE7005EECF6 /* XCRemoteSwiftPackageReference "ReactorKit" */; - productName = ReactorKit; - }; - BDCA42002CF35EFE005EECF6 /* RxKeyboard */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA41FF2CF35EFE005EECF6 /* XCRemoteSwiftPackageReference "RxKeyboard" */; - productName = RxKeyboard; - }; - BDCA42032CF35F76005EECF6 /* PanModal */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA42022CF35F76005EECF6 /* XCRemoteSwiftPackageReference "PanModal" */; - productName = PanModal; - }; - BDCA42062CF35FA6005EECF6 /* Tabman */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA42052CF35FA6005EECF6 /* XCRemoteSwiftPackageReference "Tabman" */; - productName = Tabman; - }; - BDCA42092CF35FB1005EECF6 /* Pageboy */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA42082CF35FB1005EECF6 /* XCRemoteSwiftPackageReference "Pageboy" */; - productName = Pageboy; - }; - BDCA420C2CF35FD2005EECF6 /* RxGesture */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA420B2CF35FD2005EECF6 /* XCRemoteSwiftPackageReference "RxGesture" */; - productName = RxGesture; - }; - BDCA420F2CF35FF5005EECF6 /* Lottie */ = { - isa = XCSwiftPackageProductDependency; - package = BDCA420E2CF35FF5005EECF6 /* XCRemoteSwiftPackageReference "lottie-spm" */; - productName = Lottie; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = BDCA41B52CF35AC0005EECF6 /* Project object */; diff --git a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved index c64560f9..069cbc12 100644 --- a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "893e9d5956a6d674d140654334c58c276c99001321206803c07c48415f2e6ac3", + "originHash" : "bb8b8add80965de3fd56f4bb28fdba3b8a176e1362ab3ddd0bb3dcc27bc6f621", "pins" : [ { "identity" : "alamofire", @@ -29,11 +29,11 @@ } }, { - "identity" : "lottie-spm", + "identity" : "lottie-ios", "kind" : "remoteSourceControl", - "location" : "https://github.com/airbnb/lottie-spm.git", + "location" : "https://github.com/airbnb/lottie-ios", "state" : { - "revision" : "8c6edf4f0fa84fe9c058600a4295eb0c01661c69", + "revision" : "047aa81b77adcbf583a966dfef620d17650cc656", "version" : "4.5.1" } }, @@ -127,15 +127,6 @@ "version" : "3.21.0" } }, - { - "identity" : "swiftsoup", - "kind" : "remoteSourceControl", - "location" : "https://github.com/scinfu/SwiftSoup", - "state" : { - "revision" : "bba848db50462894e7fc0891d018dfecad4ef11e", - "version" : "2.8.7" - } - }, { "identity" : "tabman", "kind" : "remoteSourceControl", diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 048389c4..dec23ab8 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -4,7 +4,6 @@ import CoreLocation import Data import Domain import DomainInterface -import Presentation import Infrastructure import KakaoSDKCommon diff --git a/Poppool/Poppool/Application/SceneDelegate.swift b/Poppool/Poppool/Application/SceneDelegate.swift index df8f5bb8..1df47b66 100644 --- a/Poppool/Poppool/Application/SceneDelegate.swift +++ b/Poppool/Poppool/Application/SceneDelegate.swift @@ -11,7 +11,6 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { static let appDidBecomeActive = PublishSubject() static let appDidDisconnect = PublishSubject() - private let disposeBag = DisposeBag() func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 961cac34..f8acaeec 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -9,13 +9,14 @@ /* Begin PBXBuildFile section */ 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; - 05BDD3DA2DB66E6400C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3D92DB66E6400C1E192 /* SnapKit */; }; + 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1E02DB67C8300B141FF /* RxSwift */; }; + 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CB2DB6756500C1E192 /* SnapKit */; }; + 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */; }; 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D22DB6536200771CB3 /* RxCocoa */; }; - 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D42DB6536200771CB3 /* RxSwift */; }; 08B2A3582DB66B4100E57EFA /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3572DB66B4100E57EFA /* RxKeyboard */; }; 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */; }; 08B2A35E2DB66B8600E57EFA /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A35D2DB66B8600E57EFA /* RxDataSources */; }; @@ -58,13 +59,14 @@ buildActionMask = 2147483647; files = ( 08B2A3672DB66BD400E57EFA /* Pageboy in Frameworks */, - 05BDD3DA2DB66E6400C1E192 /* SnapKit in Frameworks */, - 05EC93D52DB6536200771CB3 /* RxSwift in Frameworks */, + 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */, 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, + 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */, 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */, 08B2A3612DB66BAB00E57EFA /* Then in Frameworks */, + 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */, @@ -142,7 +144,6 @@ 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */, 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, 05EC93D22DB6536200771CB3 /* RxCocoa */, - 05EC93D42DB6536200771CB3 /* RxSwift */, 08B2A3572DB66B4100E57EFA /* RxKeyboard */, 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */, 08B2A35D2DB66B8600E57EFA /* RxDataSources */, @@ -150,7 +151,9 @@ 08B2A3632DB66BBC00E57EFA /* Tabman */, 08B2A3662DB66BD400E57EFA /* Pageboy */, 08B2A3692DB66BF200E57EFA /* RxGesture */, - 05BDD3D92DB66E6400C1E192 /* SnapKit */, + 05BDD5CB2DB6756500C1E192 /* SnapKit */, + 05BDD5CE2DB6770300C1E192 /* Lottie */, + 0522C1E02DB67C8300B141FF /* RxSwift */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -193,6 +196,7 @@ 08B2A3622DB66BBC00E57EFA /* XCRemoteSwiftPackageReference "Tabman" */, 08B2A3652DB66BD400E57EFA /* XCRemoteSwiftPackageReference "Pageboy" */, 08B2A3682DB66BF200E57EFA /* XCRemoteSwiftPackageReference "RxGesture" */, + 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9052DB537960084221A /* Products */; @@ -472,6 +476,14 @@ minimumVersion = 1.2.7; }; }; + 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/airbnb/lottie-ios"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.5.1; + }; + }; 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; @@ -549,11 +561,21 @@ package = 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */; productName = PanModal; }; - 05BDD3D92DB66E6400C1E192 /* SnapKit */ = { + 0522C1E02DB67C8300B141FF /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 05BDD5CB2DB6756500C1E192 /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; + 05BDD5CE2DB6770300C1E192 /* Lottie */ = { + isa = XCSwiftPackageProductDependency; + package = 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */; + productName = Lottie; + }; 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */ = { isa = XCSwiftPackageProductDependency; package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; @@ -569,11 +591,6 @@ package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxCocoa; }; - 05EC93D42DB6536200771CB3 /* RxSwift */ = { - isa = XCSwiftPackageProductDependency; - package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxSwift; - }; 08B2A3572DB66B4100E57EFA /* RxKeyboard */ = { isa = XCSwiftPackageProductDependency; package = 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */; From 1d65ee27af82ee6f6325ef49eb7f53677ed25057 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 21 Apr 2025 13:23:00 +0000 Subject: [PATCH 185/393] style/#112: Apply SwiftLint autocorrect --- .../Infrastructure/DIContainer/DependencyWrapper.swift | 2 +- .../Data/RepositoryImpl/PreSignedRepositoryImpl.swift | 4 ++-- .../Entity/AdminResponse/AdminStore.swift | 2 +- .../Entity/AdminResponse/AdminStoreDetail.swift | 4 ++-- .../Entity/AuthResponse/CategoryResponse.swift | 2 +- .../Entity/PopUpResponse/GetPopUpCommentResponse.swift | 2 +- .../Entity/PopUpResponse/GetPopUpDetailResponse.swift | 6 +++--- .../PopUpResponse/GetSearchBottomPopUpListResponse.swift | 2 +- .../PopUpResponse/GetSearchPopUpListResponse.swift | 2 +- .../Entity/UserResponse/GetBlockUserListResponse.swift | 2 +- .../Entity/UserResponse/GetMyPageResponse.swift | 2 +- .../Entity/UserResponse/GetMyProfileResponse.swift | 2 +- .../Entity/UserResponse/GetNoticeDetailResponse.swift | 2 +- .../Entity/UserResponse/GetNoticeListResponse.swift | 2 +- .../GetOtherUserCommentedPopUpListResponse.swift | 2 +- .../Entity/UserResponse/GetRecentPopUpResponse.swift | 2 +- .../Entity/UserResponse/GetWithdrawlListResponse.swift | 2 +- Poppool/Poppool/Application/AppDelegate.swift | 2 +- .../Admin/AdminRegister/PopUpStoreRegisterReactor.swift | 4 ++-- .../Presentation/Scene/Admin/AdminStoreCell.swift | 2 +- .../Presentation/Scene/Admin/AdminViewController.swift | 4 ++-- .../Scene/Comment/CommentList/CommentListReactor.swift | 4 ++-- .../NormalCommentAdd/NormalCommentAddReactor.swift | 2 +- .../NormalCommentEdit/NormalCommentEditReactor.swift | 9 ++++----- .../Presentation/Scene/Detail/DetailReactor.swift | 4 ++-- .../Main/View/HomeCardSection/HomeCardSectionCell.swift | 2 +- .../Presentation/Scene/Login/Main/LoginReactor.swift | 2 +- .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 2 +- .../Scene/Map/MapView/MapViewController.swift | 2 +- .../MyPage/ProfileEdit/Main/ProfileEditReactor.swift | 2 +- .../Presentation/Scene/Splash/SplashController.swift | 4 ++-- .../Presentation/Utills/Service/AppleLoginService.swift | 2 +- .../Presentation/Utills/Service/KakaoLoginService.swift | 2 +- 33 files changed, 45 insertions(+), 46 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift index cbfee35a..7096c486 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/DIContainer/DependencyWrapper.swift @@ -20,7 +20,7 @@ import Foundation public final class Dependency { /// DIContainer에서 꺼내온 실제 인스턴스 public private(set) var wrappedValue: T - + /// DIContainer로부터 자동으로 인스턴스를 꺼내와 초기화합니다. public init() { self.wrappedValue = DIContainer.resolve(T.self) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift index 8bcf66d5..3c08b22b 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/PreSignedRepositoryImpl.swift @@ -18,13 +18,13 @@ public final class PreSignedRepositoryImpl: PreSignedRepository { ) }) } - + public func tryDelete(objectKeyList: [String]) -> Completable { return service.tryDelete( targetPaths: PresignedURLRequestDTO(objectKeyList: objectKeyList) ) } - + public func fullImageURL(from filePath: String) -> URL? { return service.fullImageURL(from: filePath) } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift index e1d0c9f9..e57f78fc 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStore.swift @@ -7,7 +7,7 @@ public struct AdminStore { self.categoryName = categoryName self.mainImageUrl = mainImageUrl } - + public let id: Int64 public let name: String public let categoryName: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index 353eda79..7dbb50b8 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -20,7 +20,7 @@ public struct AdminStoreDetail { self.markerTitle = markerTitle self.markerSnippet = markerSnippet } - + public let id: Int64 public let name: String public let categoryId: Int64 @@ -44,7 +44,7 @@ public struct AdminStoreDetail { self.id = id self.imageUrl = imageUrl } - + public let id: Int64 public let imageUrl: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index f8848576..23d1c6e0 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -5,7 +5,7 @@ public struct CategoryResponse { self.categoryId = categoryId self.category = category } - + public let categoryId: Int64 public let category: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift index 5f8e5ae9..ae460a5a 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpCommentResponse.swift @@ -4,6 +4,6 @@ public struct GetPopUpCommentResponse { public init(commentList: [GetPopUpDetailCommentResponse]) { self.commentList = commentList } - + public let commentList: [GetPopUpDetailCommentResponse] } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift index be18c5c7..fcb8137c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetPopUpDetailResponse.swift @@ -41,7 +41,7 @@ public struct GetPopUpDetailImageResponse { self.id = id self.imageUrl = imageUrl } - + public let id: Int64 public let imageUrl: String? } @@ -60,7 +60,7 @@ public struct GetPopUpDetailCommentResponse { self.createDateTime = createDateTime self.commentImageList = commentImageList } - + public let commentId: Int64 public let creator: String? public let nickname: String? @@ -81,7 +81,7 @@ public struct GetPopUpDetailSimilarResponse { self.mainImageUrl = mainImageUrl self.endDate = endDate } - + public let id: Int64 public let name: String? public let mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift index b5434ece..acaab501 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchBottomPopUpListResponse.swift @@ -7,7 +7,7 @@ public struct GetSearchBottomPopUpListResponse { self.totalPages = totalPages self.totalElements = totalElements } - + public var popUpStoreList: [PopUpStoreResponse] public var loginYn: Bool public var totalPages: Int32 diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift index e628592f..93c13fa5 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/PopUpResponse/GetSearchPopUpListResponse.swift @@ -5,7 +5,7 @@ public struct GetSearchPopUpListResponse { self.popUpStoreList = popUpStoreList self.loginYn = loginYn } - + public var popUpStoreList: [PopUpStoreResponse] public var loginYn: Bool } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift index a1495ca9..a63aaf7f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetBlockUserListResponse.swift @@ -19,7 +19,7 @@ public struct GetBlockUserListDataResponse { self.nickname = nickname self.instagramId = instagramId } - + public var userId: String? public var profileImageUrl: String? public var nickname: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift index b7d1dd73..47bfb54b 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyPageResponse.swift @@ -26,7 +26,7 @@ public struct GetMyPagePopUpResponse { self.popUpStoreName = popUpStoreName self.mainImageUrl = mainImageUrl } - + public var popUpStoreId: Int64 public var popUpStoreName: String? public var mainImageUrl: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift index ce7a1534..671433c8 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetMyProfileResponse.swift @@ -11,7 +11,7 @@ public struct GetMyProfileResponse { self.age = age self.interestCategoryList = interestCategoryList } - + public var profileImageUrl: String? public var nickname: String? public var email: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift index 532a7a5c..a1e20371 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeDetailResponse.swift @@ -7,7 +7,7 @@ public struct GetNoticeDetailResponse { self.content = content self.createDateTime = createDateTime } - + var id: Int64 public var title: String? public var content: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift index 121592eb..fe085bfc 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetNoticeListResponse.swift @@ -14,7 +14,7 @@ public struct GetNoticeListDataResponse { self.title = title self.createdDateTime = createdDateTime } - + public var id: Int64 public var title: String? public var createdDateTime: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift index ab1710d1..65c2f463 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetOtherUserCommentedPopUpListResponse.swift @@ -19,7 +19,7 @@ public struct GetOtherUserCommentedPopUpResponse { self.address = address self.closedYn = closedYn } - + public var popUpStoreId: Int64 public var popUpStoreName: String? public var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift index 46d0e070..0e84a1f6 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetRecentPopUpResponse.swift @@ -23,7 +23,7 @@ public struct GetRecentPopUpDataResponse { self.address = address self.closeYn = closeYn } - + public var popUpStoreId: Int64 public var popUpStoreName: String? public var desc: String? diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift index 636ee8f9..830001e0 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/UserResponse/GetWithdrawlListResponse.swift @@ -13,7 +13,7 @@ public struct GetWithdrawlListDataResponse { self.id = id self.survey = survey } - + public var id: Int64 public var survey: String? } diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index dec23ab8..f7f91de9 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -1,5 +1,5 @@ -import UIKit import CoreLocation +import UIKit import Data import Domain diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 53df9995..324f2183 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -1,8 +1,8 @@ -import UIKit import CoreLocation +import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift index 3d6b6dda..2abf1f73 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift @@ -80,7 +80,7 @@ final class AdminStoreCell: UITableViewCell { } // MARK: - Configure - func configure(with store: AdminStore) { + func configure(with store: AdminStore) { Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) titleLabel.text = store.name diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index 7e98d74a..ddf71a45 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa @@ -298,7 +298,7 @@ final class AdminViewController: BaseViewController, View { cellIdentifier: AdminStoreCell.identifier, cellType: AdminStoreCell.self )) { _, store, cell in - cell.configure(with: store) + cell.configure(with: store) } .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index 0707c1ba..e0b71792 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa @@ -302,7 +302,7 @@ final class CommentListReactor: Reactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) - .subscribe(onDisposed: { + .subscribe(onDisposed: { Logger.log(message: "S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index e3acc723..b68f78ed 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -1,8 +1,8 @@ import PhotosUI import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 788444f9..1ad07a70 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -1,8 +1,8 @@ import PhotosUI import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa @@ -35,14 +35,13 @@ final class NormalCommentEditReactor: Reactor { var isReloadView: Bool = true var isSaving: Bool = false } - + struct PutCommentImageData { var imageId: Int64? var imageUrl: String? var actionType: String? } - // MARK: - properties var initialState: State @@ -190,10 +189,10 @@ final class NormalCommentEditReactor: Reactor { content: newState.text, imageUrlList: (convertAddImages + convertKeepImages + convertDeleteImages).map { $0.imageUrl } ) - .subscribe(onDisposed: { [weak self, weak controller] in + .subscribe(onDisposed: { [weak self, weak controller] in guard let self = self else { return } self.preSignedUseCase.tryDelete(objectKeyList: deleteImages.compactMap { $0.0 }) - .subscribe(onDisposed: { + .subscribe(onDisposed: { controller?.navigationController?.popViewController(animated: true) }) .disposed(by: self.disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 71a11147..f54b4f78 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DomainInterface +import Infrastructure import LinkPresentation import ReactorKit @@ -538,7 +538,7 @@ extension DetailReactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) - .subscribe(onDisposed: { + .subscribe(onDisposed: { Logger.log(message: "S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index 8c26452a..c69e6409 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -56,7 +56,7 @@ final class HomeCardSectionCell: UICollectionViewCell { label.textColor = .w100 return label }() - + // MARK: - init override init(frame: CGRect) { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index cb6b11e3..32b3fe8e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -1,5 +1,5 @@ -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index d257b177..88170711 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -1,5 +1,5 @@ -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 1c275561..39e20a31 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1,5 +1,5 @@ -import UIKit import CoreLocation +import UIKit import DomainInterface import Infrastructure diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 0b57dd84..42702d27 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -1,8 +1,8 @@ import PhotosUI import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index e28d8ac6..a4bc2163 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DomainInterface +import Infrastructure import ReactorKit import RxCocoa @@ -9,7 +9,7 @@ import RxSwift import SnapKit public final class SplashController: BaseViewController { - + // MARK: - Properties var disposeBag = DisposeBag() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift index 91a42cc0..18458103 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift @@ -1,5 +1,5 @@ -import Infrastructure import DomainInterface +import Infrastructure import AuthenticationServices import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift index a7efe24e..634cf838 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift @@ -1,5 +1,5 @@ -import Infrastructure import DomainInterface +import Infrastructure import KakaoSDKAuth import KakaoSDKUser From 7c055bc643825cd1b8a08d606c52f1697715b847 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 22 Apr 2025 14:05:34 +0900 Subject: [PATCH 186/393] =?UTF-8?q?refactor/#122:=20login=20service?= =?UTF-8?q?=EB=A5=BC=20=EB=84=A4=ED=8A=B8=EC=9B=8C=ED=81=AC=20layer?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 기존에 Service를 이용하던 곳은 UseCase를 이용하도록 수정 - UseCase를 이용하면서 필요한 의존성 주입 진행 - UseCase는 Repository를 이용하도록 수정 - AppleLogin 객체를 재생성하는 부분을 제거 - Data에 KakaoSDK 추가 및 Presentation에서 KakaoSDK 제거 --- .../Data/Data.xcodeproj/project.pbxproj | 30 +++++++++++++++++++ .../Network}/Service/AppleLoginService.swift | 0 .../Network}/Service/AuthServiceable.swift | 0 .../Network}/Service/KakaoLoginService.swift | 0 .../AppleLoginRepositoryImpl.swift | 15 ++++++++++ .../KakaoLoginRepositoryImpl.swift | 15 ++++++++++ .../UseCaseImpl/AppleLoginUseCaseImpl.swift | 17 +++++++++++ .../UseCaseImpl/KakaoLoginUseCaseImpl.swift | 17 +++++++++++ .../Repository/AppleLoginRepository.swift | 7 +++++ .../Repository/KakaoLoginRepository.swift | 7 +++++ .../UseCase/AppleLoginUseCase.swift | 7 +++++ .../UseCase/KakaoLoginUseCase.swift | 7 +++++ .../xcshareddata/swiftpm/Package.resolved | 6 ++-- Poppool/Poppool/Application/AppDelegate.swift | 6 ++++ .../Presentation.xcodeproj/project.pbxproj | 25 ---------------- .../Scene/Detail/DetailReactor.swift | 12 ++++++-- .../Scene/Login/Main/LoginReactor.swift | 21 ++++++++----- .../Scene/Login/Sub/SubLoginReactor.swift | 20 ++++++++----- .../Scene/MyPage/Main/MyPageReactor.swift | 4 ++- .../Scene/Splash/SplashController.swift | 4 ++- 20 files changed, 171 insertions(+), 49 deletions(-) rename Poppool/{PresentationLayer/Presentation/Presentation/Utills => DataLayer/Data/Data/Network}/Service/AppleLoginService.swift (100%) rename Poppool/{PresentationLayer/Presentation/Presentation/Utills => DataLayer/Data/Data/Network}/Service/AuthServiceable.swift (100%) rename Poppool/{PresentationLayer/Presentation/Presentation/Utills => DataLayer/Data/Data/Network}/Service/KakaoLoginService.swift (100%) create mode 100644 Poppool/DataLayer/Data/Data/RepositoryImpl/AppleLoginRepositoryImpl.swift create mode 100644 Poppool/DataLayer/Data/Data/RepositoryImpl/KakaoLoginRepositoryImpl.swift create mode 100644 Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AppleLoginUseCaseImpl.swift create mode 100644 Poppool/DomainLayer/Domain/Domain/UseCaseImpl/KakaoLoginUseCaseImpl.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Repository/AppleLoginRepository.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Repository/KakaoLoginRepository.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/UseCase/AppleLoginUseCase.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/UseCase/KakaoLoginUseCase.swift diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 84c04464..dfaf69fc 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -8,6 +8,8 @@ /* Begin PBXBuildFile section */ 0522C1DB2DB67C6100B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1DA2DB67C6100B141FF /* RxSwift */; }; + 05BBA73A2DB75A320047A061 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA7392DB75A320047A061 /* KakaoSDKAuth */; }; + 05BBA73C2DB75A320047A061 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA73B2DB75A320047A061 /* KakaoSDKUser */; }; 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD3DB2DB66EB500C1E192 /* Alamofire */; }; 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6152DB53A5600508FFD /* DomainInterface.framework */; }; 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6162DB53A5600508FFD /* Infrastructure.framework */; }; @@ -87,11 +89,16 @@ Network/Interceptor/TokenInterceptor.swift, Network/Provider/Provider.swift, Network/Provider/ProviderImpl.swift, + Network/Service/AppleLoginService.swift, + Network/Service/AuthServiceable.swift, + Network/Service/KakaoLoginService.swift, Network/Service/PreSignedService.swift, RepositoryImpl/AdminRepositoryImpl.swift, + RepositoryImpl/AppleLoginRepositoryImpl.swift, RepositoryImpl/AuthAPIRepositoryImpl.swift, RepositoryImpl/CommentAPIRepositoryImpl.swift, RepositoryImpl/HomeAPIRepositoryImpl.swift, + RepositoryImpl/KakaoLoginRepositoryImpl.swift, RepositoryImpl/MapDirectionRepositoryImpl.swift, RepositoryImpl/MapRepositoryImpl.swift, RepositoryImpl/PopUpAPIRepositoryImpl.swift, @@ -121,8 +128,10 @@ files = ( 05BDD3DC2DB66EB500C1E192 /* Alamofire in Frameworks */, 05C1D6172DB53A5600508FFD /* DomainInterface.framework in Frameworks */, + 05BBA73C2DB75A320047A061 /* KakaoSDKUser in Frameworks */, 0522C1DB2DB67C6100B141FF /* RxSwift in Frameworks */, 05C1D6192DB53A5600508FFD /* Infrastructure.framework in Frameworks */, + 05BBA73A2DB75A320047A061 /* KakaoSDKAuth in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -185,6 +194,8 @@ packageProductDependencies = ( 05BDD3DB2DB66EB500C1E192 /* Alamofire */, 0522C1DA2DB67C6100B141FF /* RxSwift */, + 05BBA7392DB75A320047A061 /* KakaoSDKAuth */, + 05BBA73B2DB75A320047A061 /* KakaoSDKUser */, ); productName = Data; productReference = 058CC8DC2DB5376A0084221A /* Data.framework */; @@ -217,6 +228,7 @@ packageReferences = ( 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */, 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */, + 05BBA7382DB75A320047A061 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC8DD2DB5376A0084221A /* Products */; @@ -464,6 +476,14 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 05BBA7382DB75A320047A061 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 2.24.1; + }; + }; 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; @@ -488,6 +508,16 @@ package = 05BDD5DA2DB6786900C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; + 05BBA7392DB75A320047A061 /* KakaoSDKAuth */ = { + isa = XCSwiftPackageProductDependency; + package = 05BBA7382DB75A320047A061 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKAuth; + }; + 05BBA73B2DB75A320047A061 /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 05BBA7382DB75A320047A061 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; 05BDD3DB2DB66EB500C1E192 /* Alamofire */ = { isa = XCSwiftPackageProductDependency; package = 08B2A3532DB66B1D00E57EFA /* XCRemoteSwiftPackageReference "Alamofire" */; diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AppleLoginService.swift rename to Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AuthServiceable.swift b/Poppool/DataLayer/Data/Data/Network/Service/AuthServiceable.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/AuthServiceable.swift rename to Poppool/DataLayer/Data/Data/Network/Service/AuthServiceable.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Service/KakaoLoginService.swift rename to Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AppleLoginRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AppleLoginRepositoryImpl.swift new file mode 100644 index 00000000..8e306c22 --- /dev/null +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AppleLoginRepositoryImpl.swift @@ -0,0 +1,15 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public class AppleLoginRepositoryImpl: AppleLoginRepository { + private let service = AppleLoginService() + + public init() { } + + public func fetchUserCredential() -> Observable { + return service.fetchUserCredential() + } +} diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/KakaoLoginRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/KakaoLoginRepositoryImpl.swift new file mode 100644 index 00000000..86145f69 --- /dev/null +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/KakaoLoginRepositoryImpl.swift @@ -0,0 +1,15 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public class KakaoLoginRepositoryImpl: KakaoLoginRepository { + let service = KakaoLoginService() + + public init() { } + + public func fetchUserCredential() -> Observable { + return service.fetchUserCredential() + } +} diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AppleLoginUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AppleLoginUseCaseImpl.swift new file mode 100644 index 00000000..2f0e4f9f --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AppleLoginUseCaseImpl.swift @@ -0,0 +1,17 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public class AppleLoginUseCaseImpl: AppleLoginUseCase { + private let repository: AppleLoginRepository + + public init(repository: AppleLoginRepository) { + self.repository = repository + } + + public func fetchUserCredential() -> Observable { + return repository.fetchUserCredential() + } +} diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/KakaoLoginUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/KakaoLoginUseCaseImpl.swift new file mode 100644 index 00000000..261dc3a9 --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/KakaoLoginUseCaseImpl.swift @@ -0,0 +1,17 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public class KakaoLoginUseCaseImpl: KakaoLoginUseCase { + private let repository: KakaoLoginRepository + + public init(repository: KakaoLoginRepository) { + self.repository = repository + } + + public func fetchUserCredential() -> Observable { + return repository.fetchUserCredential() + } +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/AppleLoginRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AppleLoginRepository.swift new file mode 100644 index 00000000..6ba5676b --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/AppleLoginRepository.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol AppleLoginRepository { + func fetchUserCredential() -> Observable +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/KakaoLoginRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/KakaoLoginRepository.swift new file mode 100644 index 00000000..4be6e2ca --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/KakaoLoginRepository.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol KakaoLoginRepository { + func fetchUserCredential() -> Observable +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AppleLoginUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AppleLoginUseCase.swift new file mode 100644 index 00000000..8841534c --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/AppleLoginUseCase.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol AppleLoginUseCase { + func fetchUserCredential() -> Observable +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/KakaoLoginUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/KakaoLoginUseCase.swift new file mode 100644 index 00000000..22e4e65a --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/KakaoLoginUseCase.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol KakaoLoginUseCase { + func fetchUserCredential() -> Observable +} diff --git a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved index 069cbc12..7bcd7a79 100644 --- a/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Poppool/Poppool.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "bb8b8add80965de3fd56f4bb28fdba3b8a176e1362ab3ddd0bb3dcc27bc6f621", + "originHash" : "e3308f4d8d004a111784c1f182de9af75f8c85dd7807e55541802130c4c227fb", "pins" : [ { "identity" : "alamofire", @@ -24,8 +24,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/kakao/kakao-ios-sdk.git", "state" : { - "revision" : "bfe2fe42f730ccfe59e85f6e9eda2f4578e9a307", - "version" : "2.24.0" + "revision" : "2c780b70d4197be3f72deee2fd82ea3e3b767007", + "version" : "2.24.1" } }, { diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index f7f91de9..46c60253 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -52,6 +52,8 @@ extension AppDelegate { DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } DIContainer.register(MapDirectionRepository.self) { return MapDirectionRepositoryImpl(provider: provider) } DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } + DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } + DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -63,6 +65,8 @@ extension AppDelegate { @Dependency var authAPIRepository: AuthAPIRepository @Dependency var signUpRepository: SignUpRepository @Dependency var preSignedRepository: PreSignedRepository + @Dependency var kakaoLoginRepository: KakaoLoginRepository + @Dependency var appleLoginRepository: AppleLoginRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -74,5 +78,7 @@ extension AppDelegate { DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } DIContainer.register(SignUpAPIUseCase.self) { return SignUpAPIUseCaseImpl(repository: signUpRepository) } DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } + DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } + DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index f8acaeec..d67c1f31 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -14,8 +14,6 @@ 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */; }; - 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */; }; - 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */; }; 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D22DB6536200771CB3 /* RxCocoa */; }; 08B2A3582DB66B4100E57EFA /* RxKeyboard in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3572DB66B4100E57EFA /* RxKeyboard */; }; 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */; }; @@ -69,11 +67,9 @@ 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, - 05EC93CF2DB64C6F00771CB3 /* KakaoSDKAuth in Frameworks */, 08B2A3642DB66BBC00E57EFA /* Tabman in Frameworks */, 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */, 08B2A35E2DB66B8600E57EFA /* RxDataSources in Frameworks */, - 05EC93D12DB64C6F00771CB3 /* KakaoSDKUser in Frameworks */, 08B2A3582DB66B4100E57EFA /* RxKeyboard in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -141,8 +137,6 @@ packageProductDependencies = ( 05125B972DB626E3001342A2 /* ReactorKit */, 05125BA02DB6275C001342A2 /* PanModal */, - 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */, - 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */, 05EC93D22DB6536200771CB3 /* RxCocoa */, 08B2A3572DB66B4100E57EFA /* RxKeyboard */, 08B2A35A2DB66B5A00E57EFA /* FloatingPanel */, @@ -188,7 +182,6 @@ 05125B962DB626E3001342A2 /* XCRemoteSwiftPackageReference "ReactorKit" */, 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */, 05125B9F2DB6275C001342A2 /* XCRemoteSwiftPackageReference "PanModal" */, - 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */, 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */, 08B2A3592DB66B5A00E57EFA /* XCRemoteSwiftPackageReference "FloatingPanel" */, 08B2A35C2DB66B8600E57EFA /* XCRemoteSwiftPackageReference "RxDataSources" */, @@ -484,14 +477,6 @@ minimumVersion = 4.5.1; }; }; - 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/kakao/kakao-ios-sdk"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.24.0; - }; - }; 08B2A3562DB66B4100E57EFA /* XCRemoteSwiftPackageReference "RxKeyboard" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/RxSwiftCommunity/RxKeyboard"; @@ -576,16 +561,6 @@ package = 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */; productName = Lottie; }; - 05EC93CE2DB64C6F00771CB3 /* KakaoSDKAuth */ = { - isa = XCSwiftPackageProductDependency; - package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKAuth; - }; - 05EC93D02DB64C6F00771CB3 /* KakaoSDKUser */ = { - isa = XCSwiftPackageProductDependency; - package = 05EC93CD2DB64C6F00771CB3 /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; - productName = KakaoSDKUser; - }; 05EC93D22DB6536200771CB3 /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index f54b4f78..43b3121c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -168,7 +168,9 @@ final class DetailReactor: Reactor { } else { let loginController = SubLoginController() loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen @@ -206,7 +208,9 @@ final class DetailReactor: Reactor { } else { let loginController = SubLoginController() loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen @@ -243,7 +247,9 @@ final class DetailReactor: Reactor { case .moveToLoginScene(let controller): let loginController = SubLoginController() loginController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) ) let nextController = UINavigationController(rootViewController: loginController) nextController.modalPresentationStyle = .fullScreen diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index 32b3fe8e..1ae1b5fd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -34,17 +34,22 @@ final class LoginReactor: Reactor { private var authrizationCode: String? - private let kakaoLoginService = KakaoLoginService() - private var appleLoginService = AppleLoginService() 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 + authAPIUseCase: AuthAPIUseCase, + kakaoLoginUseCase: KakaoLoginUseCase, + appleLoginUseCase: AppleLoginUseCase ) { self.authAPIUseCase = authAPIUseCase + self.kakaoLoginUseCase = kakaoLoginUseCase + self.appleLoginUseCase = appleLoginUseCase self.initialState = State() } @@ -81,9 +86,9 @@ final class LoginReactor: Reactor { controller.view.window?.rootViewController = homeTabbar case .loadView: break - case .resetService: - authrizationCode = nil - appleLoginService = AppleLoginService() + case .resetService: break +// authrizationCode = nil +// appleLoginService = AppleLoginService() case .moveToInquiryScene(let controller): let nextController = FAQController() nextController.reactor = FAQReactor() @@ -93,7 +98,7 @@ final class LoginReactor: Reactor { } func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginService.fetchUserCredential() + return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in return owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") @@ -120,7 +125,7 @@ final class LoginReactor: Reactor { } func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginService.fetchUserCredential() + return appleLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in owner.authrizationCode = response.authorizationCode diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 88170711..fe7b6438 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -34,17 +34,21 @@ final class SubLoginReactor: Reactor { private var authrizationCode: String? - private let kakaoLoginService = KakaoLoginService() - private var appleLoginService = AppleLoginService() 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 + authAPIUseCase: AuthAPIUseCase, + kakaoLoginUseCase: KakaoLoginUseCase, + appleLoginUseCase: AppleLoginUseCase ) { self.authAPIUseCase = authAPIUseCase + self.kakaoLoginUseCase = kakaoLoginUseCase + self.appleLoginUseCase = appleLoginUseCase self.initialState = State() } @@ -78,9 +82,9 @@ final class SubLoginReactor: Reactor { controller.dismiss(animated: true) case .loadView: break - case .resetService: - authrizationCode = nil - appleLoginService = AppleLoginService() + case .resetService: break +// authrizationCode = nil +// appleLoginService = AppleLoginService() case .moveToInquiryScene(let controller): let nextController = FAQController() nextController.reactor = FAQReactor() @@ -90,7 +94,7 @@ final class SubLoginReactor: Reactor { } func loginWithKakao(controller: BaseViewController) -> Observable { - return kakaoLoginService.fetchUserCredential() + return kakaoLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in owner.authAPIUseCase.postTryLogin(userCredential: response, socialType: "kakao") @@ -117,7 +121,7 @@ final class SubLoginReactor: Reactor { } func loginWithApple(controller: BaseViewController) -> Observable { - return appleLoginService.fetchUserCredential() + return appleLoginUseCase.fetchUserCredential() .withUnretained(self) .flatMap { owner, response in owner.authrizationCode = response.authorizationCode diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 3fe8c1d9..26fe5b7b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -268,7 +268,9 @@ final class MyPageReactor: Reactor { case .moveToLoginScene(let controller): let nextController = SubLoginController() nextController.reactor = SubLoginReactor( - authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self) + authAPIUseCase: DIContainer.resolve(AuthAPIUseCase.self), + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) ) let navigationController = UINavigationController(rootViewController: nextController) navigationController.modalPresentationStyle = .fullScreen diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index a4bc2163..4d688b1a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -63,7 +63,9 @@ private extension SplashController { guard let self = self else { return } let loginViewController = LoginController() loginViewController.reactor = LoginReactor( - authAPIUseCase: authAPIUseCase + authAPIUseCase: authAPIUseCase, + kakaoLoginUseCase: DIContainer.resolve(KakaoLoginUseCase.self), + appleLoginUseCase: DIContainer.resolve(AppleLoginUseCase.self) ) let loginNavigationController = UINavigationController(rootViewController: loginViewController) rootViewController = loginNavigationController From 7507b60b5074b31f2f9015715292c3b896274d50 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 22 Apr 2025 14:20:53 +0900 Subject: [PATCH 187/393] =?UTF-8?q?fix/#122:=20Kakao=20=EB=A1=9C=EA=B7=B8?= =?UTF-8?q?=EC=9D=B8=20=EC=98=A4=EB=A5=98=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 로그인에 필요한 KakaoSDKUser가 빠져서 생긴 문제 --- Poppool/Poppool.xcodeproj/project.pbxproj | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 3970791e..db866640 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D82DB67C5900B141FF /* RxSwift */; }; 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C3C52DB67D7800B141FF /* RxGesture */; }; + 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */; }; 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; @@ -21,7 +22,6 @@ 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; - 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; /* End PBXBuildFile section */ @@ -89,9 +89,9 @@ 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */, - 4E1514302D994A3A00DFD08F /* KakaoSDKCommon in Frameworks */, 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */, 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, + 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */, 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, @@ -155,9 +155,9 @@ packageProductDependencies = ( 4E1514292D99480200DFD08F /* NMapsMap */, 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */, - 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */, 0522C1D82DB67C5900B141FF /* RxSwift */, 0522C3C52DB67D7800B141FF /* RxGesture */, + 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */, ); productName = Poppool; productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; @@ -545,6 +545,11 @@ package = 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */; productName = RxGesture; }; + 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */ = { + isa = XCSwiftPackageProductDependency; + package = 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; + productName = KakaoSDKUser; + }; 4E1514292D99480200DFD08F /* NMapsMap */ = { isa = XCSwiftPackageProductDependency; package = 4E1514282D99480200DFD08F /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; @@ -554,10 +559,6 @@ isa = XCSwiftPackageProductDependency; productName = KakaoSDKAuth; }; - 4E15142F2D994A3A00DFD08F /* KakaoSDKCommon */ = { - isa = XCSwiftPackageProductDependency; - productName = KakaoSDKCommon; - }; 4EE360FC2D91876300D2441D /* NMapsMap */ = { isa = XCSwiftPackageProductDependency; package = 4EE360FB2D91876300D2441D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; From bb2f0ec5dc3f29a29e1cb1669adc2b9b3c7197d0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 22 Apr 2025 14:34:09 +0900 Subject: [PATCH 188/393] =?UTF-8?q?refactor/#122:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20Mutation,=20Action=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 - AppleLogin 재시도 시 발생하는 에러로 인해 Service를 재할당 해주던 로직 - 제거 후 별다른 이상이 없어 관련 Flow 제거 --- .../Presentation/Scene/Login/Main/LoginController.swift | 6 ------ .../Presentation/Scene/Login/Main/LoginReactor.swift | 7 ------- .../Presentation/Scene/Login/Sub/SubLoginController.swift | 5 ----- .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 7 ------- 4 files changed, 25 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift index df5d00de..b8824c0d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift @@ -59,12 +59,6 @@ private extension LoginController { // MARK: - Methods extension LoginController { func bind(reactor: Reactor) { - - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - mainView.guestButton.rx.tap .withUnretained(self) .map { (owner, _) in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index 1ae1b5fd..e4c22d6e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -12,7 +12,6 @@ final class LoginReactor: Reactor { case kakaoButtonTapped(controller: BaseViewController) case appleButtonTapped(controller: BaseViewController) case guestButtonTapped(controller: BaseViewController) - case viewWillAppear case inquiryButtonTapped(controller: BaseViewController) } @@ -20,7 +19,6 @@ final class LoginReactor: Reactor { case moveToSignUpScene(controller: BaseViewController) case moveToHomeScene(controller: BaseViewController) case loadView - case resetService case moveToInquiryScene(controller: BaseViewController) } @@ -64,8 +62,6 @@ final class LoginReactor: Reactor { _ = keyChainService.deleteToken(type: .accessToken) _ = keyChainService.deleteToken(type: .refreshToken) return Observable.just(.moveToHomeScene(controller: controller)) - case .viewWillAppear: - return Observable.just(.resetService) case .inquiryButtonTapped(let controller): return Observable.just(.moveToInquiryScene(controller: controller)) } @@ -86,9 +82,6 @@ final class LoginReactor: Reactor { controller.view.window?.rootViewController = homeTabbar case .loadView: break - case .resetService: break -// authrizationCode = nil -// appleLoginService = AppleLoginService() case .moveToInquiryScene(let controller): let nextController = FAQController() nextController.reactor = FAQReactor() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift index a377e56b..37e32b43 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift @@ -58,11 +58,6 @@ private extension SubLoginController { // MARK: - Methods extension SubLoginController { func bind(reactor: Reactor) { - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear} - .bind(to: reactor.action) - .disposed(by: disposeBag) - mainView.xmarkButton.rx.tap .withUnretained(self) .map { (owner, _) in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index fe7b6438..8b194b1b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -12,7 +12,6 @@ final class SubLoginReactor: Reactor { case kakaoButtonTapped(controller: BaseViewController) case appleButtonTapped(controller: BaseViewController) case xmarkButtonTapped(controller: BaseViewController) - case viewWillAppear case inquiryButtonTapped(controller: BaseViewController) } @@ -20,7 +19,6 @@ final class SubLoginReactor: Reactor { case moveToSignUpScene(controller: BaseViewController) case dismissScene(controller: BaseViewController) case loadView - case resetService case moveToInquiryScene(controller: BaseViewController) } @@ -61,8 +59,6 @@ final class SubLoginReactor: Reactor { return loginWithApple(controller: controller) case .xmarkButtonTapped(let controller): return Observable.just(.dismissScene(controller: controller)) - case .viewWillAppear: - return Observable.just(.resetService) case .inquiryButtonTapped(let controller): return Observable.just(.moveToInquiryScene(controller: controller)) } @@ -82,9 +78,6 @@ final class SubLoginReactor: Reactor { controller.dismiss(animated: true) case .loadView: break - case .resetService: break -// authrizationCode = nil -// appleLoginService = AppleLoginService() case .moveToInquiryScene(let controller): let nextController = FAQController() nextController.reactor = FAQReactor() From 4a00617b66d753d483808ad7e476e9a2ae0c2baa Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 27 Apr 2025 16:50:28 +0900 Subject: [PATCH 189/393] =?UTF-8?q?style/#126:=20View=20=EC=BB=A8=EB=B2=A4?= =?UTF-8?q?=EC=85=98=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BeforeSearch/SearchController.swift | 33 ++++- .../Search/BeforeSearch/View/SearchView.swift | 17 ++- .../Search/Main/SearchMainController.swift | 53 +++----- .../Scene/Search/Main/SearchMainView.swift | 125 +++++++++--------- 4 files changed, 116 insertions(+), 112 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 9cac2f2b..1c380b2e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -30,7 +30,10 @@ final class SearchController: BaseViewController, View { extension SearchController { override func viewDidLoad() { super.viewDidLoad() - setUp() + + self.addViews() + self.setupContstraints() + self.configureUI() } override func viewWillAppear(_ animated: Bool) { @@ -41,36 +44,50 @@ extension SearchController { // MARK: - SetUp private extension SearchController { - func setUp() { + func addViews() { + [mainView].forEach { + self.view.addSubview($0) + } + } + + func setupContstraints() { + mainView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } + + func configureUI() { if let layout = reactor?.compositionalLayout { mainView.contentCollectionView.collectionViewLayout = layout } + mainView.contentCollectionView.delegate = self mainView.contentCollectionView.dataSource = self + mainView.contentCollectionView.register( SearchTitleSectionCell.self, forCellWithReuseIdentifier: SearchTitleSectionCell.identifiers ) + mainView.contentCollectionView.register( SpacingSectionCell.self, forCellWithReuseIdentifier: SpacingSectionCell.identifiers ) + mainView.contentCollectionView.register( CancelableTagSectionCell.self, forCellWithReuseIdentifier: CancelableTagSectionCell.identifiers ) + mainView.contentCollectionView.register( SearchCountTitleSectionCell.self, forCellWithReuseIdentifier: SearchCountTitleSectionCell.identifiers ) + mainView.contentCollectionView.register( HomeCardSectionCell.self, forCellWithReuseIdentifier: HomeCardSectionCell.identifiers ) - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } } } @@ -122,8 +139,9 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource ) -> UICollectionViewCell { let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) guard let userDefaultService = reactor?.userDefaultService else { return cell } - var searchList = userDefaultService.fetchArray(key: "searchList") ?? [] + let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] guard let reactor = reactor else { return cell } + if let cell = cell as? SearchTitleSectionCell { cell.titleButton.rx.tap .map { Reactor.Action.recentSearchListAllDeleteButtonTapped } @@ -168,6 +186,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource .bind(to: reactor.action) .disposed(by: cell.disposeBag) } + return cell } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift index 874e3b1a..8ffc6413 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift @@ -12,26 +12,31 @@ import SnapKit final class SearchView: UIView { // MARK: - Components - let contentCollectionView: UICollectionView = { - return UICollectionView(frame: .zero, collectionViewLayout: .init()) - }() + let contentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) // MARK: - init init() { super.init(frame: .zero) - setUpConstraints() + + self.addSubviews() + self.setUpConstraints() } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + fatalError("\(#file), \(#function) Error") } } // MARK: - SetUp private extension SearchView { + func addSubviews() { + [contentCollectionView].forEach { + self.addSubview($0) + } + } + func setUpConstraints() { - self.addSubview(contentCollectionView) contentCollectionView.snp.makeConstraints { make in make.top.equalToSuperview().offset(56) make.leading.trailing.bottom.equalToSuperview() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift index 7eb6542e..0808019d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift @@ -9,6 +9,7 @@ import RxCocoa import RxSwift import SnapKit import Tabman +import Then final class SearchMainController: BaseTabmanController, View { @@ -19,23 +20,19 @@ final class SearchMainController: BaseTabmanController, View { private var mainView = SearchMainView() - var beforeController: SearchController = { - let controller = SearchController() - controller.reactor = SearchReactor( + var beforeController = SearchController().then { + $0.reactor = SearchReactor( userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) - return controller - }() + } - var afterController: SearchResultController = { - let controller = SearchResultController() - controller.reactor = SearchResultReactor( + var afterController = SearchResultController().then { + $0.reactor = SearchResultReactor( userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) ) - return controller - }() + } lazy var controllers = [ beforeController, @@ -49,7 +46,9 @@ final class SearchMainController: BaseTabmanController, View { extension SearchMainController { override func viewDidLoad() { super.viewDidLoad() - setUp() + + self.addViews() + self.setupContstraints() } override func viewDidAppear(_ animated: Bool) { @@ -68,14 +67,19 @@ extension SearchMainController { // MARK: - SetUp private extension SearchMainController { - func setUp() { - view.addSubview(mainView) + func addViews() { + [mainView] + .forEach { self.view.addSubview($0) } + } + + func setupContstraints() { + self.dataSource = self + self.isScrollEnabled = false + mainView.snp.makeConstraints { make in make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) make.height.equalTo(56) } - self.dataSource = self - self.isScrollEnabled = false } } @@ -98,25 +102,6 @@ extension SearchMainController { }) .disposed(by: disposeBag) -// mainView.searchTextField.rx.controlEvent(.editingDidEndOnExit) -// .withUnretained(self) -// .map { (owner, _) in -// Reactor.Action.returnSearchKeyWord(text: owner.mainView.searchTextField.text ) -// } -// .bind(to: reactor.action) -// .disposed(by: disposeBag) -// -// mainView.searchTextField.rx.controlEvent(.editingDidEndOnExit) -// .withUnretained(self) -// .subscribe(onNext: { (owner, _) in -// if let text = owner.mainView.searchTextField.text { -// if !text.isEmpty { -// owner.scrollToPage(.at(index: 1), animated: false) -// } -// } -// owner.beforeController.reactor?.action.onNext(.returnSearchKeyword(text: owner.mainView.searchTextField.text)) -// }) -// .disposed(by: disposeBag) mainView.searchTextField.rx.controlEvent(.editingDidEndOnExit) .withLatestFrom(mainView.searchTextField.rx.text.orEmpty) .withUnretained(self) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift index 3d3ffda9..8798ec65 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift @@ -1,101 +1,96 @@ -// -// SearchMainView.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit import SnapKit +import Then final class SearchMainView: UIView { // MARK: - Components - private let searchTrailingView: UIView = { - let view = UIView() - view.backgroundColor = .g50 - view.layer.cornerRadius = 4 - view.clipsToBounds = true - return view - }() - - private let searchIconImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_search_gray") - return view - }() - - private let searchStackView: UIStackView = { - let view = UIStackView() - view.spacing = 4 - view.alignment = .center - return view - }() - - let cancelButton: UIButton = { - let button = UIButton(type: .system) - button.setTitle("취소", for: .normal) - button.setTitleColor(.g1000, for: .normal) - button.titleLabel?.font = .korFont(style: .regular, size: 14) - button.imageView?.contentMode = .scaleAspectFit - return button - }() - - let searchTextField: UITextField = { - let view = UITextField() - view.font = .korFont(style: .regular, size: 14) - view.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .korFont(style: .regular, size: 14)!) - return view - }() - - let clearButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "icon_clearButton"), for: .normal) - return button - }() - - private var headerStackView: UIStackView = { - let view = UIStackView() - view.alignment = .center - view.spacing = 16 - return view - }() + private let searchTrailingView = UIView().then { + $0.backgroundColor = .g50 + $0.layer.cornerRadius = 4 + $0.clipsToBounds = true + } + + private let searchIconImageView = UIImageView().then { + $0.image = UIImage(named: "icon_search_gray") + } + + private let searchStackView = UIStackView().then { + $0.spacing = 4 + $0.alignment = .center + } + + let cancelButton = UIButton(type: .system).then { + $0.setTitle("취소", for: .normal) + $0.setTitleColor(.g1000, for: .normal) + $0.titleLabel?.font = .korFont(style: .regular, size: 14) + $0.imageView?.contentMode = .scaleAspectFit + } + + let searchTextField = UITextField().then { + $0.font = .korFont(style: .regular, size: 14) + $0.setPlaceholder( + text: "팝업스토어명을 입력해보세요", + color: .g400, + font: .korFont(style: .regular, size: 14)! + ) + } + + let clearButton = UIButton().then { + $0.setImage(UIImage(named: "icon_clearButton"), for: .normal) + } + + private var headerStackView = UIStackView().then { + $0.alignment = .center + $0.spacing = 16 + } // MARK: - init init() { super.init(frame: .zero) - setUpConstraints() + + self.addViews() + self.setUpConstraints() } required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") + fatalError("\(#file), \(#function) Error") } } // MARK: - SetUp private extension SearchMainView { + func addViews() { + [headerStackView] + .forEach { self.addSubview($0) } + + [searchTrailingView, cancelButton] + .forEach { headerStackView.addArrangedSubview($0) } + + [searchStackView] + .forEach { searchTrailingView.addSubview($0) } + + [searchIconImageView, searchTextField, clearButton] + .forEach { searchStackView.addArrangedSubview($0) } + } + func setUpConstraints() { searchTrailingView.snp.makeConstraints { make in make.height.equalTo(37) } - headerStackView.addArrangedSubview(searchTrailingView) - headerStackView.addArrangedSubview(cancelButton) - self.addSubview(headerStackView) + headerStackView.snp.makeConstraints { make in make.top.equalToSuperview().inset(7) make.leading.equalToSuperview().inset(20) make.trailing.equalToSuperview().inset(16) } - searchTrailingView.addSubview(searchStackView) + searchStackView.snp.makeConstraints { make in make.top.bottom.equalToSuperview() make.leading.trailing.equalToSuperview().inset(12) } - searchStackView.addArrangedSubview(searchIconImageView) - searchStackView.addArrangedSubview(searchTextField) - searchStackView.addArrangedSubview(clearButton) searchIconImageView.snp.makeConstraints { make in make.size.equalTo(20) From ec3f787e746a4a98a90df55b904b7279488c0786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 28 Apr 2025 18:48:41 +0900 Subject: [PATCH 190/393] =?UTF-8?q?Feat/#127:=20OSLog=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Logger/Logger.swift | 73 ++++++++++++++---- .../Marker/Marker.imageset/Contents.json | 4 +- ...353\263\265\354\202\254\353\263\270 2.png" | Bin 0 -> 1976 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 0 -> 1976 bytes .../Marker/TapMarker.imageset/Contents.json | 2 + ...353\263\265\354\202\254\353\263\270 2.png" | Bin 0 -> 2698 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 0 -> 2698 bytes 7 files changed, 64 insertions(+), 15 deletions(-) create mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" create mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" create mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" create mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270.png" diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift index 16360c15..122f328b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift @@ -1,6 +1,9 @@ import Foundation +import OSLog public struct Logger { + private static let subsystem = Bundle.main.bundleIdentifier ?? "com.poppoolIOS.poppool" + public enum Level { case info case debug @@ -42,28 +45,70 @@ public struct Logger { return "🍎" } } + + var osLogType: OSLogType { + switch self { + case .debug: + return .debug + case .info, .event: + return .info + case .network: + return .default + case .error: + return .error + case .custom: + return .default + } + } } - static var isShowFileName: Bool = false - static var isShowLine: Bool = false + static var isShowFileName: Bool = false // 파일 이름 포함여부 + static var isShowLine: Bool = true // 라인 번호 포함 여부 static var isShowLog: Bool = true + private static var loggers: [String: os.Logger] = [:] + private static func getLogger(for category: Level) -> os.Logger { + let categoryName = category.categoryName + + if let cachedLogger = loggers[categoryName] { + return cachedLogger + } + + let logger = os.Logger(subsystem: subsystem, category: categoryName) + loggers[categoryName] = logger + return logger + } + public static func log( message: Any, category: Level, - fileName: String = "Input is not found", - line: Int? = nil + fileName: String = #file, + line: Int = #line ) { - if isShowLog { - print("\(category.categoryIcon) [\(category.categoryName)]: \(message)") - if isShowFileName { - guard let fileName = fileName.components(separatedBy: "/").last else { return } - print(" \(category.categoryIcon) [FileName]: \(fileName)") - } - if isShowLine { - guard let line = line else { return } - print(" \(category.categoryIcon) [Line]: \(line)") - } + guard isShowLog else { return } + + let logger = getLogger(for: category) + var fullMessage = "\(category.categoryIcon) \(message)" + + if isShowFileName { + guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } + fullMessage += " | 📁 \(fileNameOnly)" + } + + if isShowLine { + fullMessage += " | 📍 \(line)" + } + + logger.log(level: category.osLogType, "\(fullMessage, privacy: .public)") + + // 디버깅 시 Xcode 콘솔에서도 바로 확인할 수 있도록 print도 함께 사용 불필요시 제거 + print("\(category.categoryIcon) [\(category.categoryName)]: \(message)") + if isShowFileName { + guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } + print(" \(category.categoryIcon) [FileName]: \(fileNameOnly)") + } + if isShowLine { + print(" \(category.categoryIcon) [Line]: \(line)") } } } diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/Contents.json index fb8ebfdd..083c9790 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/Contents.json @@ -1,15 +1,17 @@ { "images" : [ { - "filename" : "solid.png", + "filename" : "solid 복사본 2.png", "idiom" : "universal", "scale" : "1x" }, { + "filename" : "solid 복사본.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "solid.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" new file mode 100644 index 0000000000000000000000000000000000000000..10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe GIT binary patch literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* literal 0 HcmV?d00001 diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" new file mode 100644 index 0000000000000000000000000000000000000000..10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe GIT binary patch literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* literal 0 HcmV?d00001 diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json index fb8ebfdd..ee893b42 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json @@ -6,10 +6,12 @@ "scale" : "1x" }, { + "filename" : "solid 복사본.png", "idiom" : "universal", "scale" : "2x" }, { + "filename" : "solid 복사본 2.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" new file mode 100644 index 0000000000000000000000000000000000000000..39f13b3126d68a1cbc184d26c7b7cac06527e45b GIT binary patch literal 2698 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 Date: Tue, 29 Apr 2025 13:51:04 +0900 Subject: [PATCH 191/393] Update README.md --- README.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 96a2a2fa..88cedaba 100644 --- a/README.md +++ b/README.md @@ -1 +1,26 @@ -# iOS-renew \ No newline at end of file +

+ + # Poppool + 5 5_1 + +

+ + + + +

흩어져 있는 팝업스토어 정보를 제공하고 인스타그램 연동으로 부가 기능을 제공하는 서비스입니다. + +

+ +
+ +
+ +##  iOS Members  + +|[준영](https://github.com/dongglehada)|[기현](https://github.com/zzangzzangguy)|[영훈](https://github.com/0Hooni)| +|:-----:|:-----:|:-----:| + | | | + +
+
From 2d75d3b7b521f413ea9a91da11a22bc613cb7c76 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 21:22:45 +0900 Subject: [PATCH 192/393] =?UTF-8?q?refactor/#126:=20=EB=AA=A9=EC=A0=81?= =?UTF-8?q?=EC=9D=B4=20=EC=9E=98=20=EB=93=A4=EC=96=B4=EB=82=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Search/BeforeSearch/SearchController.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 1c380b2e..8a635377 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -23,7 +23,7 @@ final class SearchController: BaseViewController, View { private var mainView = SearchView() private var sections: [any Sectionable] = [] private let cellTapped: PublishSubject = .init() - private let pageChange: PublishSubject = .init() + private let loadNextPage = PublishSubject() } // MARK: - Life Cycle @@ -107,7 +107,7 @@ extension SearchController { .bind(to: reactor.action) .disposed(by: disposeBag) - pageChange + loadNextPage .throttle(.milliseconds(1000), scheduler: MainScheduler.asyncInstance) .map { Reactor.Action.changePage } .bind(to: reactor.action) @@ -196,7 +196,7 @@ extension SearchController: UICollectionViewDelegate, UICollectionViewDataSource let scrollViewHeight = scrollView.frame.size.height let contentOffsetY = scrollView.contentOffset.y if contentOffsetY + scrollViewHeight >= contentHeight { - pageChange.onNext(()) + loadNextPage.onNext(()) } } From b593626a6bc73f6200eed4bd75fbcf7b24ccf156 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 21:37:10 +0900 Subject: [PATCH 193/393] =?UTF-8?q?fix/#126:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EB=A7=88=EC=A7=80=EB=A7=89=20=EC=9D=B4=EB=B2=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=A9=EC=B6=9C=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 - throttle의 마지막 이벤트 방출을 추가로 해주는 latest의 기본값이 true임 - 다음 페이지를 호출하는 이벤트를 시작과 끝에 두번 호출이 되게 하면 페이지 요청이 중복적으로 들어가는 문제와 설계에 따라서는 다음 페이지까지도 불러올 수 있기에 latest 옵션을 false로 변경 --- .../Scene/Search/BeforeSearch/SearchController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 8a635377..e8b206a6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -108,7 +108,7 @@ extension SearchController { .disposed(by: disposeBag) loadNextPage - .throttle(.milliseconds(1000), scheduler: MainScheduler.asyncInstance) + .throttle(.seconds(1), latest: false, scheduler: MainScheduler.asyncInstance) .map { Reactor.Action.changePage } .bind(to: reactor.action) .disposed(by: disposeBag) From 1c72dd3a16e95239dcb3ce07fa7bfa4301cd1870 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 21:50:30 +0900 Subject: [PATCH 194/393] =?UTF-8?q?refactor/#126:=20Action=EC=9D=98=20?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EB=B0=8D=EC=9D=84=20Subject=EC=9D=98=20?= =?UTF-8?q?=EC=9D=98=EB=8F=84=EC=99=80=20=ED=86=B5=EC=9D=BC=EC=8B=9C?= =?UTF-8?q?=ED=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Search/BeforeSearch/SearchController.swift | 2 +- .../Scene/Search/BeforeSearch/SearchReactor.swift | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index e8b206a6..7e486a9b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -109,7 +109,7 @@ extension SearchController { loadNextPage .throttle(.seconds(1), latest: false, scheduler: MainScheduler.asyncInstance) - .map { Reactor.Action.changePage } + .map { Reactor.Action.loadNextPage } .bind(to: reactor.action) .disposed(by: disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 17cd6119..9685e9c0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -21,7 +21,7 @@ final class SearchReactor: Reactor { case changeCategory(categoryList: [Int64], categoryTitleList: [String?]) case categoryDelteButtonTapped(indexPath: IndexPath) case resetCategory - case changePage + case loadNextPage case bookmarkButtonTapped(indexPath: IndexPath) case resetSearchKeyWord } @@ -102,7 +102,8 @@ final class SearchReactor: Reactor { switch action { case .resetSearchKeyWord: return Observable.just(.resetSearchKeyWord) - case .changePage: + case .loadNextPage: + if isLoading { return Observable.just(.loadView) } else { From 7d82d2a902ea68af336c1b0cdf8454f4bafd949a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 22:27:59 +0900 Subject: [PATCH 195/393] =?UTF-8?q?fix/#126:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20View=20=EA=B0=B1=EC=8B=A0=EC=9D=84=20=EB=A7=89?= =?UTF-8?q?=EA=B3=A0=20=EB=A1=9C=EC=A7=81=EC=9D=84=20=EB=8B=A8=EC=88=9C?= =?UTF-8?q?=ED=99=94=EC=8B=9C=ED=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 불필요한 상황에서 모두 loadView를 mutate하고 있었음 - 아마 if state가 아닌 상황들에서 return이 없다보니 complete가 아닌 영향을 크게 주지 않는 loadView를 호출할게 아닌가 싶음. --- .../Search/BeforeSearch/SearchReactor.swift | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 9685e9c0..742d7c17 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -103,18 +103,10 @@ final class SearchReactor: Reactor { case .resetSearchKeyWord: return Observable.just(.resetSearchKeyWord) case .loadNextPage: - - if isLoading { - return Observable.just(.loadView) - } else { - if currentPage < lastPage { - isLoading = true - currentPage += 1 - return setBottomSearchList(sort: sort) - } else { - return Observable.just(.loadView) - } - } + guard !isLoading, currentPage < lastPage else { return Observable.empty() } + isLoading = true + currentPage += 1 + return setBottomSearchList(sort: sort) case .viewWillAppear: setSearchList() return setBottomSearchList(sort: sort) From 5996f6e55a08f68553c5c7469f336b12d7142f14 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 23:43:39 +0900 Subject: [PATCH 196/393] =?UTF-8?q?refactor/#126:=20=EA=B0=80=EB=8F=85?= =?UTF-8?q?=EC=84=B1=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Search/BeforeSearch/SearchReactor.swift | 73 ++++++++++--------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 742d7c17..064472b5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -300,14 +300,37 @@ final class SearchReactor: Reactor { func setBottomSearchList(sort: String?) -> Observable { let isOpen = filterIndex == 0 ? true : false - let categorys = searchCategorySection.inputDataList.compactMap { $0.id } - return popUpAPIUseCase.getSearchBottomPopUpList(isOpen: isOpen, categories: categorys, page: currentPage, size: 50, sort: sort) - .withUnretained(self) - .map { (owner, response) in - let isLogin = response.loginYn - if owner.currentPage == 0 { - owner.searchListSection.inputDataList = response.popUpStoreList.map { - return .init( + let categories = searchCategorySection.inputDataList.compactMap { $0.id } + + return popUpAPIUseCase.getSearchBottomPopUpList( + isOpen: isOpen, + categories: categories, + page: currentPage, + size: 50, + sort: sort + ) + .withUnretained(self) + .map { (owner, response) in + let isLogin = response.loginYn + if owner.currentPage == 0 { + owner.searchListSection.inputDataList = response.popUpStoreList.map { + return .init( + imagePath: $0.mainImageUrl, + id: $0.id, + category: $0.category, + title: $0.name, + address: $0.address, + startDate: $0.startDate, + endDate: $0.endDate, + isBookmark: $0.bookmarkYn, + isLogin: isLogin + ) + } + } else { + if owner.currentPage != owner.lastAppendPage { + owner.lastAppendPage = owner.currentPage + let newData = response.popUpStoreList.map { + return HomeCardSectionCell.Input( imagePath: $0.mainImageUrl, id: $0.id, category: $0.category, @@ -319,32 +342,16 @@ final class SearchReactor: Reactor { isLogin: isLogin ) } - } else { - if owner.currentPage != owner.lastAppendPage { - owner.lastAppendPage = owner.currentPage - let newData = response.popUpStoreList.map { - return HomeCardSectionCell.Input( - imagePath: $0.mainImageUrl, - id: $0.id, - category: $0.category, - title: $0.name, - address: $0.address, - startDate: $0.startDate, - endDate: $0.endDate, - isBookmark: $0.bookmarkYn, - isLogin: isLogin - ) - } - owner.searchListSection.inputDataList.append(contentsOf: newData) - } + owner.searchListSection.inputDataList.append(contentsOf: newData) } - let isOpenString = isOpen ? "오픈・" : "종료・" - let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" - let sortedTitle = isOpenString + sortedString - owner.searchSortedSection.inputDataList = [.init(count: response.totalElements, sortedTitle: sortedTitle)] - owner.lastPage = response.totalPages - owner.isLoading = false - return .loadView } + let isOpenString = isOpen ? "오픈・" : "종료・" + let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" + let sortedTitle = isOpenString + sortedString + owner.searchSortedSection.inputDataList = [.init(count: response.totalElements, sortedTitle: sortedTitle)] + owner.lastPage = response.totalPages + owner.isLoading = false + return .loadView + } } } From b85b113fb72247428bde5180f3f5c4255e36ab44 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 29 Apr 2025 23:57:58 +0900 Subject: [PATCH 197/393] =?UTF-8?q?fix/#126:=20=ED=95=9C=EB=B2=88=EC=9D=98?= =?UTF-8?q?=20=EB=A1=9C=EB=94=A9=EC=97=90=20=EB=84=88=EB=AC=B4=20=EB=A7=8E?= =?UTF-8?q?=EC=9D=80=20=EC=95=84=EC=9D=B4=ED=85=9C=EC=9D=84=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=EC=9D=84=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 - 50개나 요청할 필요가 없다 생각했음 - 한번에 보이는 팝업의 수량이 그리 많지 않았음 - 이미 충분히 카테고리와 정렬로 최상단의 정보의 중요도가 더 높다 판단했음 - 고로 그 아래 50개씩이나 불러올 이유는 없다고 보였음 --- .../Presentation/Scene/Search/BeforeSearch/SearchReactor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 064472b5..3966075a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -306,7 +306,7 @@ final class SearchReactor: Reactor { isOpen: isOpen, categories: categories, page: currentPage, - size: 50, + size: 10, sort: sort ) .withUnretained(self) From 6d0b611c5093c71473f4027e7f537fe99f62ca26 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 30 Apr 2025 00:00:19 +0900 Subject: [PATCH 198/393] =?UTF-8?q?refactor/#126:=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=ED=83=80=EC=9E=85=EC=9D=84=20=EB=AA=85?= =?UTF-8?q?=EC=8B=9C=ED=95=B4=EC=A4=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 단순히 .init으로 설정해주면 return이 뭘로 되어있는지 확인을 해줘야되는 번거로움이 있음 --- .../Presentation/Scene/Search/BeforeSearch/SearchReactor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 3966075a..f878376e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -314,7 +314,7 @@ final class SearchReactor: Reactor { let isLogin = response.loginYn if owner.currentPage == 0 { owner.searchListSection.inputDataList = response.popUpStoreList.map { - return .init( + return HomeCardSectionCell.Input( imagePath: $0.mainImageUrl, id: $0.id, category: $0.category, From 6497a30b6cbca74ac7a16ada1c931ca743da726b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 30 Apr 2025 00:38:58 +0900 Subject: [PATCH 199/393] =?UTF-8?q?refactor/#126:=20=EC=83=88=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=EC=9D=84=20=EC=A4=91=EB=B3=B5=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=83=9D=EC=84=B1=ED=95=B4=EC=A3=BC=EB=8A=94=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EB=A5=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 한번 생성자로 만들어준 아이템은 저장하도록 함 - 이후 조건에 따라 아이템으로 밀어줄건지 추가해줄건지만 정함 --- .../Search/BeforeSearch/SearchReactor.swift | 51 ++++++++----------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index f878376e..c3a1369b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -312,43 +312,32 @@ final class SearchReactor: Reactor { .withUnretained(self) .map { (owner, response) in let isLogin = response.loginYn + let newItems = response.popUpStoreList.map { + HomeCardSectionCell.Input( + imagePath: $0.mainImageUrl, + id: $0.id, + category: $0.category, + title: $0.name, + address: $0.address, + startDate: $0.startDate, + endDate: $0.endDate, + isBookmark: $0.bookmarkYn, + isLogin: isLogin + ) + } + if owner.currentPage == 0 { - owner.searchListSection.inputDataList = response.popUpStoreList.map { - return HomeCardSectionCell.Input( - imagePath: $0.mainImageUrl, - id: $0.id, - category: $0.category, - title: $0.name, - address: $0.address, - startDate: $0.startDate, - endDate: $0.endDate, - isBookmark: $0.bookmarkYn, - isLogin: isLogin - ) - } - } else { - if owner.currentPage != owner.lastAppendPage { - owner.lastAppendPage = owner.currentPage - let newData = response.popUpStoreList.map { - return HomeCardSectionCell.Input( - imagePath: $0.mainImageUrl, - id: $0.id, - category: $0.category, - title: $0.name, - address: $0.address, - startDate: $0.startDate, - endDate: $0.endDate, - isBookmark: $0.bookmarkYn, - isLogin: isLogin - ) - } - owner.searchListSection.inputDataList.append(contentsOf: newData) - } + owner.searchListSection.inputDataList = newItems + } else if owner.currentPage != owner.lastAppendPage { + owner.lastAppendPage = owner.currentPage + owner.searchListSection.inputDataList.append(contentsOf: newItems) } + let isOpenString = isOpen ? "오픈・" : "종료・" let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" let sortedTitle = isOpenString + sortedString owner.searchSortedSection.inputDataList = [.init(count: response.totalElements, sortedTitle: sortedTitle)] + owner.lastPage = response.totalPages owner.isLoading = false return .loadView From d4c7a81de8d4516d405bb7dcddfea309f390b9a4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 30 Apr 2025 00:46:18 +0900 Subject: [PATCH 200/393] =?UTF-8?q?refactor/#126:=20=EC=83=88=EB=A1=9C=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=EB=90=98=EB=8A=94=20Cell.Input=EC=9E=84?= =?UTF-8?q?=EC=9D=84=20=EB=AA=85=EC=8B=9C=ED=95=98=EB=8F=84=EB=A1=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 --- .../Scene/Search/BeforeSearch/SearchReactor.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index c3a1369b..b11fa24f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -325,7 +325,7 @@ final class SearchReactor: Reactor { isLogin: isLogin ) } - + if owner.currentPage == 0 { owner.searchListSection.inputDataList = newItems } else if owner.currentPage != owner.lastAppendPage { @@ -336,7 +336,13 @@ final class SearchReactor: Reactor { let isOpenString = isOpen ? "오픈・" : "종료・" let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" let sortedTitle = isOpenString + sortedString - owner.searchSortedSection.inputDataList = [.init(count: response.totalElements, sortedTitle: sortedTitle)] + + owner.searchSortedSection.inputDataList = [ + SearchCountTitleSectionCell.Input( + count: response.totalElements, + sortedTitle: sortedTitle + ) + ] owner.lastPage = response.totalPages owner.isLoading = false From a04857fa4d1b6d87bd3922f8ed11c3624b9b4c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 30 Apr 2025 20:00:34 +0900 Subject: [PATCH 201/393] =?UTF-8?q?feat/#127:=20logLevel=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20,=20=EB=A1=9C=EA=B1=B0=20=ED=82=A4=20enum=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20print=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=A0=9C=EA=B1=B0=20=EB=A1=9C=EA=B1=B0?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=8B=9C=20message=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 4 + .../Infrastructure/Logger/Logger.swift | 71 ++-- .../Service/KeyChainService.swift | 6 +- .../PreSignedAPI/PreSignedAPIEndPoint.swift | 6 +- .../Data/Network/Common/Requestable.swift | 2 +- .../Network/EndPoint/MultipartEndPoint.swift | 4 +- .../Interceptor/TokenInterceptor.swift | 4 +- .../Data/Network/Provider/ProviderImpl.swift | 57 +--- .../Network/Service/AppleLoginService.swift | 8 +- .../Network/Service/KakaoLoginService.swift | 6 +- .../Network/Service/PreSignedService.swift | 48 +-- .../RepositoryImpl/MapRepositoryImpl.swift | 6 +- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 12 +- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 8 +- Poppool/Poppool.xcodeproj/project.pbxproj | 1 - .../xcshareddata/xcschemes/Poppool.xcscheme | 4 +- .../Marker/Marker.imageset/Contents.json | 4 +- ...353\263\265\354\202\254\353\263\270 2.png" | Bin 1976 -> 0 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 1976 -> 0 bytes .../Marker/Marker.imageset/solid.png | Bin 1976 -> 0 bytes .../Marker/Marker.imageset/solid.svg | 4 + .../Marker/TapMarker.imageset/Contents.json | 4 +- ...353\263\265\354\202\254\353\263\270 2.png" | Bin 2698 -> 0 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 2698 -> 0 bytes .../Marker/TapMarker.imageset/solid.png | Bin 2698 -> 0 bytes .../Marker/TapMarker.imageset/solid.svg | 4 + .../AdminBottomSheetView.swift | 4 +- .../AdminBottomSheetViewController.swift | 8 +- .../Scene/Admin/AdminReactor.swift | 4 +- .../PopUpStoreRegisterReactor.swift | 48 +-- .../Scene/Admin/AdminStoreCell.swift | 4 +- .../Scene/Admin/AdminViewController.swift | 2 +- .../CommentDetail/CommentDetailReactor.swift | 4 +- .../CommentList/CommentListReactor.swift | 2 +- .../NormalCommentAddReactor.swift | 4 +- .../NormalCommentEditReactor.swift | 4 +- .../Scene/Detail/DetailReactor.swift | 10 +- .../BalloonBackgroundView.swift | 16 +- .../FillterSheetView/BalloonChipCell.swift | 4 +- .../Map/FillterSheetView/FilterChip.swift | 2 +- .../FullScreenMapViewController.swift | 4 +- .../MapGuideView/MapGuideReactor.swift | 4 +- .../Scene/Map/MapView/MapMarker.swift | 55 ++- .../Scene/Map/MapView/MapReactor.swift | 6 +- .../Scene/Map/MapView/MapViewController.swift | 313 ++++-------------- .../Map/StoreListView/StoreListReactor.swift | 4 +- .../Main/ProfileEditController.swift | 2 +- .../Utills/Common/ClusteringManager.swift | 4 +- .../Controllers/BaseTabmanController.swift | 4 +- .../Controllers/BaseViewController.swift | 4 +- .../SectionSupplementaryItem.swift | 2 +- .../Interfaces/Sectionable/Sectionable.swift | 4 +- 52 files changed, 301 insertions(+), 484 deletions(-) delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270.png" delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid.svg diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index d5ab556c..75cc80c7 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D62DB67B4F00B141FF /* RxSwift */; }; 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; + 4EBC91D32DB8039800495C3B /* OSLog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EBC91D22DB8039800495C3B /* OSLog.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -27,6 +28,7 @@ /* Begin PBXFileReference section */ 058CC9182DB5383C0084221A /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4EBC91D22DB8039800495C3B /* OSLog.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OSLog.framework; path = System/Library/Frameworks/OSLog.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -42,6 +44,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4EBC91D32DB8039800495C3B /* OSLog.framework in Frameworks */, 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */, 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, @@ -54,6 +57,7 @@ 05125B6A2DB56C32001342A2 /* Frameworks */ = { isa = PBXGroup; children = ( + 4EBC91D22DB8039800495C3B /* OSLog.framework */, ); name = Frameworks; sourceTree = ""; diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift index 122f328b..977637ac 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift @@ -4,13 +4,13 @@ import OSLog public struct Logger { private static let subsystem = Bundle.main.bundleIdentifier ?? "com.poppoolIOS.poppool" - public enum Level { + public enum Level: Hashable { case info case debug case network case error case event - case custom(categoryName: String) + case custom(name: String) var categoryName: String { switch self { @@ -24,8 +24,7 @@ public struct Logger { return "Error" case .event: return "Event" - case .custom(let categoryName): - return categoryName + case .custom(let name): return name } } @@ -45,70 +44,64 @@ public struct Logger { return "🍎" } } + } + + public enum LogLevel { + case debug + case info + case error + case fault var osLogType: OSLogType { switch self { case .debug: return .debug - case .info, .event: + case .info: return .info - case .network: - return .default case .error: return .error - case .custom: - return .default + case .fault: + return .fault } } } - static var isShowFileName: Bool = false // 파일 이름 포함여부 - static var isShowLine: Bool = true // 라인 번호 포함 여부 - static var isShowLog: Bool = true + /// : 아래 옵션 주석 해제시 파일명/라인 번호를 로그 메시지에 포함 + // private static var isShowFileName: Bool = false // 파일 이름 포함 여부 + // private static var isShowLine: Bool = true // 라인 번호 포함 여부 + private static var isShowLog: Bool = true - private static var loggers: [String: os.Logger] = [:] + private static var loggers: [Level: os.Logger] = [:] private static func getLogger(for category: Level) -> os.Logger { let categoryName = category.categoryName - if let cachedLogger = loggers[categoryName] { + if let cachedLogger = loggers[category] { return cachedLogger } let logger = os.Logger(subsystem: subsystem, category: categoryName) - loggers[categoryName] = logger + loggers[category] = logger return logger } + /// : 파일명과 라인 정보 파라미터 포함 + // public static func log( + // _ message: Any, + // category: Level, + // level: LogLevel = .info, + // fileName: String = #file, + // line: Int = #line + // ) { public static func log( - message: Any, + _ message: Any, category: Level, - fileName: String = #file, - line: Int = #line + level: LogLevel = .info ) { guard isShowLog else { return } let logger = getLogger(for: category) - var fullMessage = "\(category.categoryIcon) \(message)" + let fullMessage = "\(category.categoryIcon) \(message)" - if isShowFileName { - guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } - fullMessage += " | 📁 \(fileNameOnly)" - } - - if isShowLine { - fullMessage += " | 📍 \(line)" - } - - logger.log(level: category.osLogType, "\(fullMessage, privacy: .public)") - - // 디버깅 시 Xcode 콘솔에서도 바로 확인할 수 있도록 print도 함께 사용 불필요시 제거 - print("\(category.categoryIcon) [\(category.categoryName)]: \(message)") - if isShowFileName { - guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } - print(" \(category.categoryIcon) [FileName]: \(fileNameOnly)") - } - if isShowLine { - print(" \(category.categoryIcon) [Line]: \(line)") - } + logger.log(level: level.osLogType, "\(fullMessage, privacy: .public)") } } diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index bc44ded4..1c728116 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -43,7 +43,7 @@ public final class KeyChainService { if let data = dataTypeRef as? Data { if let value = String(data: data, encoding: .utf8) { Logger.log( - message: "Successfully fetched \(type.rawValue) from KeyChain: \(value)", + "Successfully fetched \(type.rawValue) from KeyChain", category: .info, fileName: #file, line: #line @@ -84,7 +84,7 @@ public final class KeyChainService { let status = SecItemAdd(keyChainQuery, nil) if status == errSecSuccess { Logger.log( - message: "Successfully saved \(type.rawValue) to KeyChain: \(value)", + "Successfully fetched \(type.rawValue) from KeyChain: \(value)", category: .info, fileName: #file, line: #line @@ -111,7 +111,7 @@ public final class KeyChainService { if status == errSecSuccess { Logger.log( - message: "Successfully deleted \(type.rawValue) from KeyChain", + "Successfully deleted \(type.rawValue) from KeyChain", category: .info, fileName: #file, line: #line diff --git a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift index 21d930f2..47bd16a8 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift @@ -4,7 +4,7 @@ import Infrastructure struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { - Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned URL 생성 - Request: \(request)", category: .debug) return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/upload-preSignedUrl", @@ -14,7 +14,7 @@ struct PreSignedAPIEndPoint { } static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint { - Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned Download URL 생성 - Request: \(request)", category: .debug) return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/download-preSignedUrl", @@ -24,7 +24,7 @@ struct PreSignedAPIEndPoint { } static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint { - Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned Delete 생성 - Request: \(request)", category: .debug) return RequestEndpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/delete", diff --git a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift index 2709e94c..847e264d 100644 --- a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift +++ b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift @@ -21,7 +21,7 @@ extension Requestable { let url = try url() Logger.log( - message: "\(url) URL 생성", + "\(url) URL 생성", category: .network, fileName: #file, line: #line diff --git a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift index 00a5bf3c..4c9e7011 100644 --- a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift @@ -34,7 +34,7 @@ public class MultipartEndPoint: URLRequestConvertible { public func asURLRequest() throws -> URLRequest { let url = try baseURL.asURL().appendingPathComponent(path) var request = URLRequest(url: url) - Logger.log(message: "\(request) URL 생성", category: .network) + Logger.log("\(request) URL 생성", category: .network) request.method = method if let headers = headers { @@ -57,7 +57,7 @@ public class MultipartEndPoint: URLRequestConvertible { multipartFormData.append(jsonString.data(using: .utf8)!, withName: "data") } } catch { - Logger.log(message: "JSON 변환 오류: \(error)", category: .network) + Logger.log("JSON 변환 오류: \(error)", category: .network) } } diff --git a/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift b/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift index 5dc2418b..2f1c68dc 100644 --- a/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift +++ b/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift @@ -11,7 +11,7 @@ final class TokenInterceptor: RequestInterceptor { _ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - Logger.log(message: "TokenInterceptor Adapt Token", category: .network) + Logger.log("TokenInterceptor Adapt Token", category: .network) @Dependency var keyChainService: KeyChainService var urlRequest = urlRequest let accessTokenResult = keyChainService.fetchToken(type: .accessToken) @@ -32,7 +32,7 @@ final class TokenInterceptor: RequestInterceptor { dueTo error: any Error, completion: @escaping (RetryResult) -> Void ) { - Logger.log(message: "TokenInterceptor Retry Start", category: .network) + Logger.log("TokenInterceptor Retry Start", category: .network) completion(.doNotRetry) } } diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index e3d925ed..c0909032 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -22,22 +22,13 @@ public final class ProviderImpl: Provider { /// 1) endpoint -> urlRequest 생성 let urlRequest = try endpoint.getUrlRequest() - Logger.log( - message: """ - [Provider] 최종 요청 URL: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - - Headers: \(urlRequest.allHTTPHeaderFields ?? [:]) - 요청 시각: \(Date()) - """, - category: .debug - ) + let request = AF.request(urlRequest, interceptor: interceptor) .validate() .responseData { [weak self] response in Logger.log( - message: """ + """ [Provider] 응답 수신: - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - 응답 시각: \(Date()) @@ -63,14 +54,14 @@ public final class ProviderImpl: Provider { observer.onCompleted() } catch { Logger.log( - message: "디코딩 실패: \(error.localizedDescription)", + "디코딩 실패: \(error.localizedDescription)", category: .error ) observer.onError(NetworkError.decodeError) } case .failure(let error): - Logger.log(message: "요청 실패 Error:\(error)", category: .error) + Logger.log("요청 실패 Error:\(error)", category: .error) observer.onError(error) } } @@ -80,7 +71,7 @@ public final class ProviderImpl: Provider { } } catch { - Logger.log(message: "[Provider] URLRequest 생성 실패: \(error.localizedDescription)", category: .error) + Logger.log("[Provider] URLRequest 생성 실패: \(error.localizedDescription)", category: .error) observer.onError(NetworkError.urlRequest(error)) return Disposables.create() } @@ -101,19 +92,10 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.getUrlRequest() - Logger.log( - message: """ - [Provider] 최종 요청 URL(Completable): - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - 요청 시각: \(Date()) - """, - category: .debug - ) self.executeRequest(urlRequest, interceptor: interceptor) { response in Logger.log( - message: "응답 시각 :\(Date())", + "응답 시각 :\(Date())", category: .network ) @@ -132,12 +114,12 @@ public final class ProviderImpl: Provider { case .success: observer(.completed) case .failure(let error): - Logger.log(message: "요청 실패 Error:\(error)", category: .error) + Logger.log("요청 실패 Error:\(error)", category: .error) observer(.error(self.handleRequestError(response: response, error: error))) } } } catch { - Logger.log(message: "[Provider] URLRequest 생성 실패 (Completable): \(error.localizedDescription)", category: .error) + Logger.log("[Provider] URLRequest 생성 실패 (Completable): \(error.localizedDescription)", category: .error) observer(.error(NetworkError.urlRequest(error))) } @@ -158,23 +140,16 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.asURLRequest() - Logger.log( - message: """ - [Provider] 이미지 업로드 요청: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - """, - category: .network - ) + AF.upload(multipartFormData: { multipartFormData in request.asMultipartFormData(multipartFormData: multipartFormData) - Logger.log(message: "업로드 시각 :\(Date())", category: .network) + Logger.log("업로드 시각 :\(Date())", category: .network) }, with: urlRequest, interceptor: interceptor) .validate() .response { response in Logger.log( - message: "이미지 업로드 응답 시각 :\(Date())", + "이미지 업로드 응답 시각 :\(Date())", category: .network ) switch response.result { @@ -200,15 +175,7 @@ private extension ProviderImpl { interceptor: RequestInterceptor?, completion: @escaping (AFDataResponse) -> Void ) { - // 여기서도 최종 URL 찍을 수 있음 - Logger.log( - message: """ - [Provider] executeRequest: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - 요청 시각: \(Date()) - """, - category: .debug - ) + AF.request(urlRequest, interceptor: interceptor) .validate() diff --git a/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift index 18458103..4339955f 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift @@ -37,7 +37,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi let windowSecne = scenes.first as? UIWindowScene guard let window = windowSecne?.windows.first else { Logger.log( - message: "\(#function) UIWindow fetch Fail", + "\(#function) UIWindow fetch Fail", category: .error, fileName: #file, line: #line @@ -71,8 +71,8 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi guard let convertAuthorizationCode = String(data: authorizationCode, encoding: .utf8) else { return } - Logger.log(message: "IDToken: \(idToken)", category: .info) - Logger.log(message: "Auth Code: \(convertAuthorizationCode)", category: .info) + Logger.log("IDToken: \(idToken)", category: .info) + Logger.log("Auth Code: \(convertAuthorizationCode)", category: .info) authServiceResponse.onNext(.init(idToken: idToken, authorizationCode: convertAuthorizationCode)) default: break @@ -84,7 +84,7 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi didCompleteWithError error: Error ) { Logger.log( - message: "AppleLogin Fail", + "AppleLogin Fail", category: .error, fileName: #file, line: #line diff --git a/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift index 634cf838..8d66e896 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift @@ -16,7 +16,7 @@ public final class KakaoLoginService: AuthServiceable { UserApi.shared.unlink { error in if let error = error { observer.onNext(()) - Logger.log(message: error.localizedDescription, category: .error) + Logger.log(error.localizedDescription, category: .error) } else { observer.onNext(()) observer.onCompleted() @@ -31,7 +31,7 @@ public final class KakaoLoginService: AuthServiceable { return Observable.create { [weak self] observer in guard let self else { Logger.log( - message: "KakaoTalk login Error", + "KakaoTalk login Error", category: .error, fileName: #file, line: #line @@ -42,7 +42,7 @@ public final class KakaoLoginService: AuthServiceable { // 카카오톡 설치 유무 확인 guard UserApi.isKakaoTalkLoginAvailable() else { Logger.log( - message: "KakaoTalk is not install", + "KakaoTalk is not install", category: .error, fileName: #file, line: #line diff --git a/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift index 3609bc2b..017c76c4 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift @@ -29,20 +29,20 @@ class PreSignedService { } func tryUpload(datas: [PresignedURLRequest]) -> Single { - Logger.log(message: "tryUpload 호출됨 - 요청 데이터 수: \(datas.count)", category: .debug) + Logger.log("tryUpload 호출됨 - 요청 데이터 수: \(datas.count)", category: .debug) return Single.create { [weak self] observer in - Logger.log(message: "tryUpload 내부 흐름 시작", category: .debug) + Logger.log("tryUpload 내부 흐름 시작", category: .debug) guard let self = self else { - Logger.log(message: "self가 nil입니다. 작업을 중단합니다.", category: .error) + Logger.log("self가 nil입니다. 작업을 중단합니다.", category: .error) return Disposables.create() } // 1. 업로드 링크 요청 self.getUploadLinks(request: .init(objectKeyList: datas.map { $0.filePath })) .subscribe { response in - Logger.log(message: "getUploadLinks 성공: \(response.preSignedUrlList)", category: .debug) + Logger.log("getUploadLinks 성공: \(response.preSignedUrlList)", category: .debug) let responseList = response.preSignedUrlList let inputList = datas @@ -51,23 +51,23 @@ class PreSignedService { let requestList = zip(responseList, inputList).compactMap { zipResponse in let urlResponse = zipResponse.0 let inputResponse = zipResponse.1 - Logger.log(message: "업로드 준비 - URL: \(urlResponse.preSignedUrl)", category: .debug) + Logger.log("업로드 준비 - URL: \(urlResponse.preSignedUrl)", category: .debug) return self.uploadFromS3(url: urlResponse.preSignedUrl, image: inputResponse.image) } // 3. 병렬 업로드 실행 Single.zip(requestList) .subscribe(onSuccess: { _ in - Logger.log(message: "모든 이미지 업로드 성공", category: .info) + Logger.log("모든 이미지 업로드 성공", category: .info) observer(.success(())) }, onFailure: { error in - Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) + Logger.log("이미지 업로드 실패: \(error.localizedDescription)", category: .error) observer(.failure(error)) }) .disposed(by: self.disposeBag) } onError: { error in - Logger.log(message: "getUploadLinks 실패: \(error.localizedDescription)", category: .error) + Logger.log("getUploadLinks 실패: \(error.localizedDescription)", category: .error) observer(.failure(error)) } .disposed(by: self.disposeBag) @@ -123,16 +123,16 @@ class PreSignedService { return filePaths.compactMap { imageMap[$0] } } .subscribe(onSuccess: { sortedImages in - Logger.log(message: "All images downloaded successfully", category: .info) + Logger.log("All images downloaded successfully", category: .info) observer(.success(sortedImages)) }, onFailure: { error in - Logger.log(message: "Image download failed: \(error.localizedDescription)", category: .error) + Logger.log("Image download failed: \(error.localizedDescription)", category: .error) observer(.failure(error)) }) .disposed(by: self.disposeBag) } onError: { error in - Logger.log(message: "getDownloadLinks Fail: \(error.localizedDescription)", category: .error) + Logger.log("getDownloadLinks Fail: \(error.localizedDescription)", category: .error) observer(.failure(error)) } .disposed(by: disposeBag) @@ -148,7 +148,7 @@ private extension PreSignedService { return Single.create { single in if let imageData = image.jpegData(compressionQuality: 0), let url = URL(string: url) { - Logger.log(message: "S3 업로드 요청 URL: \(url.absoluteString)", category: .debug) + Logger.log("S3 업로드 요청 URL: \(url.absoluteString)", category: .debug) let headers: HTTPHeaders = [ "Content-Type": "image/jpeg" @@ -156,19 +156,19 @@ private extension PreSignedService { AF.upload(imageData, to: url, method: .put, headers: headers) .response { response in - Logger.log(message: "S3 업로드 응답 상태: \(response.response?.statusCode ?? -1)", category: .debug) + Logger.log("S3 업로드 응답 상태: \(response.response?.statusCode ?? -1)", category: .debug) switch response.result { case .success: - Logger.log(message: "S3 업로드 성공 - URL: \(url.absoluteString)", category: .info) + Logger.log("S3 업로드 성공 - URL: \(url.absoluteString)", category: .info) single(.success(())) case .failure(let error): - Logger.log(message: "S3 업로드 실패: \(error.localizedDescription)", category: .error) + Logger.log("S3 업로드 실패: \(error.localizedDescription)", category: .error) single(.failure(error)) } } return Disposables.create() } else { - Logger.log(message: "S3 업로드 실패 - 잘못된 URL 또는 데이터", category: .error) + Logger.log("S3 업로드 실패 - 잘못된 URL 또는 데이터", category: .error) single(.failure(NSError(domain: "InvalidDataOrURL", code: -1, userInfo: nil))) return Disposables.create() } @@ -197,13 +197,13 @@ private extension PreSignedService { } func getUploadLinks(request: PresignedURLRequestDTO) -> Observable { - Logger.log(message: "Presigned URL 생성 요청 데이터: \(request)", category: .debug) + Logger.log("Presigned URL 생성 요청 데이터: \(request)", category: .debug) let endPoint = PreSignedAPIEndPoint.presigned_upload(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) .do(onNext: { response in - Logger.log(message: "Presigned URL 응답 데이터: \(response.preSignedUrlList)", category: .debug) + Logger.log("Presigned URL 응답 데이터: \(response.preSignedUrlList)", category: .debug) }, onError: { error in - Logger.log(message: "Presigned URL 요청 실패: \(error.localizedDescription)", category: .error) + Logger.log("Presigned URL 요청 실패: \(error.localizedDescription)", category: .error) }) } @@ -214,18 +214,18 @@ private extension PreSignedService { } extension PreSignedService { func deleteImage(filePath: String, completion: @escaping (Result) -> Void) { - Logger.log(message: "이미지 삭제 시작 - 경로: \(filePath)", category: .debug) + Logger.log("이미지 삭제 시작 - 경로: \(filePath)", category: .debug) let request = PresignedURLRequestDTO(objectKeyList: [filePath]) tryDelete(targetPaths: request) .subscribe( onCompleted: { - Logger.log(message: "이미지 삭제 성공: \(filePath)", category: .debug) + Logger.log("이미지 삭제 성공: \(filePath)", category: .debug) completion(.success(())) }, onError: { error in - Logger.log(message: "이미지 삭제 실패: \(error.localizedDescription)", category: .error) + Logger.log("이미지 삭제 실패: \(error.localizedDescription)", category: .error) completion(.failure(error)) } ) @@ -285,12 +285,12 @@ extension PreSignedService { guard let encodedPath = filePath .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)? .replacingOccurrences(of: "+", with: "%2B") else { - Logger.log(message: "URL 인코딩 실패: \(filePath)", category: .error) + Logger.log("URL 인코딩 실패: \(filePath)", category: .error) return nil } let fullString = baseURL + encodedPath - Logger.log(message: "생성된 URL: \(fullString)", category: .debug) + Logger.log("생성된 URL: \(fullString)", category: .debug) return URL(string: fullString) } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 236aad21..74f1b2ea 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -48,7 +48,7 @@ public final class MapRepositoryImpl: MapRepository { } public func fetchCategories() -> Observable<[CategoryResponse]> { - Logger.log(message: "카테고리 목록 요청을 시작합니다.", category: .network) + Logger.log("카테고리 목록 요청을 시작합니다.", category: .network) return provider.requestData( with: SignUpAPIEndpoint.signUp_getCategoryList(), @@ -56,7 +56,7 @@ public final class MapRepositoryImpl: MapRepository { ) .do(onNext: { _ in Logger.log( - message: "카테고리 목록 응답 성공", + "카테고리 목록 응답 성공", category: .debug ) }) @@ -65,7 +65,7 @@ public final class MapRepositoryImpl: MapRepository { } .catch { error in Logger.log( - message: "카테고리 목록 요청 실패: \(error.localizedDescription)", + "카테고리 목록 요청 실패: \(error.localizedDescription)", category: .error ) throw error diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift index de49b176..c2482298 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -22,26 +22,26 @@ public final class AdminUseCaseImpl: AdminUseCase { } public func createStore(params: CreateStoreParams) -> Completable { - Logger.log(message: "createStore 호출 - 스토어명: \(params.name)", category: .debug) + Logger.log("createStore 호출 - 스토어명: \(params.name)", category: .debug) return repository.createStore(params: params) .do(onError: { error in - Logger.log(message: "createStore 실패 - Error: \(error)", category: .error) + Logger.log("createStore 실패 - Error: \(error)", category: .error) }, onCompleted: { - Logger.log(message: "createStore 성공", category: .info) + Logger.log("createStore 성공", category: .info) }) } public func updateStore(params: UpdateStoreParams) -> Completable { - Logger.log(message: """ + Logger.log(""" Updating store with location: Latitude: \(params.latitude) Longitude: \(params.longitude) """, category: .debug) return repository.updateStore(params: params) .do(onError: { error in - Logger.log(message: "Store update failed: \(error)", category: .error) + Logger.log("Store update failed: \(error)", category: .error) }, onCompleted: { - Logger.log(message: "Store update successful", category: .debug) + Logger.log("Store update successful", category: .debug) }) } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index 5253690e..457d8a8c 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -32,9 +32,9 @@ public final class MapUseCaseImpl: MapUseCase { categories: categories ) .do(onNext: { stores in - Logger.log(message: "맵 범위 내 스토어 \(stores.count)개 로드됨", category: .debug) + Logger.log("맵 범위 내 스토어 \(stores.count)개 로드됨", category: .debug) }, onError: { error in - Logger.log(message: "맵 범위 내 스토어 로드 실패: \(error)", category: .error) + Logger.log("맵 범위 내 스토어 로드 실패: \(error)", category: .error) }) } @@ -47,9 +47,9 @@ public final class MapUseCaseImpl: MapUseCase { categories: categories ) .do(onNext: { stores in - Logger.log(message: "'\(query)' 검색 결과 \(stores.count)개 로드됨", category: .debug) + Logger.log("'\(query)' 검색 결과 \(stores.count)개 로드됨", category: .debug) }, onError: { error in - Logger.log(message: "스토어 검색 실패: \(error)", category: .error) + Logger.log("스토어 검색 실패: \(error)", category: .error) }) } diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index db866640..a858dfe7 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( /Localized/Resource/LaunchScreen.storyboard, - Resource/Debug.xcconfig, Resource/Info.plist, ); target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; diff --git a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme index 9b875b57..77d7ce2e 100644 --- a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme +++ b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme @@ -63,7 +63,9 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + allowLocationSimulation = "YES" + consoleMode = "0" + structuredConsoleMode = "1"> ^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" deleted file mode 100644 index 10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png deleted file mode 100644 index 10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg new file mode 100644 index 00000000..1cb7b17e --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json index ee893b42..b1cb08ba 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "solid.png", + "filename" : "solid.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "solid 복사본.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "solid 복사본 2.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" deleted file mode 100644 index 39f13b3126d68a1cbc184d26c7b7cac06527e45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2698 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 + + + diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 69a94cbe..d8e74495 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -212,7 +212,7 @@ final class AdminBottomSheetView: UIView { // MARK: - Public Methods func updateContentVisibility(isCategorySelected: Bool) { - Logger.log(message: "높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) + Logger.log("높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) let newHeight: CGFloat = isCategorySelected ? 200 : 160 @@ -223,6 +223,6 @@ final class AdminBottomSheetView: UIView { setNeedsLayout() layoutIfNeeded() - Logger.log(message: "높이 변경 완료", category: .debug) + Logger.log("높이 변경 완료", category: .debug) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 89ee0c06..a143fbe3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -42,7 +42,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { private func setupViews() { view.backgroundColor = .clear - Logger.log(message: "초기 뷰 계층:", category: .debug) + Logger.log("초기 뷰 계층:", category: .debug) view.addSubview(mainView) mainView.isUserInteractionEnabled = true @@ -57,7 +57,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { containerViewBottomConstraint = make.bottom.equalTo(view.snp.bottom).constraint } - Logger.log(message: "mainView 추가 후 계층:", category: .debug) + Logger.log("mainView 추가 후 계층:", category: .debug) dimmedView.backgroundColor = .black.withAlphaComponent(0.4) dimmedView.alpha = 0 @@ -72,7 +72,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { make.edges.equalToSuperview() } - Logger.log(message: "최종 뷰 계층:", category: .debug) + Logger.log("최종 뷰 계층:", category: .debug) } private func setupCollectionView() { @@ -214,6 +214,6 @@ final class AdminBottomSheetViewController: BaseViewController, View { } deinit { - Logger.log(message: "BottomSheet deinit", category: .debug) + Logger.log("BottomSheet deinit", category: .debug) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift index 33da143f..b023363b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift @@ -56,9 +56,9 @@ final class AdminReactor: Reactor { .just(.setIsLoading(true)), adminUseCase.fetchStoreList(query: query, page: 0, size: 100) .do(onNext: { response in - Logger.log(message: "조회 성공 - 응답 데이터: \(response)", category: .info) + Logger.log("조회 성공 - 응답 데이터: \(response)", category: .info) }, onError: { error in - Logger.log(message: "조회 실패 - 에러: \(error.localizedDescription)", category: .error) + Logger.log("조회 실패 - 에러: \(error.localizedDescription)", category: .error) }) .map { .setStores($0) }, .just(.setIsLoading(false)) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 324f2183..5ec2c51c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -401,76 +401,76 @@ final class PopUpStoreRegisterReactor: Reactor { // 폼 유효성 검사 private func validateForm(state: State) -> Bool { - Logger.log(message: "폼 유효성 검사 시작", category: .debug) + Logger.log("폼 유효성 검사 시작", category: .debug) // 이름 필드 검사 guard !state.name.isEmpty else { - Logger.log(message: "유효성 검사 실패: 이름 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 이름 비어있음", category: .debug) return false } // 주소 필드 검사 guard !state.address.isEmpty else { - Logger.log(message: "유효성 검사 실패: 주소 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 주소 비어있음", category: .debug) return false } // 위도/경도 필드 검사 guard !state.lat.isEmpty else { - Logger.log(message: "유효성 검사 실패: 위도 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 위도 비어있음", category: .debug) return false } guard !state.lon.isEmpty else { - Logger.log(message: "유효성 검사 실패: 경도 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 경도 비어있음", category: .debug) return false } // 설명 필드 검사 guard !state.description.isEmpty else { - Logger.log(message: "유효성 검사 실패: 설명 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 설명 비어있음", category: .debug) return false } // 카테고리 필드 검사 guard !state.category.isEmpty else { - Logger.log(message: "유효성 검사 실패: 카테고리 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 카테고리 비어있음", category: .debug) return false } // 이미지 검사 guard !state.images.isEmpty else { - Logger.log(message: "유효성 검사 실패: 이미지 없음", category: .debug) + Logger.log("유효성 검사 실패: 이미지 없음", category: .debug) return false } // 대표 이미지 검사 guard state.images.contains(where: { $0.isMain }) else { - Logger.log(message: "유효성 검사 실패: 대표 이미지 없음", category: .debug) + Logger.log("유효성 검사 실패: 대표 이미지 없음", category: .debug) return false } // 날짜 검사 guard state.selectedStartDate != nil else { - Logger.log(message: "유효성 검사 실패: 시작 날짜 없음", category: .debug) + Logger.log("유효성 검사 실패: 시작 날짜 없음", category: .debug) return false } guard state.selectedEndDate != nil else { - Logger.log(message: "유효성 검사 실패: 종료 날짜 없음", category: .debug) + Logger.log("유효성 검사 실패: 종료 날짜 없음", category: .debug) return false } // 위도/경도 유효성 검사 guard let latVal = Double(state.lat), let lonVal = Double(state.lon) else { - Logger.log(message: "유효성 검사 실패: 위도/경도 형식 오류", category: .debug) + Logger.log("유효성 검사 실패: 위도/경도 형식 오류", category: .debug) return false } // 위도/경도 값이 유효한지 검사 guard latVal != 0 || lonVal != 0 else { - Logger.log(message: "유효성 검사 실패: 위도/경도 값이 모두 0", category: .debug) + Logger.log("유효성 검사 실패: 위도/경도 값이 모두 0", category: .debug) return false } @@ -478,17 +478,17 @@ final class PopUpStoreRegisterReactor: Reactor { if let startDate = state.selectedStartDate, let endDate = state.selectedEndDate, startDate > endDate { - Logger.log(message: "유효성 검사 실패: 시작일이 종료일보다 늦음", category: .debug) + Logger.log("유효성 검사 실패: 시작일이 종료일보다 늦음", category: .debug) return false } - Logger.log(message: "유효성 검사 성공", category: .debug) + Logger.log("유효성 검사 성공", category: .debug) return true } // 주소 지오코딩 private func geocodeAddress(address: String) -> Observable { - Logger.log(message: "지오코딩 함수 호출: \(address)", category: .debug) + Logger.log("지오코딩 함수 호출: \(address)", category: .debug) return Observable.create { observer in let geocoder = CLGeocoder() @@ -500,7 +500,7 @@ final class PopUpStoreRegisterReactor: Reactor { preferredLocale: Locale(identifier: "ko_KR") ) { placemarks, error in if let error = error { - Logger.log(message: "Geocoding error: \(error.localizedDescription)", category: .error) + Logger.log("Geocoding error: \(error.localizedDescription)", category: .error) observer.onNext(nil) observer.onCompleted() return @@ -525,10 +525,10 @@ final class PopUpStoreRegisterReactor: Reactor { preSignedUseCase.tryDelete(objectKeyList: imagePaths) .subscribe( onCompleted: { - Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) + Logger.log("S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) }, onError: { error in - Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) + Logger.log("S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) } ) .disposed(by: disposeBag) @@ -537,7 +537,7 @@ final class PopUpStoreRegisterReactor: Reactor { // 카테고리 ID 매핑 private func getCategoryId(from title: String) -> Int { let cleanTitle = title.replacingOccurrences(of: " ▾", with: "") - Logger.log(message: "카테고리 매핑 시작 - 타이틀: \(cleanTitle)", category: .debug) + Logger.log("카테고리 매핑 시작 - 타이틀: \(cleanTitle)", category: .debug) let categoryMap: [String: Int64] = [ "패션": 1, @@ -555,10 +555,10 @@ final class PopUpStoreRegisterReactor: Reactor { ] if let id = categoryMap[cleanTitle] { - Logger.log(message: "카테고리 매핑 성공: \(cleanTitle) -> \(id)", category: .debug) + Logger.log("카테고리 매핑 성공: \(cleanTitle) -> \(id)", category: .debug) return Int(id) } else { - Logger.log(message: "카테고리 매핑 실패: \(cleanTitle)에 해당하는 ID를 찾을 수 없음", category: .error) + Logger.log("카테고리 매핑 실패: \(cleanTitle)에 해당하는 ID를 찾을 수 없음", category: .error) return 1 // 기본값 } } @@ -655,13 +655,13 @@ final class PopUpStoreRegisterReactor: Reactor { defer { dispatchGroup.leave() } if let error = error { - Logger.log(message: "이미지 로드 오류: \(error.localizedDescription)", category: .error) + Logger.log("이미지 로드 오류: \(error.localizedDescription)", category: .error) return } guard let data = data, let image = UIImage(data: data) else { - Logger.log(message: "이미지 변환 실패", category: .error) + Logger.log("이미지 변환 실패", category: .error) return } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift index 2abf1f73..fca9ddfa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift @@ -81,14 +81,14 @@ final class AdminStoreCell: UITableViewCell { // MARK: - Configure func configure(with store: AdminStore) { - Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) + Logger.log("셀 데이터 바인딩: \(store)", category: .debug) titleLabel.text = store.name categoryLabel.text = store.categoryName statusChip.text = "운영" let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") - Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) + Logger.log("이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index ddf71a45..6e31f7e6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -223,7 +223,7 @@ final class AdminViewController: BaseViewController, View { allImageUrls = Array(Set(allImageUrls)) - Logger.log(message: "삭제할 이미지: \(allImageUrls.count)개", category: .debug) + Logger.log("삭제할 이미지: \(allImageUrls.count)개", category: .debug) @Dependency var preSignedUseCase: PreSignedUseCase preSignedUseCase.tryDelete(objectKeyList: allImageUrls) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 510d5841..ba634aef 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -94,14 +94,14 @@ final class CommentDetailReactor: Reactor { newState.commentData.likeCount += 1 userAPIUseCase.postCommentLike(commentId: newState.commentData.commentID) .subscribe(onDisposed: { - Logger.log(message: "CommentLike", category: .info) + Logger.log("CommentLike", category: .info) }) .disposed(by: disposeBag) } else { newState.commentData.likeCount -= 1 userAPIUseCase.deleteCommentLike(commentId: newState.commentData.commentID) .subscribe(onDisposed: { - Logger.log(message: "CommentLikeDelete", category: .info) + Logger.log("CommentLikeDelete", category: .info) }) .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index e0b71792..5f88d677 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -303,7 +303,7 @@ final class CommentListReactor: Reactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) .subscribe(onDisposed: { - Logger.log(message: "S3 Image Delete 완료", category: .info) + Logger.log("S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) case .edit: diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index b68f78ed..9ff04140 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -223,11 +223,11 @@ extension NormalCommentAddReactor: PHPickerViewControllerDelegate { if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { - Logger.log(message: "Failed to load image", category: .error) + Logger.log("Failed to load image", category: .error) } } } else { - Logger.log(message: "ItemProvider Can Not Load Object", category: .error) + Logger.log("ItemProvider Can Not Load Object", category: .error) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 1ad07a70..dc616f45 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -261,11 +261,11 @@ extension NormalCommentEditReactor: PHPickerViewControllerDelegate { if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { - Logger.log(message: "Failed to load image", category: .error) + Logger.log("Failed to load image", category: .error) } } } else { - Logger.log(message: "ItemProvider Can Not Load Object", category: .error) + Logger.log("ItemProvider Can Not Load Object", category: .error) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 43b3121c..6c766d50 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -426,23 +426,23 @@ extension DetailReactor { // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedPath) else { - Logger.log(message: "URL 생성 실패", category: .error) + Logger.log("URL 생성 실패", category: .error) return } // 🔹 비동기적으로 이미지 다운로드 URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { - Logger.log(message: "다운로드 실패", category: .error) + Logger.log("다운로드 실패", category: .error) return } guard let data = data, let image = UIImage(data: data) else { - Logger.log(message: "이미지 변환 실패", category: .error) + Logger.log("이미지 변환 실패", category: .error) return } - Logger.log(message: "이미지 다운로드 성공", category: .info) + Logger.log("이미지 다운로드 성공", category: .info) let sharedText = "[팝풀] \(storeName) 팝업 어때요?\n지금 바로 팝풀에서 확인해보세요!" // UI 업데이트는 메인 스레드에서 실행 @@ -545,7 +545,7 @@ extension DetailReactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) .subscribe(onDisposed: { - Logger.log(message: "S3 Image Delete 완료", category: .info) + Logger.log("S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift index 45161c19..e5cc9467 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift @@ -27,15 +27,15 @@ final class BalloonBackgroundView: UIView { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(8) let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 20, leading: 20, bottom: 19, trailing: 20) + section.contentInsets = .init(top: 18, leading: 20, bottom: 19, trailing: 20) section.interGroupSpacing = 8 return section } - let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) - cv.backgroundColor = .clear - cv.isScrollEnabled = false - cv.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifier) - return cv + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.isScrollEnabled = false + collectionView.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifier) + return collectionView }() // "그 외 지역" 전용 UI: 아이콘과 안내문구 @@ -104,7 +104,7 @@ final class BalloonBackgroundView: UIView { containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalToSuperview().offset(arrowHeight) - make.bottom.equalToSuperview().priority(.high) + make.bottom.equalToSuperview() } containerView.addSubview(collectionView) @@ -114,7 +114,7 @@ final class BalloonBackgroundView: UIView { collectionView.snp.makeConstraints { make in make.top.left.right.equalToSuperview() - make.bottom.equalToSuperview().priority(.high) + make.bottom.equalToSuperview() } singleRegionIcon.snp.makeConstraints { make in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 964e5995..81b1f4b5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -41,7 +41,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setImage(resizedImage, for: .normal) button.semanticContentAttribute = .forceRightToLeft button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 0) - button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 12) + button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 10, bottom: 6, right: 12) button.setBackgroundColor(.blu500, for: .normal) button.setTitleColor(.white, for: .normal) button.layer.borderWidth = 0 @@ -51,7 +51,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setImage(nil, for: .normal) button.semanticContentAttribute = .unspecified button.imageEdgeInsets = .zero - button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 12, bottom: 6, right: 12) + button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 12, bottom: 6, right: 12) button.setBackgroundColor(.white, for: .normal) button.setTitleColor(.g400, for: .normal) button.layer.borderWidth = 1 diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift index 3ad18fc0..ec0fa088 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift @@ -72,7 +72,7 @@ final class FilterChip: UIButton { } let rightPadding: CGFloat = closeButton.isHidden ? 12 : 34 - contentEdgeInsets = UIEdgeInsets(top: 7, left: 12, bottom: 7, right: rightPadding) + contentEdgeInsets = UIEdgeInsets(top: 5, left: 12, bottom: 7, right: rightPadding) } // MARK: - Actions diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index b6f8989a..66be0ba2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -34,10 +34,10 @@ class FullScreenMapViewController: MapViewController { setupNavigation() // configureInitialMapPosition() self.navigationController?.navigationBar.isHidden = false - Logger.log(message: "💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) + Logger.log("💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) configureInitialMapPosition() - Logger.log(message: "✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) + Logger.log("✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) mainView.mapView.touchDelegate = self } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index a51aa8ab..e31f9c61 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -121,11 +121,9 @@ final class MapGuideReactor: Reactor { return Observable.just(.showToast("지원하지 않는 맵 앱입니다.")) } - Logger.log(message: "🗺 맵 앱 열기 시도: \(urlScheme)", category: .debug) if let url = URL(string: urlScheme) { if UIApplication.shared.canOpenURL(url) { - Logger.log(message: "✅ \(appType) 앱 실행", category: .debug) UIApplication.shared.open(url, options: [:], completionHandler: nil) return Observable.empty() } else { @@ -133,7 +131,7 @@ final class MapGuideReactor: Reactor { if appType.lowercased() == "apple" { return Observable.just(.showToast("애플 지도 앱을 열 수 없습니다.")) } else { - Logger.log(message: "❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) + Logger.log("❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) if let appStoreURL = URL(string: appStoreUrl) { UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift index be525bb3..a79bf643 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift @@ -65,7 +65,7 @@ final class MapMarker: UIView { // MARK: - Init init() { - super.init(frame: CGRect(x: 0, y: 0, width: 80, height: 32)) + super.init(frame: CGRect(x: 0, y: 0, width: 32, height: 32)) setUpConstraints() } @@ -86,10 +86,8 @@ private extension MapMarker { labelStackView.addArrangedSubview(countLabel) countBadgeView.addSubview(badgeCountLabel) - self.snp.makeConstraints { make in - make.width.equalTo(200) - make.height.equalTo(70) - } + // 고정된 크기 제약조건 제거 + // 대신 내부 컨텐츠에 맞게 크기가 조정되도록 변경 markerImageView.snp.makeConstraints { make in make.centerX.equalToSuperview() @@ -99,12 +97,12 @@ private extension MapMarker { clusterContainer.snp.makeConstraints { make in make.center.equalToSuperview() - make.height.equalTo(24) - make.width.equalTo(80) + make.height.equalTo(32) + make.width.greaterThanOrEqualTo(80) } labelStackView.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)) + make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)) } countBadgeView.snp.makeConstraints { make in @@ -140,10 +138,14 @@ private extension MapMarker { countBadgeView.snp.remakeConstraints { make in make.width.height.equalTo(20) - make.top.equalTo(markerImageView.snp.top).offset(isSelected ? 0 : -4) - make.right.equalTo(markerImageView.snp.right).offset(isSelected ? 0 : 4) + if isSelected { + make.top.equalTo(markerImageView.snp.top).offset(-4) + make.right.equalTo(markerImageView.snp.right).offset(4) + } else { + make.top.equalTo(markerImageView.snp.top).offset(-4) + make.right.equalTo(markerImageView.snp.right).offset(4) + } } - self.layoutIfNeeded() CATransaction.commit() } @@ -188,8 +190,10 @@ extension MapMarker: Inputable { regionLabel.text = input.regionName countLabel.text = " \(input.count)" + // 클러스터 마커 크기 계산 - 텍스트 내용에 맞게 동적으로 조정 + labelStackView.layoutIfNeeded() let stackSize = labelStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) - let requiredWidth = stackSize.width + 24 + let requiredWidth = max(stackSize.width + 24, 80) // 최소 너비 보장 clusterContainer.snp.remakeConstraints { make in make.center.equalToSuperview() @@ -200,18 +204,33 @@ extension MapMarker: Inputable { labelStackView.snp.remakeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)) } + + self.frame.size = CGSize(width: requiredWidth, height: 32) } private func setupSingleMarker(_ input: Input) { markerImageView.isHidden = false clusterContainer.isHidden = true + updateMarkerImage(isSelected: input.isSelected) + let size = input.isSelected ? 44 : 32 + if input.count > 1 { countBadgeView.isHidden = false badgeCountLabel.text = "\(input.count)" + + countBadgeView.snp.remakeConstraints { make in + make.width.height.equalTo(20) + make.top.equalTo(markerImageView.snp.top).offset(input.isSelected ? 0 : -4) + make.right.equalTo(markerImageView.snp.right).offset(input.isSelected ? 2 : 4) + } + + self.frame.size = CGSize(width: size + 8, height: size) } else { countBadgeView.isHidden = true + + self.frame.size = CGSize(width: size, height: size) } } } @@ -222,6 +241,18 @@ extension MapMarker { } func asImage() -> UIImage? { + if let input = currentInput { + if input.isCluster { + self.layoutIfNeeded() + let clusterSize = clusterContainer.bounds.size + self.frame = CGRect(x: 0, y: 0, width: clusterSize.width + 8, height: clusterSize.height + 8) + } else { + let size = input.isSelected ? 44 : 32 + let extraWidth = (input.count > 1) ? 10 : 0 + self.frame = CGRect(x: 0, y: 0, width: size + extraWidth, height: size + 4) + } + } + self.layoutIfNeeded() UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift index dd6d5db1..3c8405c9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift @@ -225,7 +225,7 @@ final class MapReactor: Reactor { }, onError: { error in Logger.log( - message: "❌ [에러]: 요청 실패 - \(error.localizedDescription)", + "❌ [에러]: 요청 실패 - \(error.localizedDescription)", category: .error ) }, @@ -353,7 +353,7 @@ final class MapReactor: Reactor { } Logger.log( - message: """ + """ Updated viewport stores: - Total: \(updatedStores.count) - Selected Store: \(state.selectedStore?.name ?? "None") @@ -370,7 +370,7 @@ final class MapReactor: Reactor { newState.error = error if let error = error { Logger.log( - message: """ + """ Error occurred in MapReactor: - Description: \(error.localizedDescription) - Domain: \(String(describing: (error as NSError).domain)) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 39e20a31..63194f04 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -59,7 +59,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var filterChipsTopY: CGFloat = 0 private var filterContainerBottomY: CGFloat { let frameInView = mainView.filterChips.convert(mainView.filterChips.bounds, to: view) - return frameInView.maxY // 필터 컨테이너의 바닥 높이 + return frameInView.maxY } enum ModalState { @@ -70,6 +70,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var modalState: ModalState = .bottom private let idleSubject = PublishSubject() + private let cameraIdle = PublishSubject() // MARK: - Lifecycle override func viewDidAppear(_ animated: Bool) { @@ -99,8 +100,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let reactor = self.reactor { reactor.action.onNext(.fetchCategories) - - // 한국 전체 영역에 대한 경계값 설정 let koreaRegion = ( northEast: NMGLatLng(lat: 38.0, lng: 132.0), southWest: NMGLatLng(lat: 33.0, lng: 124.0) @@ -113,7 +112,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM southWestLon: koreaRegion.southWest.lng )) } - setupMapViewRxObservables() carouselView.rx.observe(Bool.self, "hidden") @@ -146,21 +144,15 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM pageIndex < self.currentCarouselStores.count else { return } let store = self.currentCarouselStores[pageIndex] - - // 이전 선택 마커 상태 초기화 if let previousMarker = self.currentMarker { self.updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false, count: 1) } - // 스와이프한 스토어에 해당하는 마커 찾기 let markerToFocus = self.findMarkerForStore(for: store) if let markerToFocus = markerToFocus { - // 마커 선택 상태로 업데이트 self.updateMarkerStyle(marker: markerToFocus, selected: true, isCluster: false, count: 1) self.currentMarker = markerToFocus - - // 마이크로 클러스터인 경우 툴팁 처리 let userData = markerToFocus.userInfo["storeData"] as? [MapPopUpStore] if let storeArray = userData, storeArray.count > 1 { if self.currentTooltipView == nil || @@ -187,10 +179,21 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } - // NMF 이벤트 설정을 위한 새로운 메서드 private func setupMapViewRxObservables() { - // 지도 이동 완료 감지 mainView.mapView.addCameraDelegate(delegate: self) + cameraIdle + .debounce(.milliseconds(300), scheduler: MainScheduler.instance) + .map { [unowned self] in + let bounds = self.getVisibleBounds() + return MapReactor.Action.viewportChanged( + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng + ) + } + .bind(to: reactor!.action) + .disposed(by: disposeBag) idleSubject .observe(on: MainScheduler.instance) @@ -211,13 +214,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func configureTooltip(for marker: NMFMarker, stores: [MapPopUpStore]) { - Logger.log(message: """ - 툴팁 설정: - - 현재 캐러셀 스토어: \(currentCarouselStores.map { $0.name }) - - 마커 스토어: \(stores.map { $0.name }) - """, category: .debug) - - // 기존 툴팁 제거 self.currentTooltipView?.removeFromSuperview() let tooltipView = MarkerTooltipView() @@ -233,12 +229,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: stores.count) tooltipView.selectStore(at: index) - - Logger.log(message: """ - 툴팁 선택: - - 선택된 스토어: \(stores[index].name) - - 툴팁 인덱스: \(index) - """, category: .debug) } let markerPoint = self.mainView.mapView.projection.point(from: marker.position) @@ -289,8 +279,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM setupPanAndSwipeGestures() let mapViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleMapViewTap(_:))) -// mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 - mapViewTapGesture.delaysTouchesBegan = false // 터치 지연 없음 + mapViewTapGesture.delaysTouchesBegan = false mainView.mapView.addGestureRecognizer(mapViewTapGesture) mapViewTapGesture.delegate = self } @@ -301,7 +290,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬆️ 위로 스와이프 감지", category: .debug) + Logger.log("⬆️ 위로 스와이프 감지", category: .debug) switch owner.modalState { case .bottom: owner.animateToState(.middle) @@ -317,7 +306,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬇️ 아래로 스와이프 감지됨", category: .debug) + Logger.log("⬇️ 아래로 스와이프 감지됨", category: .debug) switch owner.modalState { case .top: owner.animateToState(.middle) @@ -332,7 +321,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - Bind func bind(reactor: Reactor) { - // 필터 관련 바인딩 mainView.filterChips.locationChip.rx.tap .map { Reactor.Action.filterTapped(.location) } .bind(to: reactor.action) @@ -343,11 +331,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind(to: reactor.action) .disposed(by: disposeBag) - // 리스트 버튼 탭 mainView.listButton.rx.tap .withUnretained(self) .subscribe { owner, _ in - owner.animateToState(.middle) // 버튼 눌렀을 때 상태를 middle로 변경 + owner.animateToState(.middle) } .disposed(by: disposeBag) @@ -356,8 +343,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] _ in guard let self = self, let location = self.locationManager.location else { return } - - // 현재 위치로 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: location.coordinate.latitude, lng: location.coordinate.longitude @@ -369,10 +354,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.filterChips.onRemoveLocation = { [weak self] in guard let self = self else { return } - // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.location)) - - // 현재 뷰포트의 바운드로 마커 업데이트 요청 let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( northEastLat: bounds.northEast.lat, @@ -385,7 +367,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.clusterMarkerDictionary.values.forEach { $0.mapView = nil } self.clusterMarkerDictionary.removeAll() - // 캐러셀 숨기기 추가 self.carouselView.isHidden = true self.carouselView.updateCards([]) self.currentCarouselStores = [] @@ -396,10 +377,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.filterChips.onRemoveCategory = { [weak self] in guard let self = self else { return } - // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.category)) - - // 현재 뷰포트의 바운드로 마커 업데이트 요청 let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( northEastLat: bounds.northEast.lat, @@ -419,7 +397,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM reactor.state.map { $0.selectedLocationFilters }.distinctUntilChanged(), reactor.state.map { $0.selectedCategoryFilters }.distinctUntilChanged() ) { locationFilters, categoryFilters -> (String, String) in - // 지역 필터 텍스트 포맷팅 let locationText: String if locationFilters.isEmpty { locationText = "지역선택" @@ -428,8 +405,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } else { locationText = locationFilters[0] } - - // 카테고리 필터 텍스트 포맷팅 let categoryText: String if categoryFilters.isEmpty { categoryText = "카테고리" @@ -469,7 +444,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] store in guard let self = self else { return } - // 검색 결과 위치로 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: store.latitude, lng: store.longitude @@ -499,25 +473,19 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] results in guard let self = self else { return } - // 이전 선택된 마커, 툴팁, 캐러셀 초기화 self.clearAllMarkers() self.storeListViewController.reactor?.action.onNext(.setStores([])) self.carouselView.updateCards([]) self.carouselView.isHidden = true self.resetSelectedMarker() // 추가된 부분 - // 결과가 없으면 스토어 카드 숨김 후 종료 if results.isEmpty { self.mainView.setStoreCardHidden(true, animated: true) return } else { self.mainView.setStoreCardHidden(false, animated: true) } - - // 새 결과로 마커 추가 및 업데이트 self.addMarkers(for: results) - - // 스토어 리스트 업데이트 let storeItems = results.map { store in StoreItem( id: store.id, @@ -530,13 +498,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM ) } self.storeListViewController.reactor?.action.onNext(.setStores(storeItems)) - - // 캐러셀 업데이트 self.carouselView.updateCards(results) self.carouselView.isHidden = false self.currentCarouselStores = results - - // 첫 번째 검색 결과로 지도 이동 if let firstStore = results.first { let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: firstStore.latitude, @@ -573,9 +537,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 중요: 마커에 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - Logger.log(message: "마커 터치됨! 위치: \(marker.position), 스토어: \(store.name)", category: .debug) - // 단일 스토어 마커 처리 return self.handleSingleStoreTap(marker, store: store) } @@ -584,34 +545,42 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM markerDictionary[store.id] = marker } - func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { - if selected { - marker.width = 44 - marker.height = 44 - marker.iconImage = NMFOverlayImage(name: "TapMarker") - } else if isCluster { - marker.width = 36 - marker.height = 36 - marker.iconImage = NMFOverlayImage(name: "cluster_marker") + func updateMarkerStyle( + marker: NMFMarker, + selected: Bool, + isCluster: Bool, + count: Int = 1, + regionName: String = "" + ) { + let mapMarkerView: MapMarker + if let cachedView = marker.userInfo["mapMarkerView"] as? MapMarker { + mapMarkerView = cachedView } else { - marker.width = 32 - marker.height = 32 - marker.iconImage = NMFOverlayImage(name: "Marker") + mapMarkerView = MapMarker() + marker.userInfo["mapMarkerView"] = mapMarkerView } - if count > 1 { - marker.captionText = "\(count)" - } else { - marker.captionText = "" - } + let wasMultiMarker = (mapMarkerView.currentInput?.count ?? 0) > 1 + + let input = MapMarker.Input( + isSelected: selected, + isCluster: isCluster, + regionName: regionName, + count: count, + isMultiMarker: count > 1 && !isCluster + ) + mapMarkerView.injection(with: input) - marker.anchor = CGPoint(x: 0.5, y: 1.0) + if let img = mapMarkerView.asImage() { + marker.iconImage = NMFOverlayImage(image: img) + marker.width = img.size.width + marker.height = img.size.height + marker.anchor = CGPoint(x: 0.5, y: isCluster ? 0.5 : 1.0) + } } @objc private func handleMapViewTap(_ gesture: UITapGestureRecognizer) { - // 리스트뷰가 현재 보이는 상태(중간 또는 상단)일 때만 내림 if modalState == .middle || modalState == .top { - Logger.log(message: "맵뷰 탭 감지: 리스트뷰 내림", category: .debug) animateToState(.bottom) } } @@ -626,9 +595,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 let newOffset = currentOffset + translation.y - // 오프셋 제한 범위 설정 - let minOffset: CGFloat = filterContainerBottomY // 필터 컨테이너 바닥 제한 - let maxOffset: CGFloat = view.frame.height // 최하단 제한 + let minOffset: CGFloat = filterContainerBottomY + let maxOffset: CGFloat = view.frame.height let clampedOffset = min(max(newOffset, minOffset), maxOffset) constraint.update(offset: clampedOffset) @@ -642,13 +610,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM case .ended: if let constraint = listViewTopConstraint { let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 - let middleY = view.frame.height * 0.3 // 중간 지점 기준 높이 + let middleY = view.frame.height * 0.3 let targetState: ModalState - // 속도와 위치를 기반으로 상태 결정 - if velocity.y > 500 { // 아래로 빠르게 드래그 + if velocity.y > 500 { targetState = .bottom - } else if velocity.y < -500 { // 위로 빠르게 드래그 + } else if velocity.y < -500 { targetState = .top } else if currentOffset < middleY * 0.7 { targetState = .top @@ -670,9 +637,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let middleOffset = view.frame.height * 0.3 if offset <= minOffset { - mainView.mapView.alpha = 0 // 탑에서는 완전히 숨김 + mainView.mapView.alpha = 0 } else if offset >= maxOffset { - mainView.mapView.alpha = 1 // 바텀에서는 완전히 보임 + mainView.mapView.alpha = 1 } else if offset <= middleOffset { let progress = (offset - minOffset) / (middleOffset - minOffset) mainView.mapView.alpha = progress @@ -697,7 +664,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.mainView.filterChips.bounds, to: self.view ) - self.mainView.mapView.alpha = 0 // 탑 상태에서는 숨김 + self.mainView.mapView.alpha = 0 self.storeListViewController.setGrabberHandleVisible(false) self.listViewTopConstraint?.update(offset: filterChipsFrame.maxY) self.mainView.searchInput.setBackgroundColor(.g50) @@ -726,7 +693,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.fetchStoreDetails(for: stores) Logger.log( - message: "✅ 전체 스토어 목록으로 리스트뷰 업데이트: \(stores.count)개", + "✅ 전체 스토어 목록으로 리스트뷰 업데이트: \(stores.count)개", category: .debug ) }) @@ -744,7 +711,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.view.layoutIfNeeded() }) { _ in self.modalState = state - Logger.log(message: ". 현재 상태: \(state)", category: .debug) } } @@ -760,15 +726,13 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - Helper: 클러스터용 커스텀 마커 이미지 생성 (MapMarker를 사용) func createClusterMarkerImage(regionName: String, count: Int) -> UIImage? { - // MapMarker의 입력값에 클러스터 상태를 전달합니다. - let markerView = MapMarker() // 기존 커스텀 뷰, 네이버맵용으로도 사용 가능하도록 구현됨. + let markerView = MapMarker() let input = MapMarker.Input(isSelected: false, isCluster: true, regionName: regionName, count: count, isMultiMarker: false) markerView.injection(with: input) - // 프레임이 설정되어 있지 않다면 적당한 크기로 지정 (예: 80x32) if markerView.frame == .zero { markerView.frame = CGRect(x: 0, y: 0, width: 80, height: 32) } @@ -778,23 +742,16 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateMapWithClustering() { let currentZoom = mainView.mapView.zoomLevel let level = MapZoomLevel.getLevel(from: Float(currentZoom)) - // 클러스터 처리 시 현재 스토어 목록(currentStores)을 사용 - Logger.log(message: "현재 줌 레벨: \(currentZoom), 모드: \(level), 스토어 수: \(currentStores.count)", category: .debug) - CATransaction.begin() CATransaction.setDisableActions(true) switch level { case .detailed: - // 상세 레벨에서는 개별 마커를 사용합니다. let newStoreIds = Set(currentStores.map { $0.id }) let groupedDict = groupStoresByExactLocation(currentStores) - - // 클러스터 마커 제거 clusterMarkerDictionary.values.forEach { $0.mapView = nil } clusterMarkerDictionary.removeAll() - // 그룹별로 개별 마커 생성/업데이트 for (coordinate, storeGroup) in groupedDict { if storeGroup.count == 1, let store = storeGroup.first { if let existingMarker = individualMarkerDictionary[store.id] { @@ -814,8 +771,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("개별 마커 터치됨! 스토어: \(store.name)") return self.handleSingleStoreTap(marker, store: store) } @@ -823,7 +778,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM individualMarkerDictionary[store.id] = marker } } else { - // 여러 스토어가 동일 위치에 있으면 단일 마커로 표시하면서 count 갱신 guard let firstStore = storeGroup.first else { continue } let markerKey = firstStore.id if let existingMarker = individualMarkerDictionary[markerKey] { @@ -837,11 +791,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM marker.anchor = CGPoint(x: 0.5, y: 1.0) updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeGroup.count) - // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("마이크로 클러스터 마커 터치됨! 스토어 수: \(storeGroup.count)개") return self.handleMicroClusterTap(marker, storeArray: storeGroup) } @@ -851,7 +802,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } - // 기존에 보이지 않는 개별 마커 제거 individualMarkerDictionary = individualMarkerDictionary.filter { id, marker in if newStoreIds.contains(id) { return true @@ -862,11 +812,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } case .district, .city, .country: - // 개별 마커 숨기기 individualMarkerDictionary.values.forEach { $0.mapView = nil } individualMarkerDictionary.removeAll() - // 클러스터 생성 let clusters = clusteringManager.clusterStores(currentStores, at: Float(currentZoom)) let activeClusterKeys = Set(clusters.map { $0.cluster.name }) @@ -875,7 +823,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM var marker: NMFMarker if let existingMarker = clusterMarkerDictionary[clusterKey] { marker = existingMarker - // 위치 업데이트가 필요하면 수정 if marker.position.lat != cluster.cluster.coordinate.lat || marker.position.lng != cluster.cluster.coordinate.lng { marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) @@ -886,17 +833,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) - marker.userInfo = ["clusterData": cluster] // 중요: userInfo에 cluster 객체를 직접 저장 + marker.userInfo = ["clusterData": cluster] - // 여기서 커스텀 클러스터 마커 뷰를 이미지로 변환하여 적용합니다. if let clusterImage = createClusterMarkerImage(regionName: cluster.cluster.name, count: cluster.storeCount) { marker.iconImage = NMFOverlayImage(image: clusterImage) } else { - // 기본 에셋 fallback (원하는 경우) marker.iconImage = NMFOverlayImage(name: "cluster_marker") } - // 터치 핸들러 추가 - userInfo에서 클러스터 데이터를 직접 가져오기 marker.touchHandler = { [weak self] (overlay) -> Bool in guard let self = self, let tappedMarker = overlay as? NMFMarker, @@ -912,7 +856,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM marker.mapView = mainView.mapView } - // 활성 클러스터가 아닌 마커 제거 for (key, marker) in clusterMarkerDictionary { if !activeClusterKeys.contains(key) { marker.mapView = nil @@ -1045,7 +988,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM currentFilterBottomSheet = nil } - // 기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { markerDictionary.values.forEach { $0.mapView = nil } markerDictionary.removeAll() @@ -1057,7 +999,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) - // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } @@ -1070,7 +1011,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } private func updateListView(with results: [MapPopUpStore]) { - // MapPopUpStore 배열을 StoreItem 배열로 변환 let storeItems = results.map { store in StoreItem( id: store.id, @@ -1115,8 +1055,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM northEast: bounds.northEast ) } - - // 현재 보이는 지도 영역의 경계를 가져오는 함수 private func getVisibleBounds() -> (northEast: NMGLatLng, southWest: NMGLatLng) { let mapBounds = mainView.mapView.contentBounds @@ -1136,10 +1074,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.mapView.positionMode = .direction // 내 위치 트래킹 모드 활성화 case .denied, .restricted: Logger.log( - message: "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", + "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", category: .error ) - mainView.mapView.positionMode = .disabled // 내 위치 트래킹 모드 비활성화 + mainView.mapView.positionMode = .disabled @unknown default: break } @@ -1147,15 +1085,11 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateTooltipPosition() { guard let marker = currentMarker, let tooltip = currentTooltipView else { return } - - // 마커 위치를 화면 좌표로 변환 let markerPoint = mainView.mapView.projection.point(from: marker.position) var markerCenter = markerPoint - // 마커 높이 고려 (네이버 마커는 크기가 다를 수 있음) - markerCenter.y = markerPoint.y - 20 // 마커 이미지 높이의 절반 정도 + markerCenter.y = markerPoint.y - 20 - // 오프셋 값 (디자인에 맞게 조정) let offsetX: CGFloat = -10 let offsetY: CGFloat = -10 @@ -1167,7 +1101,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func resetSelectedMarker() { if let currentMarker = currentMarker { - // 마커 스타일 업데이트 updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) } @@ -1219,7 +1152,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func findMarkerForStore(for store: MapPopUpStore) -> NMFMarker? { - // individualMarkerDictionary에 저장된 모든 마커를 순회 for marker in individualMarkerDictionary.values { if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore, singleStore.id == store.id { return marker @@ -1229,7 +1161,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return marker } } - // 상세 레벨이 아닐 경우 clusterMarkerDictionary에도 동일하게 검색 for marker in clusterMarkerDictionary.values { if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData, clusterData.cluster.stores.contains(where: { $0.id == store.id }) { @@ -1240,10 +1171,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func fetchStoreDetails(for stores: [MapPopUpStore]) { - // 빈 목록이면 처리하지 않음 guard !stores.isEmpty else { return } - - // 먼저 기본 정보로 StoreItem 생성하여 순서 유지 let initialStoreItems = stores.map { store in StoreItem( id: store.id, @@ -1255,11 +1183,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM isBookmarked: false ) } - - // 리스트에는 모든 스토어 정보 표시 (필터링된 모든 스토어) self.storeListViewController.reactor?.action.onNext(.setStores(initialStoreItems)) - - // 각 스토어의 상세 정보를 병렬로 가져와서 업데이트 (북마크 정보 등) stores.forEach { store in self.popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", @@ -1279,12 +1203,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } func bindViewport(reactor: MapReactor) { - // 카메라 이동 완료 시 이벤트 발생되는 Subject let cameraObservable = PublishSubject() - // NMFMapViewCameraDelegate 메서드에서 호출할 수 있도록 설정 - - // 카메라 변경 감지해서 액션 전달 cameraObservable .throttle(.milliseconds(200), scheduler: MainScheduler.instance) .map { [unowned self] _ -> MapReactor.Action in @@ -1299,7 +1219,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind(to: reactor.action) .disposed(by: disposeBag) - // 현재 뷰포트 내의 스토어 업데이트 - 초기 1회 reactor.state .map { $0.viewportStores } .distinctUntilChanged() @@ -1309,7 +1228,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .subscribe(onNext: { [weak self] stores in guard let self = self else { return } - // 현재 위치가 있으면 가장 가까운 스토어, 없으면 첫 번째 스토어 표시 if let location = self.locationManager.location { self.findAndShowNearestStore(from: location) } else if let firstStore = stores.first, @@ -1317,13 +1235,11 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM _ = self.handleSingleStoreTap(marker, store: firstStore) } - // 현재 스토어 목록 업데이트 및 클러스터링 self.currentStores = stores self.updateMapWithClustering() }) .disposed(by: disposeBag) - // 뷰포트 내 마커 업데이트 및 캐러셀 표시 reactor.state .map { $0.viewportStores } .distinctUntilChanged() @@ -1335,19 +1251,16 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let effectiveViewport = self.getEffectiveViewport() let bounds = self.getVisibleBounds() - // 화면에 보이는 스토어만 필터링 let visibleStores = stores.filter { store in let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast).contains(storePosition) } self.currentStores = visibleStores - // 개별 마커 레벨인지 확인 let currentZoom = self.mainView.mapView.zoomLevel let level = MapZoomLevel.getLevel(from: Float(currentZoom)) if level == .detailed && !visibleStores.isEmpty { - // 캐러셀에 모든 마커 정보 표시 let effectiveStores = visibleStores.filter { store in let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) return effectiveViewport.contains(storePosition) @@ -1358,9 +1271,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.carouselView.isHidden = false self.mainView.setStoreCardHidden(false, animated: true) - // 현재 선택된 마커가 있으면 해당 위치로 스크롤 if let currentMarker = self.currentMarker { - // 마커의 스토어 정보 체크 if let currentStore = currentMarker.userInfo["storeData"] as? MapPopUpStore, let index = visibleStores.firstIndex(where: { $0.id == currentStore.id }) { self.carouselView.scrollToCard(index: index) @@ -1383,7 +1294,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.carouselView.scrollToCard(index: 0) } } else { - // 선택된 마커가 없는 경우, 첫 번째 스토어로 설정 if let firstStore = visibleStores.first, let marker = self.findMarkerForStore(for: firstStore) { self.updateMarkerStyle(marker: marker, selected: true, isCluster: false) @@ -1411,7 +1321,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func findAndShowNearestStore(from location: CLLocation) { guard !currentStores.isEmpty else { - Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) + Logger.log("현재위치 표기할 스토어가 없습니다", category: .debug) return } @@ -1424,17 +1334,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } if let store = nearestStore, let marker = findMarkerForStore(for: store) { - // 카메라 이동 없이 선택된 마커만 업데이트 _ = handleSingleStoreTap(marker, store: store) } - // 마커가 없다면 새로 생성 else if let store = nearestStore { let marker = NMFMarker() marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) marker.userInfo = ["storeData": store] marker.anchor = CGPoint(x: 0.5, y: 1.0) - // 마커 스타일 설정 updateMarkerStyle(marker: marker, selected: true, isCluster: false) marker.mapView = mainView.mapView @@ -1451,18 +1358,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true - // 이전 마커 선택 상태 해제 if let previousMarker = currentMarker { updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) } - - // 새 마커 선택 상태로 설정 updateMarkerStyle(marker: marker, selected: true, isCluster: false) currentMarker = marker - - // 캐러셀에 표시할 스토어 확인 if currentCarouselStores.isEmpty || !currentCarouselStores.contains(where: { $0.id == store.id }) { - // 현재 뷰포트의 모든 스토어를 가져오기 let bounds = getVisibleBounds() let visibleStores = currentStores.filter { store in @@ -1471,21 +1372,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } if !visibleStores.isEmpty { - // 뷰포트의 모든 스토어를 캐러셀에 표시 currentCarouselStores = visibleStores carouselView.updateCards(visibleStores) - // 선택한 스토어의 인덱스를 찾아 스크롤 if let index = visibleStores.firstIndex(where: { $0.id == store.id }) { carouselView.scrollToCard(index: index) } } else { - // 뷰포트에 다른 스토어가 없는 경우, 선택한 스토어만 표시 currentCarouselStores = [store] carouselView.updateCards([store]) } } else { - // 캐러셀에 이미 해당 스토어가 있는 경우, 해당 위치로 스크롤 if let index = currentCarouselStores.firstIndex(where: { $0.id == store.id }) { carouselView.scrollToCard(index: index) } @@ -1494,16 +1391,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.isHidden = false mainView.setStoreCardHidden(false, animated: true) - // 툴팁 처리 if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { - // 마이크로 클러스터인 경우 툴팁 표시 configureTooltip(for: marker, stores: storeArray) - // 해당 스토어의 툴팁 인덱스 선택 if let index = storeArray.firstIndex(where: { $0.id == store.id }) { (currentTooltipView as? MarkerTooltipView)?.selectStore(at: index) } } else { - // 단일 마커인 경우 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil } @@ -1512,53 +1405,36 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return true } - // 리전 클러스터 탭 처리 func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { - print("handleRegionalClusterTap 함수 호출됨") let currentZoom = mainView.mapView.zoomLevel let currentLevel = MapZoomLevel.getLevel(from: Float(currentZoom)) - - // 디버깅 - print("현재 줌 레벨: \(currentZoom), 모드: \(currentLevel)") - print("클러스터 정보: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)") - switch currentLevel { - case .city: // 시 단위 클러스터 - print("시 단위 클러스터 처리") + case .city: let districtZoomLevel: Double = 10.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: districtZoomLevel) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - case .district: // 구 단위 클러스터 - print("구 단위 클러스터 처리") + case .district: let detailedZoomLevel: Double = 12.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: detailedZoomLevel) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) default: - print("기타 레벨 클러스터 처리") + print("기타") } - - // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 updateMarkersForCluster(stores: clusterData.cluster.stores) - - // 캐러셀 업데이트 carouselView.updateCards(clusterData.cluster.stores) carouselView.isHidden = false self.currentCarouselStores = clusterData.cluster.stores - return true } - // 마이크로 클러스터 탭 처리 func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { - // 이미 선택된 마커를 다시 탭할 때 if currentMarker == marker { - // 툴팁과 캐러셀만 숨기고, 마커의 선택 상태는 유지 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] @@ -1567,8 +1443,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.isHidden = true carouselView.updateCards([]) currentCarouselStores = [] - - // 마커 상태 업데이트 updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeArray.count) currentMarker = nil @@ -1594,14 +1468,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.scrollToCard(index: 0) mainView.setStoreCardHidden(false, animated: true) - - // 지도 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - - // 툴팁 생성 if storeArray.count > 1 { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in guard let self = self else { return } @@ -1614,8 +1484,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func showNoMarkersToast() { - // 디자인 예정이므로 임시 구현 - Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) + Logger.log("현재 지도 영역에 표시할 마커가 없습니다", category: .debug) } } @@ -1642,37 +1511,23 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - NMFMapViewTouchDelegate extension MapViewController { - // 마커 탭 이벤트 처리 - // 마커 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { - Logger.log(message: "didTapMarker 호출됨: \(marker.position), userInfo: \(marker.userInfo)", category: .debug) - - // 클러스터 마커 확인 if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { - Logger.log(message: "클러스터 데이터 감지: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)", category: .debug) return handleRegionalClusterTap(marker, clusterData: clusterData) } - // 마이크로 클러스터 또는 단일 스토어 마커 확인 else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { if storeArray.count > 1 { - Logger.log(message: "마이크로 클러스터 감지: \(storeArray.count)개 스토어", category: .debug) return handleMicroClusterTap(marker, storeArray: storeArray) } else if let singleStore = storeArray.first { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } } - // 단일 스토어 마커 (배열이 아닌 경우) 확인 else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } - - Logger.log(message: "인식할 수 없는 마커 타입", category: .error) return false } - // 지도 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { guard !isMovingToMarker else { return } @@ -1681,54 +1536,40 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) self.currentMarker = nil } - - // 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] currentTooltipCoordinate = nil - - // 캐러셀 초기화 carouselView.isHidden = true carouselView.updateCards([]) self.currentCarouselStores = [] mainView.setStoreCardHidden(true, animated: true) - - // 클러스터링 업데이트 updateMapWithClustering() } } // MARK: - NMFMapViewCameraDelegate extension MapViewController { - // 카메라 이동 시작 시 호출 func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) { if reason == NMFMapChangedByGesture && !isMovingToMarker { resetSelectedMarker() } } - - // 카메라 이동 중 호출 func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) { if !isMovingToMarker { currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] updateMapWithClustering() - - // 캐러셀 초기화 carouselView.isHidden = true carouselView.updateCards([]) currentCarouselStores = [] } } - - // 카메라 이동 완료 시 호출 func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) { if let marker = self.currentMarker, let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { - // 툴팁이 없으면 생성, 있으면 위치 업데이트 if self.currentTooltipView == nil { self.configureTooltip(for: marker, stores: storeArray) } else { @@ -1736,37 +1577,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } self.isMovingToMarker = false - - // 뷰포트 변경 이벤트 처리 - idleSubject 통해 알림 idleSubject.onNext(()) - - // 뷰포트 변경 이벤트 처리 - if let reactor = self.reactor { - let bounds = self.getVisibleBounds() - reactor.action.onNext(.viewportChanged( - northEastLat: bounds.northEast.lat, - northEastLon: bounds.northEast.lng, - southWestLat: bounds.southWest.lat, - southWestLon: bounds.southWest.lng - )) - } + cameraIdle.onNext(()) } } - // MARK: - UIGestureRecognizerDelegate extension MapViewController { - // 맵뷰의 다른 제스처와 충돌하지 않도록 함 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - // 맵의 내장 제스처와 동시 인식 허용 return true } - - // 리스트뷰가 보일 때만 커스텀 탭 제스처 허용 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - // 터치가 리스트뷰 영역에 있으면 커스텀 제스처 트리거하지 않음 let touchPoint = touch.location(in: view) - - // 리스트뷰가 보이고 터치가 리스트뷰 위에 있으면 탭 처리하지 않음 if modalState != .bottom { let listViewY = storeListViewController.view.frame.minY if touchPoint.y > listViewY { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index 054506ce..d335352d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -80,7 +80,7 @@ final class StoreListReactor: Reactor { // Int64 → Int32 변환 필요 guard let idInt32 = Int32(exactly: store.id) else { - Logger.log(message: "ID 값이 Int32 범위를 초과했습니다: \(store.id)", category: .error) + Logger.log("ID 값이 Int32 범위를 초과했습니다: \(store.id)", category: .error) return .empty() } @@ -192,7 +192,7 @@ final class StoreListReactor: Reactor { newState.stores[index].isBookmarked = isBookmarked Logger.log( - message: """ + """ 북마크 상태 변경: - 스토어명: \(store.title) - ID: \(store.id) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index 9dc41725..988ef8e7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -220,7 +220,7 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo func showCamera() { guard UIImagePickerController.isSourceTypeAvailable(.camera) else { - Logger.log(message: "카메라를 사용할 수 없습니다.", category: .error) + Logger.log("카메라를 사용할 수 없습니다.", category: .error) return } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift index 23bebd96..a4bd93be 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift @@ -126,10 +126,8 @@ class ClusteringManager { let combined = Array(seoulClusters.values) + Array(gyeonggiClusters.values) + Array(otherClusters.values) let filtered = combined.filter { $0.storeCount > 0 } - - Logger.log(message: "구 단위 클러스터 생성 결과: \(filtered.count)개", category: .debug) for cluster in filtered { - Logger.log(message: "- \(cluster.base.name): \(cluster.storeCount)개 매장", category: .debug) + Logger.log("- \(cluster.base.name): \(cluster.storeCount)개 매장", category: .debug) } return filtered.map { $0.toMarkerData() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift index 42a777dd..50fd3629 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift @@ -9,7 +9,7 @@ class BaseTabmanController: TabmanViewController { init() { super.init(nibName: nil, bundle: nil) Logger.log( - message: "\(self) init", + "\(self) init", category: .info, fileName: #file, line: #line @@ -28,7 +28,7 @@ class BaseTabmanController: TabmanViewController { deinit { Logger.log( - message: "\(self) deinit", + "\(self) deinit", category: .info, fileName: #file, line: #line diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift index 03eeded3..df7b6df0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift @@ -13,7 +13,7 @@ public class BaseViewController: UIViewController { public init() { super.init(nibName: nil, bundle: nil) Logger.log( - message: "\(self) init", + "\(self) init", category: .info, fileName: #file, line: #line @@ -38,7 +38,7 @@ public class BaseViewController: UIViewController { deinit { Logger.log( - message: "\(self) deinit", + "\(self) deinit", category: .info, fileName: #file, line: #line diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index c37a5e4b..64e07d8b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -67,7 +67,7 @@ extension SectionSupplementaryItemable { for: indexPath ) as? ReusableView else { Logger.log( - message: "ReusableView Error", + "ReusableView Error", category: .error, fileName: #file, line: #line diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift index 7153b6a7..ae219e92 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift @@ -131,7 +131,7 @@ extension Sectionable { for: indexPath ) as? CellType else { Logger.log( - message: "dequeueReusableCell Fail", + "dequeueReusableCell Fail", category: .error, fileName: #file, line: #line @@ -158,7 +158,7 @@ extension Sectionable { guard let item = supplementaryItems?.filter({ $0.elementKind == kind }).first else { Logger.log( - message: "ReusableView Not Register", + "ReusableView Not Register", category: .error, fileName: #file, line: #line From 8f3e6752bde3ae4f939468fe663054dbfccb2306 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 30 Apr 2025 11:02:36 +0000 Subject: [PATCH 202/393] style/#127: Apply SwiftLint autocorrect --- .../Data/Data/Network/Provider/ProviderImpl.swift | 5 ----- .../Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift | 1 - .../Scene/Map/MapView/MapViewController.swift | 9 +++------ 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index c0909032..e46126b9 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -22,8 +22,6 @@ public final class ProviderImpl: Provider { /// 1) endpoint -> urlRequest 생성 let urlRequest = try endpoint.getUrlRequest() - - let request = AF.request(urlRequest, interceptor: interceptor) .validate() .responseData { [weak self] response in @@ -92,7 +90,6 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.getUrlRequest() - self.executeRequest(urlRequest, interceptor: interceptor) { response in Logger.log( "응답 시각 :\(Date())", @@ -141,7 +138,6 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.asURLRequest() - AF.upload(multipartFormData: { multipartFormData in request.asMultipartFormData(multipartFormData: multipartFormData) Logger.log("업로드 시각 :\(Date())", category: .network) @@ -175,7 +171,6 @@ private extension ProviderImpl { interceptor: RequestInterceptor?, completion: @escaping (AFDataResponse) -> Void ) { - AF.request(urlRequest, interceptor: interceptor) .validate() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index e31f9c61..2bff7fda 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -121,7 +121,6 @@ final class MapGuideReactor: Reactor { return Observable.just(.showToast("지원하지 않는 맵 앱입니다.")) } - if let url = URL(string: urlScheme) { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 63194f04..6e6923b8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1335,8 +1335,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let store = nearestStore, let marker = findMarkerForStore(for: store) { _ = handleSingleStoreTap(marker, store: store) - } - else if let store = nearestStore { + } else if let store = nearestStore { let marker = NMFMarker() marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) marker.userInfo = ["storeData": store] @@ -1514,15 +1513,13 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { return handleRegionalClusterTap(marker, clusterData: clusterData) - } - else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { + } else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { if storeArray.count > 1 { return handleMicroClusterTap(marker, storeArray: storeArray) } else if let singleStore = storeArray.first { return handleSingleStoreTap(marker, store: singleStore) } - } - else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { + } else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { return handleSingleStoreTap(marker, store: singleStore) } return false From 4444ed3d768ad4398c3ed2baa5a02367503a17f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 30 Apr 2025 20:00:34 +0900 Subject: [PATCH 203/393] =?UTF-8?q?feat/#127:=20logLevel=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20,=20=EB=A1=9C=EA=B1=B0=20=ED=82=A4=20enum=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20print=20?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=A0=9C=EA=B1=B0=20=EB=A1=9C=EA=B1=B0?= =?UTF-8?q?=EC=B6=9C=EB=A0=A5=EC=8B=9C=20message=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 4 + .../Infrastructure/Logger/Logger.swift | 71 ++-- .../Service/KeyChainService.swift | 19 +- .../PreSignedAPI/PreSignedAPIEndPoint.swift | 6 +- .../Data/Network/Common/Requestable.swift | 6 +- .../Network/EndPoint/MultipartEndPoint.swift | 4 +- .../Interceptor/TokenInterceptor.swift | 4 +- .../Data/Network/Provider/ProviderImpl.swift | 57 +--- .../Network/Service/AppleLoginService.swift | 16 +- .../Network/Service/KakaoLoginService.swift | 14 +- .../Network/Service/PreSignedService.swift | 48 +-- .../RepositoryImpl/MapRepositoryImpl.swift | 6 +- .../Domain/UseCaseImpl/AdminUseCaseImpl.swift | 12 +- .../Domain/UseCaseImpl/MapUseCaseImpl.swift | 8 +- Poppool/Poppool.xcodeproj/project.pbxproj | 1 - .../xcshareddata/xcschemes/Poppool.xcscheme | 4 +- .../Marker/Marker.imageset/Contents.json | 4 +- ...353\263\265\354\202\254\353\263\270 2.png" | Bin 1976 -> 0 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 1976 -> 0 bytes .../Marker/Marker.imageset/solid.png | Bin 1976 -> 0 bytes .../Marker/Marker.imageset/solid.svg | 4 + .../Marker/TapMarker.imageset/Contents.json | 4 +- ...353\263\265\354\202\254\353\263\270 2.png" | Bin 2698 -> 0 bytes ... \353\263\265\354\202\254\353\263\270.png" | Bin 2698 -> 0 bytes .../Marker/TapMarker.imageset/solid.png | Bin 2698 -> 0 bytes .../Marker/TapMarker.imageset/solid.svg | 4 + .../AdminBottomSheetView.swift | 4 +- .../AdminBottomSheetViewController.swift | 8 +- .../Scene/Admin/AdminReactor.swift | 4 +- .../PopUpStoreRegisterReactor.swift | 48 +-- .../Scene/Admin/AdminStoreCell.swift | 4 +- .../Scene/Admin/AdminViewController.swift | 2 +- .../CommentDetail/CommentDetailReactor.swift | 4 +- .../CommentList/CommentListReactor.swift | 2 +- .../NormalCommentAddReactor.swift | 4 +- .../NormalCommentEditReactor.swift | 4 +- .../Scene/Detail/DetailReactor.swift | 10 +- .../BalloonBackgroundView.swift | 16 +- .../FillterSheetView/BalloonChipCell.swift | 4 +- .../Map/FillterSheetView/FilterChip.swift | 2 +- .../FullScreenMapViewController.swift | 4 +- .../MapGuideView/MapGuideReactor.swift | 4 +- .../Scene/Map/MapView/MapMarker.swift | 55 ++- .../Scene/Map/MapView/MapReactor.swift | 6 +- .../Scene/Map/MapView/MapViewController.swift | 313 ++++-------------- .../Map/StoreListView/StoreListReactor.swift | 4 +- .../Main/ProfileEditController.swift | 2 +- .../Utills/Common/ClusteringManager.swift | 4 +- .../Controllers/BaseTabmanController.swift | 12 +- .../Controllers/BaseViewController.swift | 12 +- .../SectionSupplementaryItem.swift | 7 +- .../Interfaces/Sectionable/Sectionable.swift | 12 +- 52 files changed, 317 insertions(+), 530 deletions(-) delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" delete mode 100644 "Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270.png" delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid.svg diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index d5ab556c..75cc80c7 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D62DB67B4F00B141FF /* RxSwift */; }; 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; + 4EBC91D32DB8039800495C3B /* OSLog.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EBC91D22DB8039800495C3B /* OSLog.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -27,6 +28,7 @@ /* Begin PBXFileReference section */ 058CC9182DB5383C0084221A /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4EBC91D22DB8039800495C3B /* OSLog.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OSLog.framework; path = System/Library/Frameworks/OSLog.framework; sourceTree = SDKROOT; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ @@ -42,6 +44,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4EBC91D32DB8039800495C3B /* OSLog.framework in Frameworks */, 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */, 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, @@ -54,6 +57,7 @@ 05125B6A2DB56C32001342A2 /* Frameworks */ = { isa = PBXGroup; children = ( + 4EBC91D22DB8039800495C3B /* OSLog.framework */, ); name = Frameworks; sourceTree = ""; diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift index 122f328b..977637ac 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift @@ -4,13 +4,13 @@ import OSLog public struct Logger { private static let subsystem = Bundle.main.bundleIdentifier ?? "com.poppoolIOS.poppool" - public enum Level { + public enum Level: Hashable { case info case debug case network case error case event - case custom(categoryName: String) + case custom(name: String) var categoryName: String { switch self { @@ -24,8 +24,7 @@ public struct Logger { return "Error" case .event: return "Event" - case .custom(let categoryName): - return categoryName + case .custom(let name): return name } } @@ -45,70 +44,64 @@ public struct Logger { return "🍎" } } + } + + public enum LogLevel { + case debug + case info + case error + case fault var osLogType: OSLogType { switch self { case .debug: return .debug - case .info, .event: + case .info: return .info - case .network: - return .default case .error: return .error - case .custom: - return .default + case .fault: + return .fault } } } - static var isShowFileName: Bool = false // 파일 이름 포함여부 - static var isShowLine: Bool = true // 라인 번호 포함 여부 - static var isShowLog: Bool = true + /// : 아래 옵션 주석 해제시 파일명/라인 번호를 로그 메시지에 포함 + // private static var isShowFileName: Bool = false // 파일 이름 포함 여부 + // private static var isShowLine: Bool = true // 라인 번호 포함 여부 + private static var isShowLog: Bool = true - private static var loggers: [String: os.Logger] = [:] + private static var loggers: [Level: os.Logger] = [:] private static func getLogger(for category: Level) -> os.Logger { let categoryName = category.categoryName - if let cachedLogger = loggers[categoryName] { + if let cachedLogger = loggers[category] { return cachedLogger } let logger = os.Logger(subsystem: subsystem, category: categoryName) - loggers[categoryName] = logger + loggers[category] = logger return logger } + /// : 파일명과 라인 정보 파라미터 포함 + // public static func log( + // _ message: Any, + // category: Level, + // level: LogLevel = .info, + // fileName: String = #file, + // line: Int = #line + // ) { public static func log( - message: Any, + _ message: Any, category: Level, - fileName: String = #file, - line: Int = #line + level: LogLevel = .info ) { guard isShowLog else { return } let logger = getLogger(for: category) - var fullMessage = "\(category.categoryIcon) \(message)" + let fullMessage = "\(category.categoryIcon) \(message)" - if isShowFileName { - guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } - fullMessage += " | 📁 \(fileNameOnly)" - } - - if isShowLine { - fullMessage += " | 📍 \(line)" - } - - logger.log(level: category.osLogType, "\(fullMessage, privacy: .public)") - - // 디버깅 시 Xcode 콘솔에서도 바로 확인할 수 있도록 print도 함께 사용 불필요시 제거 - print("\(category.categoryIcon) [\(category.categoryName)]: \(message)") - if isShowFileName { - guard let fileNameOnly = fileName.components(separatedBy: "/").last else { return } - print(" \(category.categoryIcon) [FileName]: \(fileNameOnly)") - } - if isShowLine { - print(" \(category.categoryIcon) [Line]: \(line)") - } + logger.log(level: level.osLogType, "\(fullMessage, privacy: .public)") } } diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift index bc44ded4..822263e7 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/KeyChainService.swift @@ -43,10 +43,8 @@ public final class KeyChainService { if let data = dataTypeRef as? Data { if let value = String(data: data, encoding: .utf8) { Logger.log( - message: "Successfully fetched \(type.rawValue) from KeyChain: \(value)", - category: .info, - fileName: #file, - line: #line + "Successfully fetched \(type.rawValue) from KeyChain", + category: .info ) return .success(value) } else { @@ -84,10 +82,9 @@ public final class KeyChainService { let status = SecItemAdd(keyChainQuery, nil) if status == errSecSuccess { Logger.log( - message: "Successfully saved \(type.rawValue) to KeyChain: \(value)", - category: .info, - fileName: #file, - line: #line + "Successfully fetched \(type.rawValue) from KeyChain: \(value)", + category: .info + ) return .success(()) } else { @@ -111,10 +108,8 @@ public final class KeyChainService { if status == errSecSuccess { Logger.log( - message: "Successfully deleted \(type.rawValue) from KeyChain", - category: .info, - fileName: #file, - line: #line + "Successfully deleted \(type.rawValue) from KeyChain", + category: .info ) return .success(()) } else { diff --git a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift index 21d930f2..47bd16a8 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/PreSignedAPI/PreSignedAPIEndPoint.swift @@ -4,7 +4,7 @@ import Infrastructure struct PreSignedAPIEndPoint { static func presigned_upload(request: PresignedURLRequestDTO) -> Endpoint { - Logger.log(message: "Presigned URL 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned URL 생성 - Request: \(request)", category: .debug) return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/upload-preSignedUrl", @@ -14,7 +14,7 @@ struct PreSignedAPIEndPoint { } static func presigned_download(request: PresignedURLRequestDTO) -> Endpoint { - Logger.log(message: "Presigned Download URL 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned Download URL 생성 - Request: \(request)", category: .debug) return Endpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/download-preSignedUrl", @@ -24,7 +24,7 @@ struct PreSignedAPIEndPoint { } static func presigned_delete(request: PresignedURLRequestDTO) -> RequestEndpoint { - Logger.log(message: "Presigned Delete 생성 - Request: \(request)", category: .debug) + Logger.log("Presigned Delete 생성 - Request: \(request)", category: .debug) return RequestEndpoint( baseURL: Secrets.popPoolBaseURL, path: "/files/delete", diff --git a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift index 2709e94c..2172c43b 100644 --- a/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift +++ b/Poppool/DataLayer/Data/Data/Network/Common/Requestable.swift @@ -21,10 +21,8 @@ extension Requestable { let url = try url() Logger.log( - message: "\(url) URL 생성", - category: .network, - fileName: #file, - line: #line + "\(url) URL 생성", + category: .network ) var urlRequest = URLRequest(url: url) diff --git a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift index 00a5bf3c..4c9e7011 100644 --- a/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift +++ b/Poppool/DataLayer/Data/Data/Network/EndPoint/MultipartEndPoint.swift @@ -34,7 +34,7 @@ public class MultipartEndPoint: URLRequestConvertible { public func asURLRequest() throws -> URLRequest { let url = try baseURL.asURL().appendingPathComponent(path) var request = URLRequest(url: url) - Logger.log(message: "\(request) URL 생성", category: .network) + Logger.log("\(request) URL 생성", category: .network) request.method = method if let headers = headers { @@ -57,7 +57,7 @@ public class MultipartEndPoint: URLRequestConvertible { multipartFormData.append(jsonString.data(using: .utf8)!, withName: "data") } } catch { - Logger.log(message: "JSON 변환 오류: \(error)", category: .network) + Logger.log("JSON 변환 오류: \(error)", category: .network) } } diff --git a/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift b/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift index 5dc2418b..2f1c68dc 100644 --- a/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift +++ b/Poppool/DataLayer/Data/Data/Network/Interceptor/TokenInterceptor.swift @@ -11,7 +11,7 @@ final class TokenInterceptor: RequestInterceptor { _ urlRequest: URLRequest, for session: Session, completion: @escaping (Result) -> Void) { - Logger.log(message: "TokenInterceptor Adapt Token", category: .network) + Logger.log("TokenInterceptor Adapt Token", category: .network) @Dependency var keyChainService: KeyChainService var urlRequest = urlRequest let accessTokenResult = keyChainService.fetchToken(type: .accessToken) @@ -32,7 +32,7 @@ final class TokenInterceptor: RequestInterceptor { dueTo error: any Error, completion: @escaping (RetryResult) -> Void ) { - Logger.log(message: "TokenInterceptor Retry Start", category: .network) + Logger.log("TokenInterceptor Retry Start", category: .network) completion(.doNotRetry) } } diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index e3d925ed..c0909032 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -22,22 +22,13 @@ public final class ProviderImpl: Provider { /// 1) endpoint -> urlRequest 생성 let urlRequest = try endpoint.getUrlRequest() - Logger.log( - message: """ - [Provider] 최종 요청 URL: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - - Headers: \(urlRequest.allHTTPHeaderFields ?? [:]) - 요청 시각: \(Date()) - """, - category: .debug - ) + let request = AF.request(urlRequest, interceptor: interceptor) .validate() .responseData { [weak self] response in Logger.log( - message: """ + """ [Provider] 응답 수신: - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - 응답 시각: \(Date()) @@ -63,14 +54,14 @@ public final class ProviderImpl: Provider { observer.onCompleted() } catch { Logger.log( - message: "디코딩 실패: \(error.localizedDescription)", + "디코딩 실패: \(error.localizedDescription)", category: .error ) observer.onError(NetworkError.decodeError) } case .failure(let error): - Logger.log(message: "요청 실패 Error:\(error)", category: .error) + Logger.log("요청 실패 Error:\(error)", category: .error) observer.onError(error) } } @@ -80,7 +71,7 @@ public final class ProviderImpl: Provider { } } catch { - Logger.log(message: "[Provider] URLRequest 생성 실패: \(error.localizedDescription)", category: .error) + Logger.log("[Provider] URLRequest 생성 실패: \(error.localizedDescription)", category: .error) observer.onError(NetworkError.urlRequest(error)) return Disposables.create() } @@ -101,19 +92,10 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.getUrlRequest() - Logger.log( - message: """ - [Provider] 최종 요청 URL(Completable): - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - 요청 시각: \(Date()) - """, - category: .debug - ) self.executeRequest(urlRequest, interceptor: interceptor) { response in Logger.log( - message: "응답 시각 :\(Date())", + "응답 시각 :\(Date())", category: .network ) @@ -132,12 +114,12 @@ public final class ProviderImpl: Provider { case .success: observer(.completed) case .failure(let error): - Logger.log(message: "요청 실패 Error:\(error)", category: .error) + Logger.log("요청 실패 Error:\(error)", category: .error) observer(.error(self.handleRequestError(response: response, error: error))) } } } catch { - Logger.log(message: "[Provider] URLRequest 생성 실패 (Completable): \(error.localizedDescription)", category: .error) + Logger.log("[Provider] URLRequest 생성 실패 (Completable): \(error.localizedDescription)", category: .error) observer(.error(NetworkError.urlRequest(error))) } @@ -158,23 +140,16 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.asURLRequest() - Logger.log( - message: """ - [Provider] 이미지 업로드 요청: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - - Method: \(urlRequest.httpMethod ?? "알 수 없음") - """, - category: .network - ) + AF.upload(multipartFormData: { multipartFormData in request.asMultipartFormData(multipartFormData: multipartFormData) - Logger.log(message: "업로드 시각 :\(Date())", category: .network) + Logger.log("업로드 시각 :\(Date())", category: .network) }, with: urlRequest, interceptor: interceptor) .validate() .response { response in Logger.log( - message: "이미지 업로드 응답 시각 :\(Date())", + "이미지 업로드 응답 시각 :\(Date())", category: .network ) switch response.result { @@ -200,15 +175,7 @@ private extension ProviderImpl { interceptor: RequestInterceptor?, completion: @escaping (AFDataResponse) -> Void ) { - // 여기서도 최종 URL 찍을 수 있음 - Logger.log( - message: """ - [Provider] executeRequest: - - URL: \(urlRequest.url?.absoluteString ?? "URL이 없습니다.") - 요청 시각: \(Date()) - """, - category: .debug - ) + AF.request(urlRequest, interceptor: interceptor) .validate() diff --git a/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift index 18458103..a119d861 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/AppleLoginService.swift @@ -37,10 +37,8 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi let windowSecne = scenes.first as? UIWindowScene guard let window = windowSecne?.windows.first else { Logger.log( - message: "\(#function) UIWindow fetch Fail", - category: .error, - fileName: #file, - line: #line + "\(#function) UIWindow fetch Fail", + category: .error ) return UIWindow() } @@ -71,8 +69,8 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi guard let convertAuthorizationCode = String(data: authorizationCode, encoding: .utf8) else { return } - Logger.log(message: "IDToken: \(idToken)", category: .info) - Logger.log(message: "Auth Code: \(convertAuthorizationCode)", category: .info) + Logger.log("IDToken: \(idToken)", category: .info) + Logger.log("Auth Code: \(convertAuthorizationCode)", category: .info) authServiceResponse.onNext(.init(idToken: idToken, authorizationCode: convertAuthorizationCode)) default: break @@ -84,10 +82,8 @@ extension AppleLoginService: ASAuthorizationControllerPresentationContextProvidi didCompleteWithError error: Error ) { Logger.log( - message: "AppleLogin Fail", - category: .error, - fileName: #file, - line: #line + "AppleLogin Fail", + category: .error ) authServiceResponse.onError(error) } diff --git a/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift b/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift index 634cf838..4427a467 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/KakaoLoginService.swift @@ -16,7 +16,7 @@ public final class KakaoLoginService: AuthServiceable { UserApi.shared.unlink { error in if let error = error { observer.onNext(()) - Logger.log(message: error.localizedDescription, category: .error) + Logger.log(error.localizedDescription, category: .error) } else { observer.onNext(()) observer.onCompleted() @@ -31,10 +31,8 @@ public final class KakaoLoginService: AuthServiceable { return Observable.create { [weak self] observer in guard let self else { Logger.log( - message: "KakaoTalk login Error", - category: .error, - fileName: #file, - line: #line + "KakaoTalk login Error", + category: .error ) return Disposables.create() } @@ -42,10 +40,8 @@ public final class KakaoLoginService: AuthServiceable { // 카카오톡 설치 유무 확인 guard UserApi.isKakaoTalkLoginAvailable() else { Logger.log( - message: "KakaoTalk is not install", - category: .error, - fileName: #file, - line: #line + "KakaoTalk is not install", + category: .error ) // 카카오톡 미설치시 웹으로 인증 시도 diff --git a/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift index 3609bc2b..017c76c4 100644 --- a/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift +++ b/Poppool/DataLayer/Data/Data/Network/Service/PreSignedService.swift @@ -29,20 +29,20 @@ class PreSignedService { } func tryUpload(datas: [PresignedURLRequest]) -> Single { - Logger.log(message: "tryUpload 호출됨 - 요청 데이터 수: \(datas.count)", category: .debug) + Logger.log("tryUpload 호출됨 - 요청 데이터 수: \(datas.count)", category: .debug) return Single.create { [weak self] observer in - Logger.log(message: "tryUpload 내부 흐름 시작", category: .debug) + Logger.log("tryUpload 내부 흐름 시작", category: .debug) guard let self = self else { - Logger.log(message: "self가 nil입니다. 작업을 중단합니다.", category: .error) + Logger.log("self가 nil입니다. 작업을 중단합니다.", category: .error) return Disposables.create() } // 1. 업로드 링크 요청 self.getUploadLinks(request: .init(objectKeyList: datas.map { $0.filePath })) .subscribe { response in - Logger.log(message: "getUploadLinks 성공: \(response.preSignedUrlList)", category: .debug) + Logger.log("getUploadLinks 성공: \(response.preSignedUrlList)", category: .debug) let responseList = response.preSignedUrlList let inputList = datas @@ -51,23 +51,23 @@ class PreSignedService { let requestList = zip(responseList, inputList).compactMap { zipResponse in let urlResponse = zipResponse.0 let inputResponse = zipResponse.1 - Logger.log(message: "업로드 준비 - URL: \(urlResponse.preSignedUrl)", category: .debug) + Logger.log("업로드 준비 - URL: \(urlResponse.preSignedUrl)", category: .debug) return self.uploadFromS3(url: urlResponse.preSignedUrl, image: inputResponse.image) } // 3. 병렬 업로드 실행 Single.zip(requestList) .subscribe(onSuccess: { _ in - Logger.log(message: "모든 이미지 업로드 성공", category: .info) + Logger.log("모든 이미지 업로드 성공", category: .info) observer(.success(())) }, onFailure: { error in - Logger.log(message: "이미지 업로드 실패: \(error.localizedDescription)", category: .error) + Logger.log("이미지 업로드 실패: \(error.localizedDescription)", category: .error) observer(.failure(error)) }) .disposed(by: self.disposeBag) } onError: { error in - Logger.log(message: "getUploadLinks 실패: \(error.localizedDescription)", category: .error) + Logger.log("getUploadLinks 실패: \(error.localizedDescription)", category: .error) observer(.failure(error)) } .disposed(by: self.disposeBag) @@ -123,16 +123,16 @@ class PreSignedService { return filePaths.compactMap { imageMap[$0] } } .subscribe(onSuccess: { sortedImages in - Logger.log(message: "All images downloaded successfully", category: .info) + Logger.log("All images downloaded successfully", category: .info) observer(.success(sortedImages)) }, onFailure: { error in - Logger.log(message: "Image download failed: \(error.localizedDescription)", category: .error) + Logger.log("Image download failed: \(error.localizedDescription)", category: .error) observer(.failure(error)) }) .disposed(by: self.disposeBag) } onError: { error in - Logger.log(message: "getDownloadLinks Fail: \(error.localizedDescription)", category: .error) + Logger.log("getDownloadLinks Fail: \(error.localizedDescription)", category: .error) observer(.failure(error)) } .disposed(by: disposeBag) @@ -148,7 +148,7 @@ private extension PreSignedService { return Single.create { single in if let imageData = image.jpegData(compressionQuality: 0), let url = URL(string: url) { - Logger.log(message: "S3 업로드 요청 URL: \(url.absoluteString)", category: .debug) + Logger.log("S3 업로드 요청 URL: \(url.absoluteString)", category: .debug) let headers: HTTPHeaders = [ "Content-Type": "image/jpeg" @@ -156,19 +156,19 @@ private extension PreSignedService { AF.upload(imageData, to: url, method: .put, headers: headers) .response { response in - Logger.log(message: "S3 업로드 응답 상태: \(response.response?.statusCode ?? -1)", category: .debug) + Logger.log("S3 업로드 응답 상태: \(response.response?.statusCode ?? -1)", category: .debug) switch response.result { case .success: - Logger.log(message: "S3 업로드 성공 - URL: \(url.absoluteString)", category: .info) + Logger.log("S3 업로드 성공 - URL: \(url.absoluteString)", category: .info) single(.success(())) case .failure(let error): - Logger.log(message: "S3 업로드 실패: \(error.localizedDescription)", category: .error) + Logger.log("S3 업로드 실패: \(error.localizedDescription)", category: .error) single(.failure(error)) } } return Disposables.create() } else { - Logger.log(message: "S3 업로드 실패 - 잘못된 URL 또는 데이터", category: .error) + Logger.log("S3 업로드 실패 - 잘못된 URL 또는 데이터", category: .error) single(.failure(NSError(domain: "InvalidDataOrURL", code: -1, userInfo: nil))) return Disposables.create() } @@ -197,13 +197,13 @@ private extension PreSignedService { } func getUploadLinks(request: PresignedURLRequestDTO) -> Observable { - Logger.log(message: "Presigned URL 생성 요청 데이터: \(request)", category: .debug) + Logger.log("Presigned URL 생성 요청 데이터: \(request)", category: .debug) let endPoint = PreSignedAPIEndPoint.presigned_upload(request: request) return provider.requestData(with: endPoint, interceptor: tokenInterceptor) .do(onNext: { response in - Logger.log(message: "Presigned URL 응답 데이터: \(response.preSignedUrlList)", category: .debug) + Logger.log("Presigned URL 응답 데이터: \(response.preSignedUrlList)", category: .debug) }, onError: { error in - Logger.log(message: "Presigned URL 요청 실패: \(error.localizedDescription)", category: .error) + Logger.log("Presigned URL 요청 실패: \(error.localizedDescription)", category: .error) }) } @@ -214,18 +214,18 @@ private extension PreSignedService { } extension PreSignedService { func deleteImage(filePath: String, completion: @escaping (Result) -> Void) { - Logger.log(message: "이미지 삭제 시작 - 경로: \(filePath)", category: .debug) + Logger.log("이미지 삭제 시작 - 경로: \(filePath)", category: .debug) let request = PresignedURLRequestDTO(objectKeyList: [filePath]) tryDelete(targetPaths: request) .subscribe( onCompleted: { - Logger.log(message: "이미지 삭제 성공: \(filePath)", category: .debug) + Logger.log("이미지 삭제 성공: \(filePath)", category: .debug) completion(.success(())) }, onError: { error in - Logger.log(message: "이미지 삭제 실패: \(error.localizedDescription)", category: .error) + Logger.log("이미지 삭제 실패: \(error.localizedDescription)", category: .error) completion(.failure(error)) } ) @@ -285,12 +285,12 @@ extension PreSignedService { guard let encodedPath = filePath .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)? .replacingOccurrences(of: "+", with: "%2B") else { - Logger.log(message: "URL 인코딩 실패: \(filePath)", category: .error) + Logger.log("URL 인코딩 실패: \(filePath)", category: .error) return nil } let fullString = baseURL + encodedPath - Logger.log(message: "생성된 URL: \(fullString)", category: .debug) + Logger.log("생성된 URL: \(fullString)", category: .debug) return URL(string: fullString) } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 236aad21..74f1b2ea 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -48,7 +48,7 @@ public final class MapRepositoryImpl: MapRepository { } public func fetchCategories() -> Observable<[CategoryResponse]> { - Logger.log(message: "카테고리 목록 요청을 시작합니다.", category: .network) + Logger.log("카테고리 목록 요청을 시작합니다.", category: .network) return provider.requestData( with: SignUpAPIEndpoint.signUp_getCategoryList(), @@ -56,7 +56,7 @@ public final class MapRepositoryImpl: MapRepository { ) .do(onNext: { _ in Logger.log( - message: "카테고리 목록 응답 성공", + "카테고리 목록 응답 성공", category: .debug ) }) @@ -65,7 +65,7 @@ public final class MapRepositoryImpl: MapRepository { } .catch { error in Logger.log( - message: "카테고리 목록 요청 실패: \(error.localizedDescription)", + "카테고리 목록 요청 실패: \(error.localizedDescription)", category: .error ) throw error diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift index de49b176..c2482298 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/AdminUseCaseImpl.swift @@ -22,26 +22,26 @@ public final class AdminUseCaseImpl: AdminUseCase { } public func createStore(params: CreateStoreParams) -> Completable { - Logger.log(message: "createStore 호출 - 스토어명: \(params.name)", category: .debug) + Logger.log("createStore 호출 - 스토어명: \(params.name)", category: .debug) return repository.createStore(params: params) .do(onError: { error in - Logger.log(message: "createStore 실패 - Error: \(error)", category: .error) + Logger.log("createStore 실패 - Error: \(error)", category: .error) }, onCompleted: { - Logger.log(message: "createStore 성공", category: .info) + Logger.log("createStore 성공", category: .info) }) } public func updateStore(params: UpdateStoreParams) -> Completable { - Logger.log(message: """ + Logger.log(""" Updating store with location: Latitude: \(params.latitude) Longitude: \(params.longitude) """, category: .debug) return repository.updateStore(params: params) .do(onError: { error in - Logger.log(message: "Store update failed: \(error)", category: .error) + Logger.log("Store update failed: \(error)", category: .error) }, onCompleted: { - Logger.log(message: "Store update successful", category: .debug) + Logger.log("Store update successful", category: .debug) }) } diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index 5253690e..457d8a8c 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -32,9 +32,9 @@ public final class MapUseCaseImpl: MapUseCase { categories: categories ) .do(onNext: { stores in - Logger.log(message: "맵 범위 내 스토어 \(stores.count)개 로드됨", category: .debug) + Logger.log("맵 범위 내 스토어 \(stores.count)개 로드됨", category: .debug) }, onError: { error in - Logger.log(message: "맵 범위 내 스토어 로드 실패: \(error)", category: .error) + Logger.log("맵 범위 내 스토어 로드 실패: \(error)", category: .error) }) } @@ -47,9 +47,9 @@ public final class MapUseCaseImpl: MapUseCase { categories: categories ) .do(onNext: { stores in - Logger.log(message: "'\(query)' 검색 결과 \(stores.count)개 로드됨", category: .debug) + Logger.log("'\(query)' 검색 결과 \(stores.count)개 로드됨", category: .debug) }, onError: { error in - Logger.log(message: "스토어 검색 실패: \(error)", category: .error) + Logger.log("스토어 검색 실패: \(error)", category: .error) }) } diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index db866640..a858dfe7 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -58,7 +58,6 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( /Localized/Resource/LaunchScreen.storyboard, - Resource/Debug.xcconfig, Resource/Info.plist, ); target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; diff --git a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme index 9b875b57..77d7ce2e 100644 --- a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme +++ b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme @@ -63,7 +63,9 @@ ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" - allowLocationSimulation = "YES"> + allowLocationSimulation = "YES" + consoleMode = "0" + structuredConsoleMode = "1"> ^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid \353\263\265\354\202\254\353\263\270.png" deleted file mode 100644 index 10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.png deleted file mode 100644 index 10ce11cd49f57af3c0b8e3ab9f74cd158d47b3fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1976 zcmZvdc|6k%9LImZ9c;#0^f;DW6PZNhXt5?dCd`rNNj<|nW(tkRbdY1DC!skbnaAj$ zcqCap9?Bdg)UaGFWk{&8m2@~C{r~*&xxTO0_wV;bZ;Gpm5>^QS02Mc~v+qW-e+Z$l z;qAoWo{d08laH_fV5j>Jfj1lqZ*3ewmappppjN0oyD?y4j-HMHa5o#Z5)20bMNc

hTI^HlF|7r;~wYAr9}nd@wr0-4D4Lr-iSgpJfoDLNYN2N_DfRpG%hw={Zjs4;S7& zm(4>go@j<-?fyTLA;~GD1wxxZ>lojkaZ0fi*Z#mh!tzkEYK2ijd@!*mw~rricB%Vv zb%jC-$`Ys(1g+yb`Is^k+?a0?JAzhI2L6rQC0Y?E{$bL=M&JQ-!k}i+Q7TLY_pa>7 zjHSMW^ZlPy>91qk5NTh*oPjSz@42_84$;c{Y>n_nR@A`aS<}?%W;DCpEl$`lRm2N} zxBRu9A_)a*ZZ6Gan5=BpbM%|=h?UN6(!?ZP6j(XI<6Q7}NtN_KtaE{JU{Mq&@We)r3R(BG|>zFzXs(tZ!^qz|1I*Du`E_wVEt+T$ zjRlsCN{n$TESgV~#5|#qKQBC~279r9@50z1LA71B#e_N7)u1HEP69oANv3hlOoK>R zhOb3(wcCxX_R<%zu@k4Z&=^kyd8fuh_O%3jyT{Tnco9^Gg$>oVGVD<7A&!=5CeHtx zf3z-IzzN2TuG!QVBkOgiU(e~pr~^ma?EF6kI7+MD-MP@&79tj9Ndqz|TsXTJ(Uk(P zJP1$2^b9{)e+LC3Io>Z+QB5X-ALjShtI_Y*x*0o$vV*$6 zSPAY-yvXv`+I|~qh<=o01$O^dJZm-Cc1a3nrwPvt(`)Wy+hHm6DRFHP!P zh|A0dlbWmZN?hVw{n)B@Z}$Km`+0O?{3k;*kI8$3whnQl_97v|f3nPe*unJk$@vr~ z3Y1qqv2QiEq>>>CAZ_XBW(HNMRUT@!Zr*M<%=Zmw+aDV}Z zTVF5Pkz|`ELup=(+;yee8V!~YECgk~yDuk~UJy9r2@0@WW%lK*#r2}`Sj6tdLsu4G zAzq$V7&m)cOm>hb#-xY8kHVb|^A_o6O~KA^eyqd@O&d6NWp0x<)zi?LnnwZWBAaeMYy9r_?P-&uv*dTB$9qrOS59}R$J?}5 z8`1u5JusPfJj$*43=-8sc#({>e|rMZQ*xBB#&LI`K%+8*d;PYMUYpw4ya_01W@++@ z9$XppxFBoJKCu?>2m{*mD{?+^=N{vu4bTE^E)?h51GLot0MurS AMF0Q* diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg new file mode 100644 index 00000000..1cb7b17e --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/Marker.imageset/solid.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json index ee893b42..b1cb08ba 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/Contents.json @@ -1,17 +1,15 @@ { "images" : [ { - "filename" : "solid.png", + "filename" : "solid.svg", "idiom" : "universal", "scale" : "1x" }, { - "filename" : "solid 복사본.png", "idiom" : "universal", "scale" : "2x" }, { - "filename" : "solid 복사본 2.png", "idiom" : "universal", "scale" : "3x" } diff --git "a/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" "b/Poppool/Poppool/Resource/Assets.xcassets/Marker/TapMarker.imageset/solid \353\263\265\354\202\254\353\263\270 2.png" deleted file mode 100644 index 39f13b3126d68a1cbc184d26c7b7cac06527e45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2698 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 + + + diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 69a94cbe..d8e74495 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -212,7 +212,7 @@ final class AdminBottomSheetView: UIView { // MARK: - Public Methods func updateContentVisibility(isCategorySelected: Bool) { - Logger.log(message: "높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) + Logger.log("높이 변경 시작: \(isCategorySelected ? "카테고리" : "상태값")", category: .debug) let newHeight: CGFloat = isCategorySelected ? 200 : 160 @@ -223,6 +223,6 @@ final class AdminBottomSheetView: UIView { setNeedsLayout() layoutIfNeeded() - Logger.log(message: "높이 변경 완료", category: .debug) + Logger.log("높이 변경 완료", category: .debug) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 89ee0c06..a143fbe3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -42,7 +42,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { private func setupViews() { view.backgroundColor = .clear - Logger.log(message: "초기 뷰 계층:", category: .debug) + Logger.log("초기 뷰 계층:", category: .debug) view.addSubview(mainView) mainView.isUserInteractionEnabled = true @@ -57,7 +57,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { containerViewBottomConstraint = make.bottom.equalTo(view.snp.bottom).constraint } - Logger.log(message: "mainView 추가 후 계층:", category: .debug) + Logger.log("mainView 추가 후 계층:", category: .debug) dimmedView.backgroundColor = .black.withAlphaComponent(0.4) dimmedView.alpha = 0 @@ -72,7 +72,7 @@ final class AdminBottomSheetViewController: BaseViewController, View { make.edges.equalToSuperview() } - Logger.log(message: "최종 뷰 계층:", category: .debug) + Logger.log("최종 뷰 계층:", category: .debug) } private func setupCollectionView() { @@ -214,6 +214,6 @@ final class AdminBottomSheetViewController: BaseViewController, View { } deinit { - Logger.log(message: "BottomSheet deinit", category: .debug) + Logger.log("BottomSheet deinit", category: .debug) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift index 33da143f..b023363b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminReactor.swift @@ -56,9 +56,9 @@ final class AdminReactor: Reactor { .just(.setIsLoading(true)), adminUseCase.fetchStoreList(query: query, page: 0, size: 100) .do(onNext: { response in - Logger.log(message: "조회 성공 - 응답 데이터: \(response)", category: .info) + Logger.log("조회 성공 - 응답 데이터: \(response)", category: .info) }, onError: { error in - Logger.log(message: "조회 실패 - 에러: \(error.localizedDescription)", category: .error) + Logger.log("조회 실패 - 에러: \(error.localizedDescription)", category: .error) }) .map { .setStores($0) }, .just(.setIsLoading(false)) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 324f2183..5ec2c51c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -401,76 +401,76 @@ final class PopUpStoreRegisterReactor: Reactor { // 폼 유효성 검사 private func validateForm(state: State) -> Bool { - Logger.log(message: "폼 유효성 검사 시작", category: .debug) + Logger.log("폼 유효성 검사 시작", category: .debug) // 이름 필드 검사 guard !state.name.isEmpty else { - Logger.log(message: "유효성 검사 실패: 이름 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 이름 비어있음", category: .debug) return false } // 주소 필드 검사 guard !state.address.isEmpty else { - Logger.log(message: "유효성 검사 실패: 주소 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 주소 비어있음", category: .debug) return false } // 위도/경도 필드 검사 guard !state.lat.isEmpty else { - Logger.log(message: "유효성 검사 실패: 위도 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 위도 비어있음", category: .debug) return false } guard !state.lon.isEmpty else { - Logger.log(message: "유효성 검사 실패: 경도 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 경도 비어있음", category: .debug) return false } // 설명 필드 검사 guard !state.description.isEmpty else { - Logger.log(message: "유효성 검사 실패: 설명 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 설명 비어있음", category: .debug) return false } // 카테고리 필드 검사 guard !state.category.isEmpty else { - Logger.log(message: "유효성 검사 실패: 카테고리 비어있음", category: .debug) + Logger.log("유효성 검사 실패: 카테고리 비어있음", category: .debug) return false } // 이미지 검사 guard !state.images.isEmpty else { - Logger.log(message: "유효성 검사 실패: 이미지 없음", category: .debug) + Logger.log("유효성 검사 실패: 이미지 없음", category: .debug) return false } // 대표 이미지 검사 guard state.images.contains(where: { $0.isMain }) else { - Logger.log(message: "유효성 검사 실패: 대표 이미지 없음", category: .debug) + Logger.log("유효성 검사 실패: 대표 이미지 없음", category: .debug) return false } // 날짜 검사 guard state.selectedStartDate != nil else { - Logger.log(message: "유효성 검사 실패: 시작 날짜 없음", category: .debug) + Logger.log("유효성 검사 실패: 시작 날짜 없음", category: .debug) return false } guard state.selectedEndDate != nil else { - Logger.log(message: "유효성 검사 실패: 종료 날짜 없음", category: .debug) + Logger.log("유효성 검사 실패: 종료 날짜 없음", category: .debug) return false } // 위도/경도 유효성 검사 guard let latVal = Double(state.lat), let lonVal = Double(state.lon) else { - Logger.log(message: "유효성 검사 실패: 위도/경도 형식 오류", category: .debug) + Logger.log("유효성 검사 실패: 위도/경도 형식 오류", category: .debug) return false } // 위도/경도 값이 유효한지 검사 guard latVal != 0 || lonVal != 0 else { - Logger.log(message: "유효성 검사 실패: 위도/경도 값이 모두 0", category: .debug) + Logger.log("유효성 검사 실패: 위도/경도 값이 모두 0", category: .debug) return false } @@ -478,17 +478,17 @@ final class PopUpStoreRegisterReactor: Reactor { if let startDate = state.selectedStartDate, let endDate = state.selectedEndDate, startDate > endDate { - Logger.log(message: "유효성 검사 실패: 시작일이 종료일보다 늦음", category: .debug) + Logger.log("유효성 검사 실패: 시작일이 종료일보다 늦음", category: .debug) return false } - Logger.log(message: "유효성 검사 성공", category: .debug) + Logger.log("유효성 검사 성공", category: .debug) return true } // 주소 지오코딩 private func geocodeAddress(address: String) -> Observable { - Logger.log(message: "지오코딩 함수 호출: \(address)", category: .debug) + Logger.log("지오코딩 함수 호출: \(address)", category: .debug) return Observable.create { observer in let geocoder = CLGeocoder() @@ -500,7 +500,7 @@ final class PopUpStoreRegisterReactor: Reactor { preferredLocale: Locale(identifier: "ko_KR") ) { placemarks, error in if let error = error { - Logger.log(message: "Geocoding error: \(error.localizedDescription)", category: .error) + Logger.log("Geocoding error: \(error.localizedDescription)", category: .error) observer.onNext(nil) observer.onCompleted() return @@ -525,10 +525,10 @@ final class PopUpStoreRegisterReactor: Reactor { preSignedUseCase.tryDelete(objectKeyList: imagePaths) .subscribe( onCompleted: { - Logger.log(message: "S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) + Logger.log("S3에서 모든 이미지 삭제 성공: \(imagePaths.count)개", category: .info) }, onError: { error in - Logger.log(message: "S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) + Logger.log("S3에서 이미지 삭제 실패: \(error.localizedDescription)", category: .error) } ) .disposed(by: disposeBag) @@ -537,7 +537,7 @@ final class PopUpStoreRegisterReactor: Reactor { // 카테고리 ID 매핑 private func getCategoryId(from title: String) -> Int { let cleanTitle = title.replacingOccurrences(of: " ▾", with: "") - Logger.log(message: "카테고리 매핑 시작 - 타이틀: \(cleanTitle)", category: .debug) + Logger.log("카테고리 매핑 시작 - 타이틀: \(cleanTitle)", category: .debug) let categoryMap: [String: Int64] = [ "패션": 1, @@ -555,10 +555,10 @@ final class PopUpStoreRegisterReactor: Reactor { ] if let id = categoryMap[cleanTitle] { - Logger.log(message: "카테고리 매핑 성공: \(cleanTitle) -> \(id)", category: .debug) + Logger.log("카테고리 매핑 성공: \(cleanTitle) -> \(id)", category: .debug) return Int(id) } else { - Logger.log(message: "카테고리 매핑 실패: \(cleanTitle)에 해당하는 ID를 찾을 수 없음", category: .error) + Logger.log("카테고리 매핑 실패: \(cleanTitle)에 해당하는 ID를 찾을 수 없음", category: .error) return 1 // 기본값 } } @@ -655,13 +655,13 @@ final class PopUpStoreRegisterReactor: Reactor { defer { dispatchGroup.leave() } if let error = error { - Logger.log(message: "이미지 로드 오류: \(error.localizedDescription)", category: .error) + Logger.log("이미지 로드 오류: \(error.localizedDescription)", category: .error) return } guard let data = data, let image = UIImage(data: data) else { - Logger.log(message: "이미지 변환 실패", category: .error) + Logger.log("이미지 변환 실패", category: .error) return } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift index 2abf1f73..fca9ddfa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminStoreCell.swift @@ -81,14 +81,14 @@ final class AdminStoreCell: UITableViewCell { // MARK: - Configure func configure(with store: AdminStore) { - Logger.log(message: "셀 데이터 바인딩: \(store)", category: .debug) + Logger.log("셀 데이터 바인딩: \(store)", category: .debug) titleLabel.text = store.name categoryLabel.text = store.categoryName statusChip.text = "운영" let imagePath = store.mainImageUrl.replacingOccurrences(of: Secrets.popPoolS3BaseURL, with: "") - Logger.log(message: "이미지 경로: \(imagePath)", category: .debug) + Logger.log("이미지 경로: \(imagePath)", category: .debug) storeImageView.setPPImage(path: imagePath) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index ddf71a45..6e31f7e6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -223,7 +223,7 @@ final class AdminViewController: BaseViewController, View { allImageUrls = Array(Set(allImageUrls)) - Logger.log(message: "삭제할 이미지: \(allImageUrls.count)개", category: .debug) + Logger.log("삭제할 이미지: \(allImageUrls.count)개", category: .debug) @Dependency var preSignedUseCase: PreSignedUseCase preSignedUseCase.tryDelete(objectKeyList: allImageUrls) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 510d5841..ba634aef 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -94,14 +94,14 @@ final class CommentDetailReactor: Reactor { newState.commentData.likeCount += 1 userAPIUseCase.postCommentLike(commentId: newState.commentData.commentID) .subscribe(onDisposed: { - Logger.log(message: "CommentLike", category: .info) + Logger.log("CommentLike", category: .info) }) .disposed(by: disposeBag) } else { newState.commentData.likeCount -= 1 userAPIUseCase.deleteCommentLike(commentId: newState.commentData.commentID) .subscribe(onDisposed: { - Logger.log(message: "CommentLikeDelete", category: .info) + Logger.log("CommentLikeDelete", category: .info) }) .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index e0b71792..5f88d677 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -303,7 +303,7 @@ final class CommentListReactor: Reactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) .subscribe(onDisposed: { - Logger.log(message: "S3 Image Delete 완료", category: .info) + Logger.log("S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) case .edit: diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index b68f78ed..9ff04140 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -223,11 +223,11 @@ extension NormalCommentAddReactor: PHPickerViewControllerDelegate { if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { - Logger.log(message: "Failed to load image", category: .error) + Logger.log("Failed to load image", category: .error) } } } else { - Logger.log(message: "ItemProvider Can Not Load Object", category: .error) + Logger.log("ItemProvider Can Not Load Object", category: .error) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 1ad07a70..dc616f45 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -261,11 +261,11 @@ extension NormalCommentEditReactor: PHPickerViewControllerDelegate { if let image = image as? UIImage { originImageList[index] = image // 로드된 이미지를 해당 인덱스에 저장 } else { - Logger.log(message: "Failed to load image", category: .error) + Logger.log("Failed to load image", category: .error) } } } else { - Logger.log(message: "ItemProvider Can Not Load Object", category: .error) + Logger.log("ItemProvider Can Not Load Object", category: .error) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 43b3121c..6c766d50 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -426,23 +426,23 @@ extension DetailReactor { // URL 인코딩 후 생성 guard let encodedPath = imagePath.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed), let url = URL(string: encodedPath) else { - Logger.log(message: "URL 생성 실패", category: .error) + Logger.log("URL 생성 실패", category: .error) return } // 🔹 비동기적으로 이미지 다운로드 URLSession.shared.dataTask(with: url) { data, _, error in if let error = error { - Logger.log(message: "다운로드 실패", category: .error) + Logger.log("다운로드 실패", category: .error) return } guard let data = data, let image = UIImage(data: data) else { - Logger.log(message: "이미지 변환 실패", category: .error) + Logger.log("이미지 변환 실패", category: .error) return } - Logger.log(message: "이미지 다운로드 성공", category: .info) + Logger.log("이미지 다운로드 성공", category: .info) let sharedText = "[팝풀] \(storeName) 팝업 어때요?\n지금 바로 팝풀에서 확인해보세요!" // UI 업데이트는 메인 스레드에서 실행 @@ -545,7 +545,7 @@ extension DetailReactor { let commentList = comment.imageList.compactMap { $0 } self.preSignedUseCase.tryDelete(objectKeyList: commentList) .subscribe(onDisposed: { - Logger.log(message: "S3 Image Delete 완료", category: .info) + Logger.log("S3 Image Delete 완료", category: .info) }) .disposed(by: self.disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift index 45161c19..e5cc9467 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift @@ -27,15 +27,15 @@ final class BalloonBackgroundView: UIView { let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(8) let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 20, leading: 20, bottom: 19, trailing: 20) + section.contentInsets = .init(top: 18, leading: 20, bottom: 19, trailing: 20) section.interGroupSpacing = 8 return section } - let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) - cv.backgroundColor = .clear - cv.isScrollEnabled = false - cv.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifier) - return cv + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) + collectionView.backgroundColor = .clear + collectionView.isScrollEnabled = false + collectionView.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifier) + return collectionView }() // "그 외 지역" 전용 UI: 아이콘과 안내문구 @@ -104,7 +104,7 @@ final class BalloonBackgroundView: UIView { containerView.snp.makeConstraints { make in make.left.right.equalToSuperview() make.top.equalToSuperview().offset(arrowHeight) - make.bottom.equalToSuperview().priority(.high) + make.bottom.equalToSuperview() } containerView.addSubview(collectionView) @@ -114,7 +114,7 @@ final class BalloonBackgroundView: UIView { collectionView.snp.makeConstraints { make in make.top.left.right.equalToSuperview() - make.bottom.equalToSuperview().priority(.high) + make.bottom.equalToSuperview() } singleRegionIcon.snp.makeConstraints { make in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 964e5995..81b1f4b5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -41,7 +41,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setImage(resizedImage, for: .normal) button.semanticContentAttribute = .forceRightToLeft button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 0) - button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 10, bottom: 6, right: 12) + button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 10, bottom: 6, right: 12) button.setBackgroundColor(.blu500, for: .normal) button.setTitleColor(.white, for: .normal) button.layer.borderWidth = 0 @@ -51,7 +51,7 @@ final class BalloonChipCell: UICollectionViewCell { button.setImage(nil, for: .normal) button.semanticContentAttribute = .unspecified button.imageEdgeInsets = .zero - button.contentEdgeInsets = UIEdgeInsets(top: 6, left: 12, bottom: 6, right: 12) + button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 12, bottom: 6, right: 12) button.setBackgroundColor(.white, for: .normal) button.setTitleColor(.g400, for: .normal) button.layer.borderWidth = 1 diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift index 3ad18fc0..ec0fa088 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChip.swift @@ -72,7 +72,7 @@ final class FilterChip: UIButton { } let rightPadding: CGFloat = closeButton.isHidden ? 12 : 34 - contentEdgeInsets = UIEdgeInsets(top: 7, left: 12, bottom: 7, right: rightPadding) + contentEdgeInsets = UIEdgeInsets(top: 5, left: 12, bottom: 7, right: rightPadding) } // MARK: - Actions diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index b6f8989a..66be0ba2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -34,10 +34,10 @@ class FullScreenMapViewController: MapViewController { setupNavigation() // configureInitialMapPosition() self.navigationController?.navigationBar.isHidden = false - Logger.log(message: "💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) + Logger.log("💡 초기 위치 구성 직전: initialStore=\(String(describing: initialStore?.name))", category: .debug) configureInitialMapPosition() - Logger.log(message: "✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) + Logger.log("✅ FullScreenMapViewController - viewDidLoad 완료", category: .debug) mainView.mapView.touchDelegate = self } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index a51aa8ab..e31f9c61 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -121,11 +121,9 @@ final class MapGuideReactor: Reactor { return Observable.just(.showToast("지원하지 않는 맵 앱입니다.")) } - Logger.log(message: "🗺 맵 앱 열기 시도: \(urlScheme)", category: .debug) if let url = URL(string: urlScheme) { if UIApplication.shared.canOpenURL(url) { - Logger.log(message: "✅ \(appType) 앱 실행", category: .debug) UIApplication.shared.open(url, options: [:], completionHandler: nil) return Observable.empty() } else { @@ -133,7 +131,7 @@ final class MapGuideReactor: Reactor { if appType.lowercased() == "apple" { return Observable.just(.showToast("애플 지도 앱을 열 수 없습니다.")) } else { - Logger.log(message: "❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) + Logger.log("❌ \(appType) 앱 미설치 - 앱스토어로 이동", category: .debug) if let appStoreURL = URL(string: appStoreUrl) { UIApplication.shared.open(appStoreURL, options: [:], completionHandler: nil) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift index be525bb3..a79bf643 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift @@ -65,7 +65,7 @@ final class MapMarker: UIView { // MARK: - Init init() { - super.init(frame: CGRect(x: 0, y: 0, width: 80, height: 32)) + super.init(frame: CGRect(x: 0, y: 0, width: 32, height: 32)) setUpConstraints() } @@ -86,10 +86,8 @@ private extension MapMarker { labelStackView.addArrangedSubview(countLabel) countBadgeView.addSubview(badgeCountLabel) - self.snp.makeConstraints { make in - make.width.equalTo(200) - make.height.equalTo(70) - } + // 고정된 크기 제약조건 제거 + // 대신 내부 컨텐츠에 맞게 크기가 조정되도록 변경 markerImageView.snp.makeConstraints { make in make.centerX.equalToSuperview() @@ -99,12 +97,12 @@ private extension MapMarker { clusterContainer.snp.makeConstraints { make in make.center.equalToSuperview() - make.height.equalTo(24) - make.width.equalTo(80) + make.height.equalTo(32) + make.width.greaterThanOrEqualTo(80) } labelStackView.snp.makeConstraints { make in - make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)) + make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)) } countBadgeView.snp.makeConstraints { make in @@ -140,10 +138,14 @@ private extension MapMarker { countBadgeView.snp.remakeConstraints { make in make.width.height.equalTo(20) - make.top.equalTo(markerImageView.snp.top).offset(isSelected ? 0 : -4) - make.right.equalTo(markerImageView.snp.right).offset(isSelected ? 0 : 4) + if isSelected { + make.top.equalTo(markerImageView.snp.top).offset(-4) + make.right.equalTo(markerImageView.snp.right).offset(4) + } else { + make.top.equalTo(markerImageView.snp.top).offset(-4) + make.right.equalTo(markerImageView.snp.right).offset(4) + } } - self.layoutIfNeeded() CATransaction.commit() } @@ -188,8 +190,10 @@ extension MapMarker: Inputable { regionLabel.text = input.regionName countLabel.text = " \(input.count)" + // 클러스터 마커 크기 계산 - 텍스트 내용에 맞게 동적으로 조정 + labelStackView.layoutIfNeeded() let stackSize = labelStackView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize) - let requiredWidth = stackSize.width + 24 + let requiredWidth = max(stackSize.width + 24, 80) // 최소 너비 보장 clusterContainer.snp.remakeConstraints { make in make.center.equalToSuperview() @@ -200,18 +204,33 @@ extension MapMarker: Inputable { labelStackView.snp.remakeConstraints { make in make.edges.equalToSuperview().inset(UIEdgeInsets(top: 0, left: 12, bottom: 0, right: 12)) } + + self.frame.size = CGSize(width: requiredWidth, height: 32) } private func setupSingleMarker(_ input: Input) { markerImageView.isHidden = false clusterContainer.isHidden = true + updateMarkerImage(isSelected: input.isSelected) + let size = input.isSelected ? 44 : 32 + if input.count > 1 { countBadgeView.isHidden = false badgeCountLabel.text = "\(input.count)" + + countBadgeView.snp.remakeConstraints { make in + make.width.height.equalTo(20) + make.top.equalTo(markerImageView.snp.top).offset(input.isSelected ? 0 : -4) + make.right.equalTo(markerImageView.snp.right).offset(input.isSelected ? 2 : 4) + } + + self.frame.size = CGSize(width: size + 8, height: size) } else { countBadgeView.isHidden = true + + self.frame.size = CGSize(width: size, height: size) } } } @@ -222,6 +241,18 @@ extension MapMarker { } func asImage() -> UIImage? { + if let input = currentInput { + if input.isCluster { + self.layoutIfNeeded() + let clusterSize = clusterContainer.bounds.size + self.frame = CGRect(x: 0, y: 0, width: clusterSize.width + 8, height: clusterSize.height + 8) + } else { + let size = input.isSelected ? 44 : 32 + let extraWidth = (input.count > 1) ? 10 : 0 + self.frame = CGRect(x: 0, y: 0, width: size + extraWidth, height: size + 4) + } + } + self.layoutIfNeeded() UIGraphicsBeginImageContextWithOptions(self.bounds.size, false, UIScreen.main.scale) defer { UIGraphicsEndImageContext() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift index dd6d5db1..3c8405c9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift @@ -225,7 +225,7 @@ final class MapReactor: Reactor { }, onError: { error in Logger.log( - message: "❌ [에러]: 요청 실패 - \(error.localizedDescription)", + "❌ [에러]: 요청 실패 - \(error.localizedDescription)", category: .error ) }, @@ -353,7 +353,7 @@ final class MapReactor: Reactor { } Logger.log( - message: """ + """ Updated viewport stores: - Total: \(updatedStores.count) - Selected Store: \(state.selectedStore?.name ?? "None") @@ -370,7 +370,7 @@ final class MapReactor: Reactor { newState.error = error if let error = error { Logger.log( - message: """ + """ Error occurred in MapReactor: - Description: \(error.localizedDescription) - Domain: \(String(describing: (error as NSError).domain)) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 39e20a31..63194f04 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -59,7 +59,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var filterChipsTopY: CGFloat = 0 private var filterContainerBottomY: CGFloat { let frameInView = mainView.filterChips.convert(mainView.filterChips.bounds, to: view) - return frameInView.maxY // 필터 컨테이너의 바닥 높이 + return frameInView.maxY } enum ModalState { @@ -70,6 +70,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private var modalState: ModalState = .bottom private let idleSubject = PublishSubject() + private let cameraIdle = PublishSubject() // MARK: - Lifecycle override func viewDidAppear(_ animated: Bool) { @@ -99,8 +100,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let reactor = self.reactor { reactor.action.onNext(.fetchCategories) - - // 한국 전체 영역에 대한 경계값 설정 let koreaRegion = ( northEast: NMGLatLng(lat: 38.0, lng: 132.0), southWest: NMGLatLng(lat: 33.0, lng: 124.0) @@ -113,7 +112,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM southWestLon: koreaRegion.southWest.lng )) } - setupMapViewRxObservables() carouselView.rx.observe(Bool.self, "hidden") @@ -146,21 +144,15 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM pageIndex < self.currentCarouselStores.count else { return } let store = self.currentCarouselStores[pageIndex] - - // 이전 선택 마커 상태 초기화 if let previousMarker = self.currentMarker { self.updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false, count: 1) } - // 스와이프한 스토어에 해당하는 마커 찾기 let markerToFocus = self.findMarkerForStore(for: store) if let markerToFocus = markerToFocus { - // 마커 선택 상태로 업데이트 self.updateMarkerStyle(marker: markerToFocus, selected: true, isCluster: false, count: 1) self.currentMarker = markerToFocus - - // 마이크로 클러스터인 경우 툴팁 처리 let userData = markerToFocus.userInfo["storeData"] as? [MapPopUpStore] if let storeArray = userData, storeArray.count > 1 { if self.currentTooltipView == nil || @@ -187,10 +179,21 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } - // NMF 이벤트 설정을 위한 새로운 메서드 private func setupMapViewRxObservables() { - // 지도 이동 완료 감지 mainView.mapView.addCameraDelegate(delegate: self) + cameraIdle + .debounce(.milliseconds(300), scheduler: MainScheduler.instance) + .map { [unowned self] in + let bounds = self.getVisibleBounds() + return MapReactor.Action.viewportChanged( + northEastLat: bounds.northEast.lat, + northEastLon: bounds.northEast.lng, + southWestLat: bounds.southWest.lat, + southWestLon: bounds.southWest.lng + ) + } + .bind(to: reactor!.action) + .disposed(by: disposeBag) idleSubject .observe(on: MainScheduler.instance) @@ -211,13 +214,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func configureTooltip(for marker: NMFMarker, stores: [MapPopUpStore]) { - Logger.log(message: """ - 툴팁 설정: - - 현재 캐러셀 스토어: \(currentCarouselStores.map { $0.name }) - - 마커 스토어: \(stores.map { $0.name }) - """, category: .debug) - - // 기존 툴팁 제거 self.currentTooltipView?.removeFromSuperview() let tooltipView = MarkerTooltipView() @@ -233,12 +229,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.updateMarkerStyle(marker: marker, selected: true, isCluster: false, count: stores.count) tooltipView.selectStore(at: index) - - Logger.log(message: """ - 툴팁 선택: - - 선택된 스토어: \(stores[index].name) - - 툴팁 인덱스: \(index) - """, category: .debug) } let markerPoint = self.mainView.mapView.projection.point(from: marker.position) @@ -289,8 +279,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM setupPanAndSwipeGestures() let mapViewTapGesture = UITapGestureRecognizer(target: self, action: #selector(handleMapViewTap(_:))) -// mapViewTapGesture.cancelsTouchesInView = false // 중요: 다른 터치 이벤트를 방해하지 않음 - mapViewTapGesture.delaysTouchesBegan = false // 터치 지연 없음 + mapViewTapGesture.delaysTouchesBegan = false mainView.mapView.addGestureRecognizer(mapViewTapGesture) mapViewTapGesture.delegate = self } @@ -301,7 +290,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬆️ 위로 스와이프 감지", category: .debug) + Logger.log("⬆️ 위로 스와이프 감지", category: .debug) switch owner.modalState { case .bottom: owner.animateToState(.middle) @@ -317,7 +306,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .skip(1) .withUnretained(self) .subscribe { owner, _ in - Logger.log(message: "⬇️ 아래로 스와이프 감지됨", category: .debug) + Logger.log("⬇️ 아래로 스와이프 감지됨", category: .debug) switch owner.modalState { case .top: owner.animateToState(.middle) @@ -332,7 +321,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - Bind func bind(reactor: Reactor) { - // 필터 관련 바인딩 mainView.filterChips.locationChip.rx.tap .map { Reactor.Action.filterTapped(.location) } .bind(to: reactor.action) @@ -343,11 +331,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind(to: reactor.action) .disposed(by: disposeBag) - // 리스트 버튼 탭 mainView.listButton.rx.tap .withUnretained(self) .subscribe { owner, _ in - owner.animateToState(.middle) // 버튼 눌렀을 때 상태를 middle로 변경 + owner.animateToState(.middle) } .disposed(by: disposeBag) @@ -356,8 +343,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] _ in guard let self = self, let location = self.locationManager.location else { return } - - // 현재 위치로 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: location.coordinate.latitude, lng: location.coordinate.longitude @@ -369,10 +354,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.filterChips.onRemoveLocation = { [weak self] in guard let self = self else { return } - // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.location)) - - // 현재 뷰포트의 바운드로 마커 업데이트 요청 let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( northEastLat: bounds.northEast.lat, @@ -385,7 +367,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.clusterMarkerDictionary.values.forEach { $0.mapView = nil } self.clusterMarkerDictionary.removeAll() - // 캐러셀 숨기기 추가 self.carouselView.isHidden = true self.carouselView.updateCards([]) self.currentCarouselStores = [] @@ -396,10 +377,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.filterChips.onRemoveCategory = { [weak self] in guard let self = self else { return } - // 필터 제거 액션 self.reactor?.action.onNext(.clearFilters(.category)) - - // 현재 뷰포트의 바운드로 마커 업데이트 요청 let bounds = self.getVisibleBounds() self.reactor?.action.onNext(.viewportChanged( northEastLat: bounds.northEast.lat, @@ -419,7 +397,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM reactor.state.map { $0.selectedLocationFilters }.distinctUntilChanged(), reactor.state.map { $0.selectedCategoryFilters }.distinctUntilChanged() ) { locationFilters, categoryFilters -> (String, String) in - // 지역 필터 텍스트 포맷팅 let locationText: String if locationFilters.isEmpty { locationText = "지역선택" @@ -428,8 +405,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } else { locationText = locationFilters[0] } - - // 카테고리 필터 텍스트 포맷팅 let categoryText: String if categoryFilters.isEmpty { categoryText = "카테고리" @@ -469,7 +444,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] store in guard let self = self else { return } - // 검색 결과 위치로 카메라 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: store.latitude, lng: store.longitude @@ -499,25 +473,19 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind { [weak self] results in guard let self = self else { return } - // 이전 선택된 마커, 툴팁, 캐러셀 초기화 self.clearAllMarkers() self.storeListViewController.reactor?.action.onNext(.setStores([])) self.carouselView.updateCards([]) self.carouselView.isHidden = true self.resetSelectedMarker() // 추가된 부분 - // 결과가 없으면 스토어 카드 숨김 후 종료 if results.isEmpty { self.mainView.setStoreCardHidden(true, animated: true) return } else { self.mainView.setStoreCardHidden(false, animated: true) } - - // 새 결과로 마커 추가 및 업데이트 self.addMarkers(for: results) - - // 스토어 리스트 업데이트 let storeItems = results.map { store in StoreItem( id: store.id, @@ -530,13 +498,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM ) } self.storeListViewController.reactor?.action.onNext(.setStores(storeItems)) - - // 캐러셀 업데이트 self.carouselView.updateCards(results) self.carouselView.isHidden = false self.currentCarouselStores = results - - // 첫 번째 검색 결과로 지도 이동 if let firstStore = results.first { let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng( lat: firstStore.latitude, @@ -573,9 +537,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 중요: 마커에 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - Logger.log(message: "마커 터치됨! 위치: \(marker.position), 스토어: \(store.name)", category: .debug) - // 단일 스토어 마커 처리 return self.handleSingleStoreTap(marker, store: store) } @@ -584,34 +545,42 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM markerDictionary[store.id] = marker } - func updateMarkerStyle(marker: NMFMarker, selected: Bool, isCluster: Bool, count: Int = 1, regionName: String = "") { - if selected { - marker.width = 44 - marker.height = 44 - marker.iconImage = NMFOverlayImage(name: "TapMarker") - } else if isCluster { - marker.width = 36 - marker.height = 36 - marker.iconImage = NMFOverlayImage(name: "cluster_marker") + func updateMarkerStyle( + marker: NMFMarker, + selected: Bool, + isCluster: Bool, + count: Int = 1, + regionName: String = "" + ) { + let mapMarkerView: MapMarker + if let cachedView = marker.userInfo["mapMarkerView"] as? MapMarker { + mapMarkerView = cachedView } else { - marker.width = 32 - marker.height = 32 - marker.iconImage = NMFOverlayImage(name: "Marker") + mapMarkerView = MapMarker() + marker.userInfo["mapMarkerView"] = mapMarkerView } - if count > 1 { - marker.captionText = "\(count)" - } else { - marker.captionText = "" - } + let wasMultiMarker = (mapMarkerView.currentInput?.count ?? 0) > 1 + + let input = MapMarker.Input( + isSelected: selected, + isCluster: isCluster, + regionName: regionName, + count: count, + isMultiMarker: count > 1 && !isCluster + ) + mapMarkerView.injection(with: input) - marker.anchor = CGPoint(x: 0.5, y: 1.0) + if let img = mapMarkerView.asImage() { + marker.iconImage = NMFOverlayImage(image: img) + marker.width = img.size.width + marker.height = img.size.height + marker.anchor = CGPoint(x: 0.5, y: isCluster ? 0.5 : 1.0) + } } @objc private func handleMapViewTap(_ gesture: UITapGestureRecognizer) { - // 리스트뷰가 현재 보이는 상태(중간 또는 상단)일 때만 내림 if modalState == .middle || modalState == .top { - Logger.log(message: "맵뷰 탭 감지: 리스트뷰 내림", category: .debug) animateToState(.bottom) } } @@ -626,9 +595,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 let newOffset = currentOffset + translation.y - // 오프셋 제한 범위 설정 - let minOffset: CGFloat = filterContainerBottomY // 필터 컨테이너 바닥 제한 - let maxOffset: CGFloat = view.frame.height // 최하단 제한 + let minOffset: CGFloat = filterContainerBottomY + let maxOffset: CGFloat = view.frame.height let clampedOffset = min(max(newOffset, minOffset), maxOffset) constraint.update(offset: clampedOffset) @@ -642,13 +610,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM case .ended: if let constraint = listViewTopConstraint { let currentOffset = constraint.layoutConstraints.first?.constant ?? 0 - let middleY = view.frame.height * 0.3 // 중간 지점 기준 높이 + let middleY = view.frame.height * 0.3 let targetState: ModalState - // 속도와 위치를 기반으로 상태 결정 - if velocity.y > 500 { // 아래로 빠르게 드래그 + if velocity.y > 500 { targetState = .bottom - } else if velocity.y < -500 { // 위로 빠르게 드래그 + } else if velocity.y < -500 { targetState = .top } else if currentOffset < middleY * 0.7 { targetState = .top @@ -670,9 +637,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let middleOffset = view.frame.height * 0.3 if offset <= minOffset { - mainView.mapView.alpha = 0 // 탑에서는 완전히 숨김 + mainView.mapView.alpha = 0 } else if offset >= maxOffset { - mainView.mapView.alpha = 1 // 바텀에서는 완전히 보임 + mainView.mapView.alpha = 1 } else if offset <= middleOffset { let progress = (offset - minOffset) / (middleOffset - minOffset) mainView.mapView.alpha = progress @@ -697,7 +664,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.mainView.filterChips.bounds, to: self.view ) - self.mainView.mapView.alpha = 0 // 탑 상태에서는 숨김 + self.mainView.mapView.alpha = 0 self.storeListViewController.setGrabberHandleVisible(false) self.listViewTopConstraint?.update(offset: filterChipsFrame.maxY) self.mainView.searchInput.setBackgroundColor(.g50) @@ -726,7 +693,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.fetchStoreDetails(for: stores) Logger.log( - message: "✅ 전체 스토어 목록으로 리스트뷰 업데이트: \(stores.count)개", + "✅ 전체 스토어 목록으로 리스트뷰 업데이트: \(stores.count)개", category: .debug ) }) @@ -744,7 +711,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.view.layoutIfNeeded() }) { _ in self.modalState = state - Logger.log(message: ". 현재 상태: \(state)", category: .debug) } } @@ -760,15 +726,13 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - Helper: 클러스터용 커스텀 마커 이미지 생성 (MapMarker를 사용) func createClusterMarkerImage(regionName: String, count: Int) -> UIImage? { - // MapMarker의 입력값에 클러스터 상태를 전달합니다. - let markerView = MapMarker() // 기존 커스텀 뷰, 네이버맵용으로도 사용 가능하도록 구현됨. + let markerView = MapMarker() let input = MapMarker.Input(isSelected: false, isCluster: true, regionName: regionName, count: count, isMultiMarker: false) markerView.injection(with: input) - // 프레임이 설정되어 있지 않다면 적당한 크기로 지정 (예: 80x32) if markerView.frame == .zero { markerView.frame = CGRect(x: 0, y: 0, width: 80, height: 32) } @@ -778,23 +742,16 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateMapWithClustering() { let currentZoom = mainView.mapView.zoomLevel let level = MapZoomLevel.getLevel(from: Float(currentZoom)) - // 클러스터 처리 시 현재 스토어 목록(currentStores)을 사용 - Logger.log(message: "현재 줌 레벨: \(currentZoom), 모드: \(level), 스토어 수: \(currentStores.count)", category: .debug) - CATransaction.begin() CATransaction.setDisableActions(true) switch level { case .detailed: - // 상세 레벨에서는 개별 마커를 사용합니다. let newStoreIds = Set(currentStores.map { $0.id }) let groupedDict = groupStoresByExactLocation(currentStores) - - // 클러스터 마커 제거 clusterMarkerDictionary.values.forEach { $0.mapView = nil } clusterMarkerDictionary.removeAll() - // 그룹별로 개별 마커 생성/업데이트 for (coordinate, storeGroup) in groupedDict { if storeGroup.count == 1, let store = storeGroup.first { if let existingMarker = individualMarkerDictionary[store.id] { @@ -814,8 +771,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("개별 마커 터치됨! 스토어: \(store.name)") return self.handleSingleStoreTap(marker, store: store) } @@ -823,7 +778,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM individualMarkerDictionary[store.id] = marker } } else { - // 여러 스토어가 동일 위치에 있으면 단일 마커로 표시하면서 count 갱신 guard let firstStore = storeGroup.first else { continue } let markerKey = firstStore.id if let existingMarker = individualMarkerDictionary[markerKey] { @@ -837,11 +791,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM marker.anchor = CGPoint(x: 0.5, y: 1.0) updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeGroup.count) - // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } - - print("마이크로 클러스터 마커 터치됨! 스토어 수: \(storeGroup.count)개") return self.handleMicroClusterTap(marker, storeArray: storeGroup) } @@ -851,7 +802,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } - // 기존에 보이지 않는 개별 마커 제거 individualMarkerDictionary = individualMarkerDictionary.filter { id, marker in if newStoreIds.contains(id) { return true @@ -862,11 +812,9 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } case .district, .city, .country: - // 개별 마커 숨기기 individualMarkerDictionary.values.forEach { $0.mapView = nil } individualMarkerDictionary.removeAll() - // 클러스터 생성 let clusters = clusteringManager.clusterStores(currentStores, at: Float(currentZoom)) let activeClusterKeys = Set(clusters.map { $0.cluster.name }) @@ -875,7 +823,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM var marker: NMFMarker if let existingMarker = clusterMarkerDictionary[clusterKey] { marker = existingMarker - // 위치 업데이트가 필요하면 수정 if marker.position.lat != cluster.cluster.coordinate.lat || marker.position.lng != cluster.cluster.coordinate.lng { marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) @@ -886,17 +833,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } marker.position = NMGLatLng(lat: cluster.cluster.coordinate.lat, lng: cluster.cluster.coordinate.lng) - marker.userInfo = ["clusterData": cluster] // 중요: userInfo에 cluster 객체를 직접 저장 + marker.userInfo = ["clusterData": cluster] - // 여기서 커스텀 클러스터 마커 뷰를 이미지로 변환하여 적용합니다. if let clusterImage = createClusterMarkerImage(regionName: cluster.cluster.name, count: cluster.storeCount) { marker.iconImage = NMFOverlayImage(image: clusterImage) } else { - // 기본 에셋 fallback (원하는 경우) marker.iconImage = NMFOverlayImage(name: "cluster_marker") } - // 터치 핸들러 추가 - userInfo에서 클러스터 데이터를 직접 가져오기 marker.touchHandler = { [weak self] (overlay) -> Bool in guard let self = self, let tappedMarker = overlay as? NMFMarker, @@ -912,7 +856,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM marker.mapView = mainView.mapView } - // 활성 클러스터가 아닌 마커 제거 for (key, marker) in clusterMarkerDictionary { if !activeClusterKeys.contains(key) { marker.mapView = nil @@ -1045,7 +988,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM currentFilterBottomSheet = nil } - // 기본 마커 private func addMarkers(for stores: [MapPopUpStore]) { markerDictionary.values.forEach { $0.mapView = nil } markerDictionary.removeAll() @@ -1057,7 +999,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: marker, selected: false, isCluster: false) - // 직접 터치 핸들러 추가 marker.touchHandler = { [weak self] (_) -> Bool in guard let self = self else { return false } @@ -1070,7 +1011,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } private func updateListView(with results: [MapPopUpStore]) { - // MapPopUpStore 배열을 StoreItem 배열로 변환 let storeItems = results.map { store in StoreItem( id: store.id, @@ -1115,8 +1055,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM northEast: bounds.northEast ) } - - // 현재 보이는 지도 영역의 경계를 가져오는 함수 private func getVisibleBounds() -> (northEast: NMGLatLng, southWest: NMGLatLng) { let mapBounds = mainView.mapView.contentBounds @@ -1136,10 +1074,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM mainView.mapView.positionMode = .direction // 내 위치 트래킹 모드 활성화 case .denied, .restricted: Logger.log( - message: "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", + "위치 서비스가 비활성화되었습니다. 설정에서 권한을 확인해주세요.", category: .error ) - mainView.mapView.positionMode = .disabled // 내 위치 트래킹 모드 비활성화 + mainView.mapView.positionMode = .disabled @unknown default: break } @@ -1147,15 +1085,11 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func updateTooltipPosition() { guard let marker = currentMarker, let tooltip = currentTooltipView else { return } - - // 마커 위치를 화면 좌표로 변환 let markerPoint = mainView.mapView.projection.point(from: marker.position) var markerCenter = markerPoint - // 마커 높이 고려 (네이버 마커는 크기가 다를 수 있음) - markerCenter.y = markerPoint.y - 20 // 마커 이미지 높이의 절반 정도 + markerCenter.y = markerPoint.y - 20 - // 오프셋 값 (디자인에 맞게 조정) let offsetX: CGFloat = -10 let offsetY: CGFloat = -10 @@ -1167,7 +1101,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func resetSelectedMarker() { if let currentMarker = currentMarker { - // 마커 스타일 업데이트 updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) } @@ -1219,7 +1152,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func findMarkerForStore(for store: MapPopUpStore) -> NMFMarker? { - // individualMarkerDictionary에 저장된 모든 마커를 순회 for marker in individualMarkerDictionary.values { if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore, singleStore.id == store.id { return marker @@ -1229,7 +1161,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return marker } } - // 상세 레벨이 아닐 경우 clusterMarkerDictionary에도 동일하게 검색 for marker in clusterMarkerDictionary.values { if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData, clusterData.cluster.stores.contains(where: { $0.id == store.id }) { @@ -1240,10 +1171,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func fetchStoreDetails(for stores: [MapPopUpStore]) { - // 빈 목록이면 처리하지 않음 guard !stores.isEmpty else { return } - - // 먼저 기본 정보로 StoreItem 생성하여 순서 유지 let initialStoreItems = stores.map { store in StoreItem( id: store.id, @@ -1255,11 +1183,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM isBookmarked: false ) } - - // 리스트에는 모든 스토어 정보 표시 (필터링된 모든 스토어) self.storeListViewController.reactor?.action.onNext(.setStores(initialStoreItems)) - - // 각 스토어의 상세 정보를 병렬로 가져와서 업데이트 (북마크 정보 등) stores.forEach { store in self.popUpAPIUseCase.getPopUpDetail( commentType: "NORMAL", @@ -1279,12 +1203,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } func bindViewport(reactor: MapReactor) { - // 카메라 이동 완료 시 이벤트 발생되는 Subject let cameraObservable = PublishSubject() - // NMFMapViewCameraDelegate 메서드에서 호출할 수 있도록 설정 - - // 카메라 변경 감지해서 액션 전달 cameraObservable .throttle(.milliseconds(200), scheduler: MainScheduler.instance) .map { [unowned self] _ -> MapReactor.Action in @@ -1299,7 +1219,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .bind(to: reactor.action) .disposed(by: disposeBag) - // 현재 뷰포트 내의 스토어 업데이트 - 초기 1회 reactor.state .map { $0.viewportStores } .distinctUntilChanged() @@ -1309,7 +1228,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .subscribe(onNext: { [weak self] stores in guard let self = self else { return } - // 현재 위치가 있으면 가장 가까운 스토어, 없으면 첫 번째 스토어 표시 if let location = self.locationManager.location { self.findAndShowNearestStore(from: location) } else if let firstStore = stores.first, @@ -1317,13 +1235,11 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM _ = self.handleSingleStoreTap(marker, store: firstStore) } - // 현재 스토어 목록 업데이트 및 클러스터링 self.currentStores = stores self.updateMapWithClustering() }) .disposed(by: disposeBag) - // 뷰포트 내 마커 업데이트 및 캐러셀 표시 reactor.state .map { $0.viewportStores } .distinctUntilChanged() @@ -1335,19 +1251,16 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM let effectiveViewport = self.getEffectiveViewport() let bounds = self.getVisibleBounds() - // 화면에 보이는 스토어만 필터링 let visibleStores = stores.filter { store in let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) return NMGLatLngBounds(southWest: bounds.southWest, northEast: bounds.northEast).contains(storePosition) } self.currentStores = visibleStores - // 개별 마커 레벨인지 확인 let currentZoom = self.mainView.mapView.zoomLevel let level = MapZoomLevel.getLevel(from: Float(currentZoom)) if level == .detailed && !visibleStores.isEmpty { - // 캐러셀에 모든 마커 정보 표시 let effectiveStores = visibleStores.filter { store in let storePosition = NMGLatLng(lat: store.latitude, lng: store.longitude) return effectiveViewport.contains(storePosition) @@ -1358,9 +1271,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.carouselView.isHidden = false self.mainView.setStoreCardHidden(false, animated: true) - // 현재 선택된 마커가 있으면 해당 위치로 스크롤 if let currentMarker = self.currentMarker { - // 마커의 스토어 정보 체크 if let currentStore = currentMarker.userInfo["storeData"] as? MapPopUpStore, let index = visibleStores.firstIndex(where: { $0.id == currentStore.id }) { self.carouselView.scrollToCard(index: index) @@ -1383,7 +1294,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.carouselView.scrollToCard(index: 0) } } else { - // 선택된 마커가 없는 경우, 첫 번째 스토어로 설정 if let firstStore = visibleStores.first, let marker = self.findMarkerForStore(for: firstStore) { self.updateMarkerStyle(marker: marker, selected: true, isCluster: false) @@ -1411,7 +1321,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM private func findAndShowNearestStore(from location: CLLocation) { guard !currentStores.isEmpty else { - Logger.log(message: "현재위치 표기할 스토어가 없습니다", category: .debug) + Logger.log("현재위치 표기할 스토어가 없습니다", category: .debug) return } @@ -1424,17 +1334,14 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } if let store = nearestStore, let marker = findMarkerForStore(for: store) { - // 카메라 이동 없이 선택된 마커만 업데이트 _ = handleSingleStoreTap(marker, store: store) } - // 마커가 없다면 새로 생성 else if let store = nearestStore { let marker = NMFMarker() marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) marker.userInfo = ["storeData": store] marker.anchor = CGPoint(x: 0.5, y: 1.0) - // 마커 스타일 설정 updateMarkerStyle(marker: marker, selected: true, isCluster: false) marker.mapView = mainView.mapView @@ -1451,18 +1358,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM func handleSingleStoreTap(_ marker: NMFMarker, store: MapPopUpStore) -> Bool { isMovingToMarker = true - // 이전 마커 선택 상태 해제 if let previousMarker = currentMarker { updateMarkerStyle(marker: previousMarker, selected: false, isCluster: false) } - - // 새 마커 선택 상태로 설정 updateMarkerStyle(marker: marker, selected: true, isCluster: false) currentMarker = marker - - // 캐러셀에 표시할 스토어 확인 if currentCarouselStores.isEmpty || !currentCarouselStores.contains(where: { $0.id == store.id }) { - // 현재 뷰포트의 모든 스토어를 가져오기 let bounds = getVisibleBounds() let visibleStores = currentStores.filter { store in @@ -1471,21 +1372,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } if !visibleStores.isEmpty { - // 뷰포트의 모든 스토어를 캐러셀에 표시 currentCarouselStores = visibleStores carouselView.updateCards(visibleStores) - // 선택한 스토어의 인덱스를 찾아 스크롤 if let index = visibleStores.firstIndex(where: { $0.id == store.id }) { carouselView.scrollToCard(index: index) } } else { - // 뷰포트에 다른 스토어가 없는 경우, 선택한 스토어만 표시 currentCarouselStores = [store] carouselView.updateCards([store]) } } else { - // 캐러셀에 이미 해당 스토어가 있는 경우, 해당 위치로 스크롤 if let index = currentCarouselStores.firstIndex(where: { $0.id == store.id }) { carouselView.scrollToCard(index: index) } @@ -1494,16 +1391,12 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.isHidden = false mainView.setStoreCardHidden(false, animated: true) - // 툴팁 처리 if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { - // 마이크로 클러스터인 경우 툴팁 표시 configureTooltip(for: marker, stores: storeArray) - // 해당 스토어의 툴팁 인덱스 선택 if let index = storeArray.firstIndex(where: { $0.id == store.id }) { (currentTooltipView as? MarkerTooltipView)?.selectStore(at: index) } } else { - // 단일 마커인 경우 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil } @@ -1512,53 +1405,36 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM return true } - // 리전 클러스터 탭 처리 func handleRegionalClusterTap(_ marker: NMFMarker, clusterData: ClusterMarkerData) -> Bool { - print("handleRegionalClusterTap 함수 호출됨") let currentZoom = mainView.mapView.zoomLevel let currentLevel = MapZoomLevel.getLevel(from: Float(currentZoom)) - - // 디버깅 - print("현재 줌 레벨: \(currentZoom), 모드: \(currentLevel)") - print("클러스터 정보: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)") - switch currentLevel { - case .city: // 시 단위 클러스터 - print("시 단위 클러스터 처리") + case .city: let districtZoomLevel: Double = 10.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: districtZoomLevel) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - case .district: // 구 단위 클러스터 - print("구 단위 클러스터 처리") + case .district: let detailedZoomLevel: Double = 12.0 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: detailedZoomLevel) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) default: - print("기타 레벨 클러스터 처리") + print("기타") } - - // 클러스터에 포함된 스토어들만 표시하도록 마커 업데이트 updateMarkersForCluster(stores: clusterData.cluster.stores) - - // 캐러셀 업데이트 carouselView.updateCards(clusterData.cluster.stores) carouselView.isHidden = false self.currentCarouselStores = clusterData.cluster.stores - return true } - // 마이크로 클러스터 탭 처리 func handleMicroClusterTap(_ marker: NMFMarker, storeArray: [MapPopUpStore]) -> Bool { - // 이미 선택된 마커를 다시 탭할 때 if currentMarker == marker { - // 툴팁과 캐러셀만 숨기고, 마커의 선택 상태는 유지 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] @@ -1567,8 +1443,6 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.isHidden = true carouselView.updateCards([]) currentCarouselStores = [] - - // 마커 상태 업데이트 updateMarkerStyle(marker: marker, selected: false, isCluster: false, count: storeArray.count) currentMarker = nil @@ -1594,14 +1468,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM carouselView.scrollToCard(index: 0) mainView.setStoreCardHidden(false, animated: true) - - // 지도 이동 let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position) cameraUpdate.animation = .easeIn cameraUpdate.animationDuration = 0.3 mainView.mapView.moveCamera(cameraUpdate) - - // 툴팁 생성 if storeArray.count > 1 { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in guard let self = self else { return } @@ -1614,8 +1484,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } private func showNoMarkersToast() { - // 디자인 예정이므로 임시 구현 - Logger.log(message: "현재 지도 영역에 표시할 마커가 없습니다", category: .debug) + Logger.log("현재 지도 영역에 표시할 마커가 없습니다", category: .debug) } } @@ -1642,37 +1511,23 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM // MARK: - NMFMapViewTouchDelegate extension MapViewController { - // 마커 탭 이벤트 처리 - // 마커 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { - Logger.log(message: "didTapMarker 호출됨: \(marker.position), userInfo: \(marker.userInfo)", category: .debug) - - // 클러스터 마커 확인 if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { - Logger.log(message: "클러스터 데이터 감지: \(clusterData.cluster.name), 스토어 수: \(clusterData.storeCount)", category: .debug) return handleRegionalClusterTap(marker, clusterData: clusterData) } - // 마이크로 클러스터 또는 단일 스토어 마커 확인 else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { if storeArray.count > 1 { - Logger.log(message: "마이크로 클러스터 감지: \(storeArray.count)개 스토어", category: .debug) return handleMicroClusterTap(marker, storeArray: storeArray) } else if let singleStore = storeArray.first { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } } - // 단일 스토어 마커 (배열이 아닌 경우) 확인 else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { - Logger.log(message: "단일 스토어 감지: \(singleStore.name)", category: .debug) return handleSingleStoreTap(marker, store: singleStore) } - - Logger.log(message: "인식할 수 없는 마커 타입", category: .error) return false } - // 지도 탭 이벤트 처리 func mapView(_ mapView: NMFMapView, didTapMap latlng: NMGLatLng, point: CGPoint) { guard !isMovingToMarker else { return } @@ -1681,54 +1536,40 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM updateMarkerStyle(marker: currentMarker, selected: false, isCluster: false) self.currentMarker = nil } - - // 툴팁 제거 currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] currentTooltipCoordinate = nil - - // 캐러셀 초기화 carouselView.isHidden = true carouselView.updateCards([]) self.currentCarouselStores = [] mainView.setStoreCardHidden(true, animated: true) - - // 클러스터링 업데이트 updateMapWithClustering() } } // MARK: - NMFMapViewCameraDelegate extension MapViewController { - // 카메라 이동 시작 시 호출 func mapView(_ mapView: NMFMapView, cameraWillChangeByReason reason: Int, animated: Bool) { if reason == NMFMapChangedByGesture && !isMovingToMarker { resetSelectedMarker() } } - - // 카메라 이동 중 호출 func mapView(_ mapView: NMFMapView, cameraIsChangingByReason reason: Int) { if !isMovingToMarker { currentTooltipView?.removeFromSuperview() currentTooltipView = nil currentTooltipStores = [] updateMapWithClustering() - - // 캐러셀 초기화 carouselView.isHidden = true carouselView.updateCards([]) currentCarouselStores = [] } } - - // 카메라 이동 완료 시 호출 func mapView(_ mapView: NMFMapView, cameraDidChangeByReason reason: Int, animated: Bool) { if let marker = self.currentMarker, let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore], storeArray.count > 1 { - // 툴팁이 없으면 생성, 있으면 위치 업데이트 if self.currentTooltipView == nil { self.configureTooltip(for: marker, stores: storeArray) } else { @@ -1736,37 +1577,17 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM } } self.isMovingToMarker = false - - // 뷰포트 변경 이벤트 처리 - idleSubject 통해 알림 idleSubject.onNext(()) - - // 뷰포트 변경 이벤트 처리 - if let reactor = self.reactor { - let bounds = self.getVisibleBounds() - reactor.action.onNext(.viewportChanged( - northEastLat: bounds.northEast.lat, - northEastLon: bounds.northEast.lng, - southWestLat: bounds.southWest.lat, - southWestLon: bounds.southWest.lng - )) - } + cameraIdle.onNext(()) } } - // MARK: - UIGestureRecognizerDelegate extension MapViewController { - // 맵뷰의 다른 제스처와 충돌하지 않도록 함 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { - // 맵의 내장 제스처와 동시 인식 허용 return true } - - // 리스트뷰가 보일 때만 커스텀 탭 제스처 허용 func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - // 터치가 리스트뷰 영역에 있으면 커스텀 제스처 트리거하지 않음 let touchPoint = touch.location(in: view) - - // 리스트뷰가 보이고 터치가 리스트뷰 위에 있으면 탭 처리하지 않음 if modalState != .bottom { let listViewY = storeListViewController.view.frame.minY if touchPoint.y > listViewY { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift index 054506ce..d335352d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListReactor.swift @@ -80,7 +80,7 @@ final class StoreListReactor: Reactor { // Int64 → Int32 변환 필요 guard let idInt32 = Int32(exactly: store.id) else { - Logger.log(message: "ID 값이 Int32 범위를 초과했습니다: \(store.id)", category: .error) + Logger.log("ID 값이 Int32 범위를 초과했습니다: \(store.id)", category: .error) return .empty() } @@ -192,7 +192,7 @@ final class StoreListReactor: Reactor { newState.stores[index].isBookmarked = isBookmarked Logger.log( - message: """ + """ 북마크 상태 변경: - 스토어명: \(store.title) - ID: \(store.id) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index 9dc41725..988ef8e7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -220,7 +220,7 @@ extension ProfileEditController: PHPickerViewControllerDelegate, UIImagePickerCo func showCamera() { guard UIImagePickerController.isSourceTypeAvailable(.camera) else { - Logger.log(message: "카메라를 사용할 수 없습니다.", category: .error) + Logger.log("카메라를 사용할 수 없습니다.", category: .error) return } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift index 23bebd96..a4bd93be 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Common/ClusteringManager.swift @@ -126,10 +126,8 @@ class ClusteringManager { let combined = Array(seoulClusters.values) + Array(gyeonggiClusters.values) + Array(otherClusters.values) let filtered = combined.filter { $0.storeCount > 0 } - - Logger.log(message: "구 단위 클러스터 생성 결과: \(filtered.count)개", category: .debug) for cluster in filtered { - Logger.log(message: "- \(cluster.base.name): \(cluster.storeCount)개 매장", category: .debug) + Logger.log("- \(cluster.base.name): \(cluster.storeCount)개 매장", category: .debug) } return filtered.map { $0.toMarkerData() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift index 42a777dd..2b3dc171 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift @@ -9,10 +9,8 @@ class BaseTabmanController: TabmanViewController { init() { super.init(nibName: nil, bundle: nil) Logger.log( - message: "\(self) init", - category: .info, - fileName: #file, - line: #line + "\(self) init", + category: .info ) } @@ -28,10 +26,8 @@ class BaseTabmanController: TabmanViewController { deinit { Logger.log( - message: "\(self) deinit", - category: .info, - fileName: #file, - line: #line + "\(self) deinit", + category: .info ) } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift index 03eeded3..7680932f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift @@ -13,10 +13,8 @@ public class BaseViewController: UIViewController { public init() { super.init(nibName: nil, bundle: nil) Logger.log( - message: "\(self) init", - category: .info, - fileName: #file, - line: #line + "\(self) init", + category: .info ) } @@ -38,10 +36,8 @@ public class BaseViewController: UIViewController { deinit { Logger.log( - message: "\(self) deinit", - category: .info, - fileName: #file, - line: #line + "\(self) deinit", + category: .info ) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index c37a5e4b..f7f7d23a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -67,11 +67,8 @@ extension SectionSupplementaryItemable { for: indexPath ) as? ReusableView else { Logger.log( - message: "ReusableView Error", - category: .error, - fileName: #file, - line: #line - ) + "ReusableView Error", + category: .error) return UICollectionReusableView() } view.injection(with: viewInput) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift index 7153b6a7..a3cbb3d6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift @@ -131,10 +131,8 @@ extension Sectionable { for: indexPath ) as? CellType else { Logger.log( - message: "dequeueReusableCell Fail", - category: .error, - fileName: #file, - line: #line + "dequeueReusableCell Fail", + category: .error ) return UICollectionViewCell() } @@ -158,10 +156,8 @@ extension Sectionable { guard let item = supplementaryItems?.filter({ $0.elementKind == kind }).first else { Logger.log( - message: "ReusableView Not Register", - category: .error, - fileName: #file, - line: #line + "ReusableView Not Register", + category: .error ) fatalError() } From b5a134f872fab70726f7e9e028b737396f5e051d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 30 Apr 2025 11:46:57 +0000 Subject: [PATCH 204/393] style/#127: Apply SwiftLint autocorrect --- .../Data/Data/Network/Provider/ProviderImpl.swift | 5 ----- .../Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift | 1 - .../Scene/Map/MapView/MapViewController.swift | 9 +++------ 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift index c0909032..e46126b9 100644 --- a/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift +++ b/Poppool/DataLayer/Data/Data/Network/Provider/ProviderImpl.swift @@ -22,8 +22,6 @@ public final class ProviderImpl: Provider { /// 1) endpoint -> urlRequest 생성 let urlRequest = try endpoint.getUrlRequest() - - let request = AF.request(urlRequest, interceptor: interceptor) .validate() .responseData { [weak self] response in @@ -92,7 +90,6 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.getUrlRequest() - self.executeRequest(urlRequest, interceptor: interceptor) { response in Logger.log( "응답 시각 :\(Date())", @@ -141,7 +138,6 @@ public final class ProviderImpl: Provider { do { let urlRequest = try request.asURLRequest() - AF.upload(multipartFormData: { multipartFormData in request.asMultipartFormData(multipartFormData: multipartFormData) Logger.log("업로드 시각 :\(Date())", category: .network) @@ -175,7 +171,6 @@ private extension ProviderImpl { interceptor: RequestInterceptor?, completion: @escaping (AFDataResponse) -> Void ) { - AF.request(urlRequest, interceptor: interceptor) .validate() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift index e31f9c61..2bff7fda 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/MapGuideReactor.swift @@ -121,7 +121,6 @@ final class MapGuideReactor: Reactor { return Observable.just(.showToast("지원하지 않는 맵 앱입니다.")) } - if let url = URL(string: urlScheme) { if UIApplication.shared.canOpenURL(url) { UIApplication.shared.open(url, options: [:], completionHandler: nil) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 63194f04..6e6923b8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1335,8 +1335,7 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM if let store = nearestStore, let marker = findMarkerForStore(for: store) { _ = handleSingleStoreTap(marker, store: store) - } - else if let store = nearestStore { + } else if let store = nearestStore { let marker = NMFMarker() marker.position = NMGLatLng(lat: store.latitude, lng: store.longitude) marker.userInfo = ["storeData": store] @@ -1514,15 +1513,13 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM func mapView(_ mapView: NMFMapView, didTap marker: NMFMarker) -> Bool { if let clusterData = marker.userInfo["clusterData"] as? ClusterMarkerData { return handleRegionalClusterTap(marker, clusterData: clusterData) - } - else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { + } else if let storeArray = marker.userInfo["storeData"] as? [MapPopUpStore] { if storeArray.count > 1 { return handleMicroClusterTap(marker, storeArray: storeArray) } else if let singleStore = storeArray.first { return handleSingleStoreTap(marker, store: singleStore) } - } - else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { + } else if let singleStore = marker.userInfo["storeData"] as? MapPopUpStore { return handleSingleStoreTap(marker, store: singleStore) } return false From 0b57cd528fb3bb811cbd4eaca2c167f0d2e6b670 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 15:48:50 +0900 Subject: [PATCH 205/393] =?UTF-8?q?refactor/#126:=20=EC=B2=AB=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EC=9D=B8=20=EA=B2=BD=EC=9A=B0=EC=97=90?= =?UTF-8?q?=EB=A7=8C=20filterTitle=EC=9D=B4=20=EC=84=A4=EC=A0=95=EB=90=98?= =?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 --- .../Search/BeforeSearch/SearchReactor.swift | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index b11fa24f..4c2cf1b7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -299,7 +299,7 @@ final class SearchReactor: Reactor { } func setBottomSearchList(sort: String?) -> Observable { - let isOpen = filterIndex == 0 ? true : false + let isOpen = filterIndex == 0 let categories = searchCategorySection.inputDataList.compactMap { $0.id } return popUpAPIUseCase.getSearchBottomPopUpList( @@ -327,22 +327,24 @@ final class SearchReactor: Reactor { } if owner.currentPage == 0 { + // 첫 페이지는 전체 reload + // SearchCountTitleSection 설정 + let isOpenString = isOpen ? "오픈・" : "종료・" + let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" + let sortedTitle = isOpenString + sortedString + owner.searchSortedSection.inputDataList = [ + SearchCountTitleSectionCell.Input( + count: response.totalElements, + sortedTitle: sortedTitle + ) + ] owner.searchListSection.inputDataList = newItems } else if owner.currentPage != owner.lastAppendPage { owner.lastAppendPage = owner.currentPage owner.searchListSection.inputDataList.append(contentsOf: newItems) } - let isOpenString = isOpen ? "오픈・" : "종료・" - let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" - let sortedTitle = isOpenString + sortedString - owner.searchSortedSection.inputDataList = [ - SearchCountTitleSectionCell.Input( - count: response.totalElements, - sortedTitle: sortedTitle - ) - ] owner.lastPage = response.totalPages owner.isLoading = false From 00f5342c2f8976f027cf3a6ea0ad8d96240a2cb8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 15:51:32 +0900 Subject: [PATCH 206/393] =?UTF-8?q?fix/#126:=20=EC=B2=AB=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EA=B0=80=20=EC=95=84=EB=8B=90=EB=95=8C?= =?UTF-8?q?=EB=8A=94=20reload=EA=B0=80=20=EC=95=84=EB=8B=8C=20update?= =?UTF-8?q?=EB=A1=9C=20=EB=8F=99=EC=9E=91=EB=90=98=EB=8F=84=EB=A1=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 --- .../Search/BeforeSearch/SearchReactor.swift | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 4c2cf1b7..c3fbf0a8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -339,16 +339,25 @@ final class SearchReactor: Reactor { ) ] owner.searchListSection.inputDataList = newItems - } else if owner.currentPage != owner.lastAppendPage { + owner.lastAppendPage = owner.currentPage + owner.lastPage = response.totalPages + owner.isLoading = false + return .loadView + } else { + // 다음 페이지는 append 후 부분 업데이트 owner.lastAppendPage = owner.currentPage owner.searchListSection.inputDataList.append(contentsOf: newItems) - } + owner.lastPage = response.totalPages + owner.isLoading = false + // HomeCardGridSection이 컬렉션뷰에서 몇 번째 섹션인지 계산 + let sectionIndex = owner.getSection().enumerated() + .first { _, section in section is HomeCardGridSection }!.offset - - owner.lastPage = response.totalPages - owner.isLoading = false - return .loadView + // append된 첫 아이템의 IndexPath + let firstIndexPath = IndexPath(item: previousCount, section: sectionIndex) + return .updateBottomSearchList(newItems: newItems, IndexPath: firstIndexPath) + } } } } From 7ccb36d81afdd8d66b4ce02076a4b0a8a01333d7 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:01:54 +0900 Subject: [PATCH 207/393] =?UTF-8?q?feat/#126:=20=EC=83=88=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=ED=85=9C=EB=A7=8C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=A0=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D?= =?UTF-8?q?=20=EB=B3=84=EB=8F=84=EC=9D=98=20mutate=EC=99=80=20action=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 --- .../Scene/Search/BeforeSearch/SearchReactor.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index c3fbf0a8..1b8d0b76 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -33,11 +33,15 @@ final class SearchReactor: Reactor { case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) case setSearchKeyWord(text: String?) case resetSearchKeyWord + case updateBottomSearchList(newItems: [HomeCardSectionCell.Input], IndexPath: IndexPath) } struct State { var sections: [any Sectionable] = [] var searchKeyWord: String? + var newBottomSearchList: [HomeCardSectionCell.Input] = [] + var bottomSearchListLastIndexPath: IndexPath? + } // MARK: - properties From a53ff06fcf8bfc74ec463a0a7862c9362d62b2f3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:03:07 +0900 Subject: [PATCH 208/393] =?UTF-8?q?feat/#126:=20=EB=B7=B0=EA=B0=80=20?= =?UTF-8?q?=EB=8B=A4=EC=8B=9C=20=EA=B7=B8=EB=A0=A4=EC=A7=88=EB=95=8C?= =?UTF-8?q?=EB=8A=94=20=EA=B4=80=EB=A6=AC=ED=95=98=EB=8D=98=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EC=B4=88=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Search/BeforeSearch/SearchReactor.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 1b8d0b76..74d1ca5b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -42,6 +42,11 @@ final class SearchReactor: Reactor { var newBottomSearchList: [HomeCardSectionCell.Input] = [] var bottomSearchListLastIndexPath: IndexPath? + mutating func resetPaginationState() { + self.newBottomSearchList = [] + self.bottomSearchListLastIndexPath = nil + } + } // MARK: - properties @@ -183,6 +188,7 @@ final class SearchReactor: Reactor { switch mutation { case .loadView: newState.sections = getSection() + newState.resetPaginationState() case .moveToCategoryScene(let controller): let categoryIDList = searchCategorySection.inputDataList.compactMap { $0.id } let nextController = SearchCategoryController() From 3ae769eec60798fa9786af2e080e053e124cb007 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:06:20 +0900 Subject: [PATCH 209/393] =?UTF-8?q?feat/#126:=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20updateBottomSearchList=20mutation=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sections에서 HomeGridSection을 찾음 - HomeGridSection에 새로 추가될 아이템을 넣어줌 - controller가 해당 아이템을 넣어줄 섹션을 알려주기 위한 indexPath 전달 --- .../Scene/Search/BeforeSearch/SearchReactor.swift | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 74d1ca5b..e229323a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -47,6 +47,15 @@ final class SearchReactor: Reactor { self.bottomSearchListLastIndexPath = nil } + mutating func updateBottomGridSection(by newItems: [HomeCardSectionCell.Input]) { + sections = sections.map { section in + if var grid = section as? HomeCardGridSection { + grid.inputDataList.append(contentsOf: newItems) + return grid + } + return section + } + } } // MARK: - properties @@ -239,6 +248,10 @@ final class SearchReactor: Reactor { case .resetSearchKeyWord: newState.searchKeyWord = nil newState.sections = getSection() + case .updateBottomSearchList(let newItems, let indexPath): + newState.updateBottomGridSection(by: newItems) + newState.newBottomSearchList = newItems + newState.bottomSearchListLastIndexPath = indexPath } return newState } From 7574d2bb6a8b4a82b6c4c3159a539a9d4b90e4a6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:07:09 +0900 Subject: [PATCH 210/393] =?UTF-8?q?refactor/#126:=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=EC=9D=B4=ED=95=B4=EB=A5=BC=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Search/BeforeSearch/SearchReactor.swift | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index e229323a..63c8179b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -248,6 +248,7 @@ final class SearchReactor: Reactor { case .resetSearchKeyWord: newState.searchKeyWord = nil newState.sections = getSection() + case .updateBottomSearchList(let newItems, let indexPath): newState.updateBottomGridSection(by: newItems) newState.newBottomSearchList = newItems @@ -334,7 +335,10 @@ final class SearchReactor: Reactor { ) .withUnretained(self) .map { (owner, response) in - let isLogin = response.loginYn + // 1) 새로 받아오기 전의 기존 아이템 개수 저장 + let previousCount = owner.searchListSection.inputDataList.count + + // 2) API 결과 매핑 let newItems = response.popUpStoreList.map { HomeCardSectionCell.Input( imagePath: $0.mainImageUrl, @@ -345,10 +349,11 @@ final class SearchReactor: Reactor { startDate: $0.startDate, endDate: $0.endDate, isBookmark: $0.bookmarkYn, - isLogin: isLogin + isLogin: response.loginYn ) } + // 3) 첫 페이지 vs 이후 페이지 분기 if owner.currentPage == 0 { // 첫 페이지는 전체 reload // SearchCountTitleSection 설정 From 904830f057f59cead9272bd8b564d771a3430a91 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:07:35 +0900 Subject: [PATCH 211/393] =?UTF-8?q?fix/#126:=20reloadData=EA=B0=80=20?= =?UTF-8?q?=EC=83=88=EB=A1=9C=EC=9A=B4=20=EC=83=81=ED=83=9C=EB=A1=9C=20?= =?UTF-8?q?=EA=B7=B8=EB=A0=A4=EC=A0=B8=EC=95=BC=EB=90=A0=EB=95=8C=EB=A7=8C?= =?UTF-8?q?=20=EB=8F=99=EC=9E=91=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 --- .../Scene/Search/BeforeSearch/SearchController.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 7e486a9b..305ef344 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -114,8 +114,9 @@ extension SearchController { .disposed(by: disposeBag) reactor.state + .filter { $0.newBottomSearchList.isEmpty && $0.bottomSearchListLastIndexPath == nil } .withUnretained(self) - .subscribe { (owner, state) in + .subscribe { owner, state in owner.sections = state.sections owner.mainView.contentCollectionView.reloadData() } From 57e7a844c3155e3f6f7374eb67f7773ef61b7ccf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 16:08:07 +0900 Subject: [PATCH 212/393] =?UTF-8?q?feat/#126:=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EC=85=98=EC=9D=84=20=EB=8F=99=EC=9E=91=ED=95=98=EA=B8=B0=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20controller=20bind=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BeforeSearch/SearchController.swift | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index 305ef344..cc473a5c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -121,6 +121,31 @@ extension SearchController { owner.mainView.contentCollectionView.reloadData() } .disposed(by: disposeBag) + + reactor.state + .map { (sections: $0.sections, + newItems: $0.newBottomSearchList, + indexPath: $0.bottomSearchListLastIndexPath) } + .filter { !$0.newItems.isEmpty && $0.indexPath != nil } + .withUnretained(self) + .subscribe { (owner, subscribeResponse) in + let (updatedSections, newPopUpItems, popUpGridindexPath) = subscribeResponse + guard let popUpGridindexPath = popUpGridindexPath else { return } + + let start = popUpGridindexPath.item + let count = newPopUpItems.count + let section = popUpGridindexPath.section + let indexPaths = (start.. Date: Thu, 1 May 2025 19:04:17 +0900 Subject: [PATCH 213/393] =?UTF-8?q?refactor/#127:=20log=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=8B=9C=EA=B7=B8=EB=8B=88=EC=B2=98=20=ED=99=95?= =?UTF-8?q?=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure/Logger/Logger.swift | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift index 977637ac..c636a5e6 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Logger/Logger.swift @@ -58,17 +58,14 @@ public struct Logger { return .debug case .info: return .info - case .error: - return .error - case .fault: - return .fault + case .error: return .error + case .fault: return .fault } } } - /// : 아래 옵션 주석 해제시 파일명/라인 번호를 로그 메시지에 포함 - // private static var isShowFileName: Bool = false // 파일 이름 포함 여부 - // private static var isShowLine: Bool = true // 라인 번호 포함 여부 + private static var isShowFileName: Bool = false + private static var isShowLine: Bool = true private static var isShowLog: Bool = true private static var loggers: [Level: os.Logger] = [:] @@ -84,23 +81,26 @@ public struct Logger { return logger } - /// : 파일명과 라인 정보 파라미터 포함 - // public static func log( - // _ message: Any, - // category: Level, - // level: LogLevel = .info, - // fileName: String = #file, - // line: Int = #line - // ) { public static func log( _ message: Any, category: Level, - level: LogLevel = .info + level: LogLevel = .info, + file: String = #file, + line: Int = #line ) { guard isShowLog else { return } let logger = getLogger(for: category) - let fullMessage = "\(category.categoryIcon) \(message)" + var fullMessage = "\(category.categoryIcon) \(message)" + + if isShowFileName { + let fileNameOnly = (file as NSString).lastPathComponent + fullMessage += " | 📁 \(fileNameOnly)" + } + + if isShowLine { + fullMessage += " | 📍 \(line)" + } logger.log(level: level.osLogType, "\(fullMessage, privacy: .public)") } From 12bbf10d0c59dfbb58bb645aa2262a8f80bfb769 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 1 May 2025 22:29:29 +0900 Subject: [PATCH 214/393] =?UTF-8?q?style/#126:=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 --- .../Presentation/Scene/Search/Main/SearchMainController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift index 0808019d..33d083b1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift @@ -48,7 +48,7 @@ extension SearchMainController { super.viewDidLoad() self.addViews() - self.setupContstraints() + self.setupConstraints() } override func viewDidAppear(_ animated: Bool) { @@ -72,7 +72,7 @@ private extension SearchMainController { .forEach { self.view.addSubview($0) } } - func setupContstraints() { + func setupConstraints() { self.dataSource = self self.isScrollEnabled = false From 8db3f9aa9f0be4499c54fb9b8340a730dd6871df Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 10:25:44 +0900 Subject: [PATCH 215/393] =?UTF-8?q?refactor/#131:=20=EB=94=94=EC=9E=90?= =?UTF-8?q?=EC=9D=B8=EC=8B=9C=EC=8A=A4=ED=85=9C=20=EB=AA=A8=EB=93=88?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents.xcworkspacedata | 3 + .../DesignSystem.xcodeproj/project.pbxproj | 477 ++++++++++++++++++ .../DesignSystem}/Components/PPButton.swift | 15 +- .../Components/PPCancelHeaderView.swift | 15 +- .../DesignSystem}/Components/PPLabel.swift | 11 +- .../DesignSystem}/Components/PPPicker.swift | 26 +- .../PPProgressIndicator.swift | 17 +- .../PPProgressIndicator/PPProgressView.swift | 17 +- .../Components/PPReturnHeaderView.swift | 16 +- .../Components/PPSegmentedControl.swift | 20 +- .../Extension/UIApplication+.swift | 0 .../Extension/UICollectionReusableView+.swift | 0 .../Extension/UICollectionViewCell+.swift | 7 + .../DesignSystem/Extension/UIColor+.swift | 0 .../DesignSystem/Extension/UIFont+.swift | 4 +- .../DesignSystem/Extension/UILabel+.swift | 9 +- .../Extension/UINavigationController+.swift | 9 +- .../Extension/UITableViewCell+.swift | 0 .../DesignSystem/Extension/UITextField+.swift | 9 +- .../DesignSystem/Extension/UIView+.swift | 9 +- .../Controllers/BaseTabmanController.swift | 8 +- .../Controllers/BaseViewController.swift | 8 +- .../Utills/Interfaces/InOutputable.swift | 12 + .../Sectionable/SectionDecorationItem.swift | 22 +- .../SectionSupplementaryItem.swift | 18 +- .../Interfaces/Sectionable/Sectionable.swift | 4 +- .../Presentation.xcodeproj/project.pbxproj | 18 +- .../Extension/UICollectionViewCell+.swift | 14 - .../AdminBottomSheetView.swift | 1 + .../AdminBottomSheetViewController.swift | 1 + .../PopUpImagesCollectionView.swift | 2 + .../PopUpStoreRegisterViewController.swift | 1 + .../Presentation/Scene/Admin/AdminView.swift | 5 +- .../Scene/Admin/AdminViewController.swift | 1 + .../Presentation/Scene/Admin/ImageCell.swift | 5 +- .../CommentCheck/CommentCheckController.swift | 9 +- .../CommentCheck/CommentCheckView.swift | 9 +- .../CommentDetailController.swift | 2 + .../CommentDetail/CommentDetailReactor.swift | 1 + .../CommentDetailContentSection.swift | 9 +- .../CommentDetailContentSectionCell.swift | 9 +- .../View/CommentDetailImageSection.swift | 9 +- .../View/CommentDetailView.swift | 9 +- .../CommentMyMenuController.swift | 9 +- .../CommentMyMenu/CommentMyMenuView.swift | 9 +- .../CommentUserInfoController.swift | 9 +- .../CommentUserInfo/CommentUserInfoView.swift | 9 +- .../CommentList/CommentListController.swift | 9 +- .../CommentList/CommentListReactor.swift | 1 + .../CommentListTitleSection.swift | 9 +- .../CommentListTitleSectionCell.swift | 9 +- .../CommentList/View/CommentListView.swift | 9 +- .../CommentSelectedController.swift | 9 +- .../CommentSelected/CommentSelectedView.swift | 9 +- .../CommentUserBlockController.swift | 9 +- .../CommentUserBlockView.swift | 9 +- .../NormalCommentAddController.swift | 9 +- .../NormalCommentAddReactor.swift | 1 + .../AddCommentDescriptionSection.swift | 9 +- .../AddCommentDescriptionSectionCell.swift | 9 +- .../AddCommentImageSection.swift | 9 +- .../AddCommentImageSectionCell.swift | 9 +- .../AddCommentSection/AddCommentSection.swift | 9 +- .../AddCommentSectionCell.swift | 9 +- .../AddCommentTitleSection.swift | 9 +- .../AddCommentTitleSectionCell.swift | 9 +- .../View/NormalCommentAddView.swift | 9 +- .../NormalCommentEditController.swift | 9 +- .../NormalCommentEditReactor.swift | 1 + .../NormalCommentEditView.swift | 9 +- .../OtherUserCommentController.swift | 9 +- .../OtherUserCommentReactor.swift | 1 + .../OtherUserCommentSection.swift | 9 +- .../OtherUserCommentSectionCell.swift | 9 +- .../View/OtherUserCommentView.swift | 9 +- .../Scene/Detail/DetailController.swift | 2 + .../Scene/Detail/DetailReactor.swift | 1 + .../DetailCommentImageCell.swift | 9 +- .../DetailCommentProfileView.swift | 9 +- .../DetailCommentSection.swift | 9 +- .../DetailCommentSectionCell.swift | 9 +- .../DetailCommentTitleSection.swift | 9 +- .../DetailCommentTitleSectionCell.swift | 9 +- .../DetailContentSection.swift | 9 +- .../DetailContentSectionCell.swift | 9 +- .../DetailEmptyCommetSection.swift | 9 +- .../DetailEmptyCommetSectionCell.swift | 9 +- .../DetailInfoSection/DetailInfoSection.swift | 9 +- .../DetailInfoSectionCell.swift | 9 +- .../DetailSimilarSection.swift | 9 +- .../DetailSimilarSectionCell.swift | 9 +- .../DetailTitleSection.swift | 9 +- .../DetailTitleSectionCell.swift | 9 +- .../Scene/Detail/View/DetailView.swift | 9 +- .../Scene/Home/List/HomeListController.swift | 9 +- .../Scene/Home/List/HomeListReactor.swift | 1 + .../Home/List/View/HomeCardGridSection.swift | 2 + .../Scene/Home/List/View/HomeListView.swift | 9 +- .../Scene/Home/Main/HomeController.swift | 2 + .../Scene/Home/Main/HomeReactor.swift | 1 + .../HomeCardSection/HomeCardSection.swift | 9 +- .../HomeCardSection/HomeCardSectionCell.swift | 2 + .../Scene/Home/Main/View/HomeHeaderView.swift | 9 +- .../HomePopularCardSection.swift | 9 +- .../HomePopularCardSectionCell.swift | 2 + .../HomeTitleSection/HomeTitleSection.swift | 9 +- .../HomeTitleSectionCell.swift | 9 +- .../Scene/Home/Main/View/HomeView.swift | 7 - .../ImageBannerChildSection.swift | 9 +- .../ImageBannerChildSectionCell.swift | 9 +- .../ImageBannerSection.swift | 9 +- .../ImageBannerSectionCell.swift | 2 + .../SectionBackGroundDecorationView.swift | 9 +- .../View/SpacingSection/SpacingSection.swift | 9 +- .../SpacingSection/SpacingSectionCell.swift | 9 +- .../ImageDetail/ImageDetailController.swift | 9 +- .../ImageDetail/ImageDetailReactor.swift | 7 +- .../Scene/Login/Main/LoginController.swift | 8 +- .../Scene/Login/Main/LoginReactor.swift | 1 + .../Scene/Login/Main/LoginView.swift | 9 +- .../Scene/Login/Sub/SubLoginController.swift | 9 +- .../Scene/Login/Sub/SubLoginReactor.swift | 1 + .../Scene/Login/Sub/SubLoginView.swift | 9 +- .../FillterSheetView/BalloonChipCell.swift | 5 +- .../FilterBottomSheetView.swift | 5 +- .../FillterSheetView/FilterChipsView.swift | 5 +- .../FullScreenMapViewController.swift | 1 + .../Scene/Map/MapView/MapMarker.swift | 5 +- .../Scene/Map/MapView/MapViewController.swift | 1 + .../Map/StoreListView/StoreListCell.swift | 5 +- .../Block/BlockUserManageController.swift | 9 +- .../MyPage/Block/BlockUserManageReactor.swift | 1 + .../BlockUserListSection.swift | 9 +- .../BlockUserListSectionCell.swift | 9 +- .../Block/View/BlockUserManageView.swift | 9 +- .../Main/MyPageBookmarkController.swift | 9 +- .../Bookmark/Main/MyPageBookmarkReactor.swift | 1 + .../Bookmark/Main/MyPageBookmarkView.swift | 9 +- .../Bookmark/Main/View/CountButtonView.swift | 9 +- .../PopUpCardSection/PopUpCardSection.swift | 9 +- .../PopUpCardSectionCell.swift | 9 +- .../View/PopUpCardSection/PopUpCardView.swift | 9 +- ...BookMarkPopUpViewTypeModalController.swift | 9 +- .../BookMarkPopUpViewTypeModalReactor.swift | 7 +- .../BookMarkPopUpViewTypeModalView.swift | 9 +- .../Scene/MyPage/FAQ/FAQController.swift | 9 +- .../Scene/MyPage/FAQ/FAQReactor.swift | 9 +- .../FAQDropdownSection.swift | 9 +- .../FAQDropdownSectionCell.swift | 9 +- .../Scene/MyPage/FAQ/View/FAQView.swift | 9 +- .../Scene/MyPage/Main/MyPageController.swift | 9 +- .../Scene/MyPage/Main/MyPageReactor.swift | 1 + .../MyPageCommentSection.swift | 9 +- .../MyPageCommentSectionCell.swift | 9 +- .../MyPageListSection/MyPageListSection.swift | 9 +- .../MyPageListSectionCell.swift | 9 +- .../MyPageLogoutSection.swift | 9 +- .../MyPageLogoutSectionCell.swift | 9 +- .../MyPageMyCommentTitleSection.swift | 9 +- .../MyPageMyCommentTitleSectionCell.swift | 9 +- .../MyPageProfileSection.swift | 9 +- .../MyPageProfileSectionCell.swift | 9 +- .../Scene/MyPage/Main/View/MyPageView.swift | 9 +- .../MyComment/Main/MyCommentController.swift | 9 +- .../MyComment/Main/MyCommentReactor.swift | 1 + .../ListCountButtonSection.swift | 9 +- .../ListCountButtonSectionCell.swift | 9 +- .../MyComment/Main/View/MyCommentView.swift | 9 +- .../MyCommentedPopUpGridSection.swift | 9 +- .../MyCommentedPopUpGridSectionCell.swift | 9 +- .../MyCommentSortedModalController.swift | 9 +- .../MyCommentSortedModalReactor.swift | 7 +- .../MyCommentSortedModalView.swift | 9 +- .../Detail/MyPageNoticeDetailController.swift | 9 +- .../Detail/MyPageNoticeDetailReactor.swift | 1 + .../Detail/MyPageNoticeDetailView.swift | 9 +- .../Notice/List/MyPageNoticeController.swift | 9 +- .../Notice/List/MyPageNoticeReactor.swift | 1 + .../Notice/List/View/MyPageNoticeView.swift | 9 +- .../NoticeListSection/NoticeListSection.swift | 9 +- .../NoticeListSectionCell.swift | 9 +- .../CategoryEditModalController.swift | 9 +- .../CategoryEditModalReactor.swift | 8 +- .../CategoryEditModalView.swift | 9 +- .../InfoEditModalController.swift | 9 +- .../InfoEditModal/InfoEditModalReactor.swift | 1 + .../InfoEditModal/InfoEditModalView.swift | 9 +- .../Main/ProfileEditController.swift | 1 + .../ProfileEdit/Main/ProfileEditReactor.swift | 1 + .../Main/View/ProfileEditView.swift | 9 +- .../Recent/MyPageRecentController.swift | 9 +- .../MyPage/Recent/MyPageRecentReactor.swift | 1 + .../MyPage/Recent/View/MyPageRecentView.swift | 9 +- .../RecentPopUpSection.swift | 9 +- .../MyPage/Terms/MyPageTermsController.swift | 9 +- .../MyPage/Terms/MyPageTermsReactor.swift | 10 +- .../WithdrawlCheckModalController.swift | 8 +- .../CheckModal/WithdrawlCheckModalView.swift | 9 +- .../WithdrawlCompleteController.swift | 9 +- .../Complete/WithdrawlCompleteView.swift | 9 +- .../View/WithdrawlCheckSection.swift | 9 +- .../View/WithdrawlCheckSectionCell.swift | 9 +- .../View/WithdrawlReasonView.swift | 9 +- .../WithdrawlReasonController.swift | 9 +- .../WithdrawlReasonReactor.swift | 1 + .../AfterSearch/SearchResultController.swift | 9 +- .../AfterSearch/SearchResultReactor.swift | 1 + .../SearchResultCountSection.swift | 9 +- .../SearchResultCountSectionCell.swift | 9 +- .../AfterSearch/View/SearchResultView.swift | 9 +- .../BeforeSearch/SearchController.swift | 9 +- .../Search/BeforeSearch/SearchReactor.swift | 1 + .../CancelableTagSection.swift | 9 +- .../CancelableTagSectionCell.swift | 9 +- .../SearchCountTitleSection.swift | 9 +- .../SearchCountTitleSectionCell.swift | 9 +- .../SearchTitleSection.swift | 9 +- .../SearchTitleSectionCell.swift | 9 +- .../SearchCategoryController.swift | 9 +- .../SearchCategoryReactor.swift | 1 + .../SearchCategoryView.swift | 9 +- .../Search/Main/SearchMainController.swift | 1 + .../Scene/Search/Main/SearchMainView.swift | 2 + .../SearchSortedController.swift | 9 +- .../SearchSortedReactor.swift | 7 +- .../SortedController/SearchSortedView.swift | 9 +- .../SignUp/Main/SignUpMainController.swift | 1 + .../Scene/SignUp/Main/SignUpMainReactor.swift | 1 + .../SignUp/Main/View/SignUpMainView.swift | 9 +- .../SignUpCompleteController.swift | 9 +- .../SignUpCompleteReactor.swift | 7 +- .../SignUpComplete/SignUpCompleteView.swift | 9 +- .../SignUp/Step1/SignUpStep1Controller.swift | 9 +- .../SignUp/Step1/SignUpStep1Reactor.swift | 10 +- .../Step1/View/SignUpCheckBoxButton.swift | 9 +- .../SignUp/Step1/View/SignUpStep1View.swift | 9 +- .../SignUp/Step1/View/SignUpTermsView.swift | 9 +- .../SignUp/Step2/SignUpStep2Controller.swift | 9 +- .../Scene/SignUp/Step2/SignUpStep2View.swift | 9 +- .../SignUp/Step3/SignUpStep3Controller.swift | 9 +- .../SignUp/Step3/SignUpStep3Reactor.swift | 1 + .../SignUp/Step3/View/SignUpStep3View.swift | 9 +- .../Step3/View/TagSection/TagSection.swift | 9 +- .../View/TagSection/TagSectionCell.swift | 9 +- .../AgeSelectedController.swift | 9 +- .../AgeSelectedModal/AgeSelectedReactor.swift | 7 +- .../AgeSelectedModal/AgeSelectedView.swift | 9 +- .../Step4/Main/SignUpStep4Controller.swift | 9 +- .../Step4/Main/SignUpStep4Reactor.swift | 7 +- .../Step4/Main/View/AgeSelectedButton.swift | 9 +- .../Step4/Main/View/SignUpStep4View.swift | 9 +- .../TermsDetail/TermsDetailController.swift | 9 +- .../SignUp/TermsDetail/TermsDetailView.swift | 9 +- .../Scene/Splash/SplashController.swift | 1 + .../Utills/Interfaces/InOutputable.swift | 19 - .../Utills/ToastMaker/BookMarkToastView.swift | 9 +- 256 files changed, 1021 insertions(+), 1421 deletions(-) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPButton.swift (92%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPCancelHeaderView.swift (85%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPLabel.swift (84%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPPicker.swift (78%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPProgressIndicator/PPProgressIndicator.swift (87%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPProgressIndicator/PPProgressView.swift (95%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPReturnHeaderView.swift (85%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Components/PPSegmentedControl.swift (91%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UIApplication+.swift (100%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UICollectionReusableView+.swift (100%) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UIColor+.swift (100%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UIFont+.swift (90%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UILabel+.swift (84%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UINavigationController+.swift (68%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UITableViewCell+.swift (100%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UITextField+.swift (69%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem}/DesignSystem/Extension/UIView+.swift (79%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Utills/Controllers/BaseTabmanController.swift (79%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Utills/Controllers/BaseViewController.swift (85%) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Utills/Interfaces/Sectionable/SectionDecorationItem.swift (70%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift (84%) rename Poppool/PresentationLayer/{Presentation/Presentation => DesignSystem/DesignSystem}/Utills/Interfaces/Sectionable/Sectionable.swift (99%) delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata index 5e470cb2..11addf63 100644 --- a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -21,6 +21,9 @@ + + diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj new file mode 100644 index 00000000..ebe58ead --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj @@ -0,0 +1,477 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 0516312C2DC3D1E900A6C0D1 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */; }; + 0516312D2DC3D1E900A6C0D1 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 051631602DC3D28400A6C0D1 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0516315F2DC3D28400A6C0D1 /* RxCocoa */; }; + 051631622DC3D28400A6C0D1 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 051631612DC3D28400A6C0D1 /* RxSwift */; }; + 051631652DC3D29400A6C0D1 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 051631642DC3D29400A6C0D1 /* SnapKit */; }; + 051631682DC3D3FA00A6C0D1 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 051631672DC3D3FA00A6C0D1 /* Tabman */; }; + 0516316B2DC3D50700A6C0D1 /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 0516316A2DC3D50700A6C0D1 /* Pageboy */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0516312E2DC3D1E900A6C0D1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 0516312D2DC3D1E900A6C0D1 /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 051630B82DC3D1A000A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 051630BA2DC3D1A000A6C0D1 /* DesignSystem */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = DesignSystem; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 051630B52DC3D1A000A6C0D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0516316B2DC3D50700A6C0D1 /* Pageboy in Frameworks */, + 051631682DC3D3FA00A6C0D1 /* Tabman in Frameworks */, + 051631622DC3D28400A6C0D1 /* RxSwift in Frameworks */, + 051631652DC3D29400A6C0D1 /* SnapKit in Frameworks */, + 051631602DC3D28400A6C0D1 /* RxCocoa in Frameworks */, + 0516312C2DC3D1E900A6C0D1 /* Infrastructure.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 051630AE2DC3D1A000A6C0D1 = { + isa = PBXGroup; + children = ( + 051630BA2DC3D1A000A6C0D1 /* DesignSystem */, + 0516312A2DC3D1E900A6C0D1 /* Frameworks */, + 051630B92DC3D1A000A6C0D1 /* Products */, + ); + sourceTree = ""; + }; + 051630B92DC3D1A000A6C0D1 /* Products */ = { + isa = PBXGroup; + children = ( + 051630B82DC3D1A000A6C0D1 /* DesignSystem.framework */, + ); + name = Products; + sourceTree = ""; + }; + 0516312A2DC3D1E900A6C0D1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 051630B32DC3D1A000A6C0D1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 051630B72DC3D1A000A6C0D1 /* DesignSystem */ = { + isa = PBXNativeTarget; + buildConfigurationList = 051630BF2DC3D1A000A6C0D1 /* Build configuration list for PBXNativeTarget "DesignSystem" */; + buildPhases = ( + 051630B32DC3D1A000A6C0D1 /* Headers */, + 051630B42DC3D1A000A6C0D1 /* Sources */, + 051630B52DC3D1A000A6C0D1 /* Frameworks */, + 051630B62DC3D1A000A6C0D1 /* Resources */, + 0516312E2DC3D1E900A6C0D1 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 051630BA2DC3D1A000A6C0D1 /* DesignSystem */, + ); + name = DesignSystem; + packageProductDependencies = ( + 0516315F2DC3D28400A6C0D1 /* RxCocoa */, + 051631612DC3D28400A6C0D1 /* RxSwift */, + 051631642DC3D29400A6C0D1 /* SnapKit */, + 051631672DC3D3FA00A6C0D1 /* Tabman */, + 0516316A2DC3D50700A6C0D1 /* Pageboy */, + ); + productName = DesignSystem; + productReference = 051630B82DC3D1A000A6C0D1 /* DesignSystem.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 051630AF2DC3D1A000A6C0D1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 051630B72DC3D1A000A6C0D1 = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 051630B22DC3D1A000A6C0D1 /* Build configuration list for PBXProject "DesignSystem" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 051630AE2DC3D1A000A6C0D1; + minimizedProjectReferenceProxies = 1; + packageReferences = ( + 0516315E2DC3D28400A6C0D1 /* XCRemoteSwiftPackageReference "RxSwift" */, + 051631632DC3D29400A6C0D1 /* XCRemoteSwiftPackageReference "SnapKit" */, + 051631662DC3D3FA00A6C0D1 /* XCRemoteSwiftPackageReference "Tabman" */, + 051631692DC3D50700A6C0D1 /* XCRemoteSwiftPackageReference "Pageboy" */, + ); + preferredProjectObjectVersion = 77; + productRefGroup = 051630B92DC3D1A000A6C0D1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 051630B72DC3D1A000A6C0D1 /* DesignSystem */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 051630B62DC3D1A000A6C0D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 051630B42DC3D1A000A6C0D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 051630BD2DC3D1A000A6C0D1 /* 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.4; + 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; + }; + 051630BE2DC3D1A000A6C0D1 /* 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.4; + 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; + }; + 051630C02DC3D1A000A6C0D1 /* 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.DesignSystem; + 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; + }; + 051630C12DC3D1A000A6C0D1 /* 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.DesignSystem; + 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; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 051630B22DC3D1A000A6C0D1 /* Build configuration list for PBXProject "DesignSystem" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 051630BD2DC3D1A000A6C0D1 /* Debug */, + 051630BE2DC3D1A000A6C0D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 051630BF2DC3D1A000A6C0D1 /* Build configuration list for PBXNativeTarget "DesignSystem" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 051630C02DC3D1A000A6C0D1 /* Debug */, + 051630C12DC3D1A000A6C0D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 0516315E2DC3D28400A6C0D1 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; + 051631632DC3D29400A6C0D1 /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.7.1; + }; + }; + 051631662DC3D3FA00A6C0D1 /* XCRemoteSwiftPackageReference "Tabman" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Tabman"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 051631692DC3D50700A6C0D1 /* XCRemoteSwiftPackageReference "Pageboy" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Pageboy"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.2.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 0516315F2DC3D28400A6C0D1 /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 0516315E2DC3D28400A6C0D1 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 051631612DC3D28400A6C0D1 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 0516315E2DC3D28400A6C0D1 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 051631642DC3D29400A6C0D1 /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 051631632DC3D29400A6C0D1 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; + 051631672DC3D3FA00A6C0D1 /* Tabman */ = { + isa = XCSwiftPackageProductDependency; + package = 051631662DC3D3FA00A6C0D1 /* XCRemoteSwiftPackageReference "Tabman" */; + productName = Tabman; + }; + 0516316A2DC3D50700A6C0D1 /* Pageboy */ = { + isa = XCSwiftPackageProductDependency; + package = 051631692DC3D50700A6C0D1 /* XCRemoteSwiftPackageReference "Pageboy" */; + productName = Pageboy; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = 051630AF2DC3D1A000A6C0D1 /* Project object */; +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPButton.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift similarity index 92% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPButton.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift index 5d99f435..c4e2a4cd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPButton.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPButton.swift @@ -1,15 +1,8 @@ -// -// PPButton.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit -class PPButton: UIButton { +public class PPButton: UIButton { - enum ButtonStyle { + public enum ButtonStyle { case primary case secondary case tertiary @@ -69,7 +62,7 @@ class PPButton: UIButton { } } - init( + public init( style: ButtonStyle, text: String, disabledText: String = "", @@ -100,7 +93,7 @@ class PPButton: UIButton { /// - Parameters: /// - color: 색상 /// - state: 상태 - func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { + public func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { UIGraphicsBeginImageContext(CGSize(width: 1.0, height: 1.0)) guard let context = UIGraphicsGetCurrentContext() else { return } context.setFillColor(color.cgColor) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPCancelHeaderView.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPCancelHeaderView.swift similarity index 85% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPCancelHeaderView.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPCancelHeaderView.swift index 4e1bd069..777f79cf 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPCancelHeaderView.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPCancelHeaderView.swift @@ -1,25 +1,18 @@ -// -// PPCancelHeaderView.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit import SnapKit -final class PPCancelHeaderView: UIView { +public final class PPCancelHeaderView: UIView { // MARK: - Components - let backButton: UIButton = { + public let backButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "icon_backButton"), for: .normal) button.tintColor = .black return button }() - let cancelButton: UIButton = { + public let cancelButton: UIButton = { let button = UIButton(type: .system) button.setTitle("취소", for: .normal) button.titleLabel?.font = .korFont(style: .regular, size: 14) @@ -28,7 +21,7 @@ final class PPCancelHeaderView: UIView { }() // MARK: - init - init() { + public init() { super.init(frame: .zero) setUpConstraints() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPLabel.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift similarity index 84% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPLabel.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift index 614638ac..32ce068f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPLabel.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPLabel.swift @@ -1,15 +1,8 @@ -// -// PPLabel.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit -class PPLabel: UILabel { +public class PPLabel: UILabel { - init( + public init( style: UIFont.FontStyle, fontSize: CGFloat, text: String = "", diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPPicker.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPPicker.swift similarity index 78% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPPicker.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPPicker.swift index acaa9456..21f007cb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPPicker.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPPicker.swift @@ -1,20 +1,14 @@ -// -// PPPicker.swift -// PopPool -// -// Created by SeoJunYoung on 7/3/24. -// +import UIKit import RxCocoa import RxSwift import SnapKit -import UIKit -final class PPPicker: UIView { +public final class PPPicker: UIView { // MARK: - Components private let components: [String] - let pickerView = UIPickerView() + public let pickerView = UIPickerView() private var selectView: UIView = { let view = UIView() view.backgroundColor = .g50 @@ -28,7 +22,7 @@ final class PPPicker: UIView { // MARK: - init /// PickerCPNT init /// - Parameter components: UIPickerView에 표시할 문자열 배열입니다. - init(components: [String]) { + public init(components: [String]) { self.components = components super.init(frame: .zero) setUp() @@ -78,22 +72,22 @@ extension PPPicker { /// 지정된 인덱스로 UIPickerView를 설정 /// - Parameter index: 설정할 인덱스 값 - func setIndex(index: Int) { + public func setIndex(index: Int) { pickerView.selectRow(index, inComponent: 0, animated: true) } } // MARK: - Delegate extension PPPicker: UIPickerViewDelegate, UIPickerViewDataSource { - func numberOfComponents(in pickerView: UIPickerView) -> Int { + public func numberOfComponents(in pickerView: UIPickerView) -> Int { return 1 } - func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { + public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { return components.count } - func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { + public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView { let label = UILabel() label.text = components[row] label.textAlignment = .center @@ -107,11 +101,11 @@ extension PPPicker: UIPickerViewDelegate, UIPickerViewDataSource { return label } - func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { + public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat { return 48 } - func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { + public func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { pickerView.reloadAllComponents() } } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressIndicator.swift similarity index 87% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressIndicator.swift index ec709b83..55537a9e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressIndicator.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressIndicator.swift @@ -1,17 +1,10 @@ -// -// PPProgressIndicator.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// +import UIKit -import Foundation import RxCocoa import RxSwift import SnapKit -import UIKit -final class PPProgressIndicator: UIStackView { +public final class PPProgressIndicator: UIStackView { // MARK: - Properties private var progressViews: [PPProgressView] @@ -22,7 +15,7 @@ final class PPProgressIndicator: UIStackView { /// - Parameters: /// - totalStep: 전체 단계 수 /// - startPoint: 초기 시작 지점 (1부터 시작하는 인덱스) - init(totalStep: Int, startPoint: Int) { + public init(totalStep: Int, startPoint: Int) { self.progressViews = (1...totalStep).map({ index in return PPProgressView(isSelected: index == startPoint) }) @@ -59,7 +52,7 @@ private extension PPProgressIndicator { extension PPProgressIndicator { /// 진행 인디케이터를 한 단계 앞으로 이동 - func increaseIndicator() { + public func increaseIndicator() { if progressIndex < progressViews.count { progressViews[progressIndex - 1].disappearAnimation(option: .fromLeft) progressIndex += 1 @@ -68,7 +61,7 @@ extension PPProgressIndicator { } /// 진행 인디케이터를 한 단계 뒤로 이동 - func decreaseIndicator() { + public func decreaseIndicator() { if progressIndex - 1 > 0 { progressViews[progressIndex - 1].disappearAnimation(option: .fromRight) progressIndex -= 1 diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressView.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressView.swift similarity index 95% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressView.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressView.swift index 60a3137a..b456b27e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPProgressIndicator/PPProgressView.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPProgressIndicator/PPProgressView.swift @@ -1,18 +1,11 @@ -// -// PPProgressView.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - -import Foundation -import SnapKit import UIKit -final class PPProgressView: UIView { +import SnapKit + +public final class PPProgressView: UIView { /// CMTPProgressView Animation Type - enum ProgressFillAnimation { + public enum ProgressFillAnimation { case fromLeft case fromRight } @@ -34,7 +27,7 @@ final class PPProgressView: UIView { /// CMTPProgressView 초기화 /// - Parameter isSelected: 선택 여부 - init(isSelected: Bool) { + public init(isSelected: Bool) { self.selectedView.isHidden = !isSelected super.init(frame: .zero) setUpConstraints() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPReturnHeaderView.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPReturnHeaderView.swift similarity index 85% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPReturnHeaderView.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPReturnHeaderView.swift index f8589886..21dd41f9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPReturnHeaderView.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPReturnHeaderView.swift @@ -1,24 +1,18 @@ -// -// PPReturnHeaderView.swift -// Poppool -// -// Created by Porori on 11/27/24. -// +import UIKit import SnapKit -import UIKit -final class PPReturnHeaderView: UIView { +public final class PPReturnHeaderView: UIView { // MARK: - Components - let backButton: UIButton = { + public let backButton: UIButton = { let button = UIButton(type: .system) button.setImage(UIImage(named: "icon_backButton"), for: .normal) button.tintColor = .black return button }() - let headerLabel: UILabel = { + public let headerLabel: UILabel = { let label = UILabel() label.font = .korFont(style: .regular, size: 15) label.textColor = .g1000 @@ -26,7 +20,7 @@ final class PPReturnHeaderView: UIView { }() // MARK: - init - init() { + public init() { super.init(frame: .zero) setUpConstraints() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPSegmentedControl.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSegmentedControl.swift similarity index 91% rename from Poppool/PresentationLayer/Presentation/Presentation/Components/PPSegmentedControl.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSegmentedControl.swift index e9cde785..1f869e68 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Components/PPSegmentedControl.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSegmentedControl.swift @@ -1,17 +1,11 @@ -// -// PPSegmentedControl.swift -// PopPool -// -// Created by SeoJunYoung on 6/27/24. -// +import UIKit import SnapKit -import UIKit -final class PPSegmentedControl: UISegmentedControl { +public final class PPSegmentedControl: UISegmentedControl { /// 세그먼트 컨트롤 타입 - enum SegmentedControlType { + public enum SegmentedControlType { case radio case base case tab @@ -32,7 +26,7 @@ final class PPSegmentedControl: UISegmentedControl { return view }() - init(type: SegmentedControlType, segments: [String], selectedSegmentIndex: Int? = nil) { + public init(type: SegmentedControlType, segments: [String], selectedSegmentIndex: Int? = nil) { super.init(frame: .zero) setUpSegments(type: type, segments: segments) if let selectedSegmentIndex = selectedSegmentIndex { @@ -46,7 +40,7 @@ final class PPSegmentedControl: UISegmentedControl { } /// 서브뷰 레이아웃 설정 - override func layoutSubviews() { + public override func layoutSubviews() { super.layoutSubviews() // layout이 업데이트 될 때 underbar 업데이트 let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(self.selectedSegmentIndex) @@ -66,7 +60,7 @@ private extension PPSegmentedControl { /// - Parameters: /// - type: 세그먼트 컨트롤 타입 /// - segments: 세그먼트 타이틀 배열 - func setUpSegments(type: SegmentedControlType, segments: [String]) { + public func setUpSegments(type: SegmentedControlType, segments: [String]) { let emptyImage = UIImage() for seg in segments.reversed() { self.insertSegment(withTitle: seg, at: 0, animated: true) @@ -128,7 +122,7 @@ private extension PPSegmentedControl { /// - color: 폰트 색상 /// - font: 폰트 스타일 /// - state: 세그먼트 상태 - func setFont(color: UIColor, font: UIFont?, state: UIControl.State) { + public func setFont(color: UIColor, font: UIFont?, state: UIControl.State) { let paragraphStyle = NSMutableParagraphStyle() paragraphStyle.lineHeightMultiple = 1.2 self.setTitleTextAttributes([ diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIApplication+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIApplication+.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIApplication+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIApplication+.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionReusableView+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionReusableView+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift new file mode 100644 index 00000000..3200f30e --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift @@ -0,0 +1,7 @@ +import UIKit + +public extension UICollectionViewCell { + public static var identifiers: String { + return String(describing: self) + } +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIColor+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIColor+.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIColor+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIColor+.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift similarity index 90% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift index 3720eb67..134846c0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIFont+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift @@ -1,5 +1,3 @@ -import Foundation - import UIKit public extension UIFont { @@ -11,7 +9,7 @@ public extension UIFont { return UIFont(name: "Poppins\(style.rawValue)", size: size) } - enum FontStyle: String { + public enum FontStyle: String { case bold = "-Bold" case medium = "-Medium" case regular = "-Regular" diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UILabel+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift similarity index 84% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UILabel+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift index 592fa79a..eb0caec1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UILabel+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UILabel+.swift @@ -1,13 +1,6 @@ -// -// UILabel+.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit -extension UILabel { +public extension UILabel { func setLineHeightText(text: String?, font: UIFont?, lineHeight: CGFloat = 1.3) { guard let text = text, let font = font else { return } let paragraphStyle = NSMutableParagraphStyle() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UINavigationController+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UINavigationController+.swift similarity index 68% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UINavigationController+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UINavigationController+.swift index 62923a54..b154ee64 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UINavigationController+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UINavigationController+.swift @@ -1,13 +1,6 @@ -// -// UINavigationController+.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit -extension UINavigationController { +public extension UINavigationController { func popViewController(animated: Bool, completion: (() -> Void)?) { CATransaction.begin() CATransaction.setCompletionBlock { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITableViewCell+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UITableViewCell+.swift similarity index 100% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITableViewCell+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UITableViewCell+.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITextField+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UITextField+.swift similarity index 69% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITextField+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UITextField+.swift index da25f377..883720b9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UITextField+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UITextField+.swift @@ -1,13 +1,6 @@ -// -// UITextField+.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit -extension UITextField { +public extension UITextField { func setPlaceholder(text: String, color: UIColor, font: UIFont) { self.attributedPlaceholder = NSAttributedString( string: text, diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIView+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIView+.swift similarity index 79% rename from Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIView+.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIView+.swift index a24684e7..44394f22 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UIView+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIView+.swift @@ -1,13 +1,6 @@ -// -// UIView+.swift -// Poppool -// -// Created by SeoJunYoung on 12/2/24. -// - import UIKit -extension UIView { +public extension UIView { func shake() { let animation = CAKeyframeAnimation(keyPath: "transform.translation.x") animation.timingFunction = CAMediaTimingFunction(name: .linear) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseTabmanController.swift similarity index 79% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseTabmanController.swift index 42a777dd..7e343ca9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseTabmanController.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseTabmanController.swift @@ -5,8 +5,8 @@ import Infrastructure import Pageboy import Tabman -class BaseTabmanController: TabmanViewController { - init() { +open class BaseTabmanController: TabmanViewController { + public init() { super.init(nibName: nil, bundle: nil) Logger.log( message: "\(self) init", @@ -16,11 +16,11 @@ class BaseTabmanController: TabmanViewController { ) } - required init?(coder aDecoder: NSCoder) { + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - override func viewDidLoad() { + open override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .systemBackground self.navigationController?.navigationBar.isHidden = true diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseViewController.swift similarity index 85% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseViewController.swift index 03eeded3..14a8e914 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Controllers/BaseViewController.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Controllers/BaseViewController.swift @@ -5,9 +5,9 @@ import Infrastructure import RxCocoa import RxSwift -public class BaseViewController: UIViewController { +open class BaseViewController: UIViewController { - var systemStatusBarIsDark: BehaviorRelay = .init(value: true) + public var systemStatusBarIsDark: BehaviorRelay = .init(value: true) var systemStatusBarDisposeBag = DisposeBag() public init() { @@ -24,14 +24,14 @@ public class BaseViewController: UIViewController { fatalError("init(coder:) has not been implemented") } - public override func viewDidLoad() { + open override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .white self.navigationController?.navigationBar.isHidden = true systemStatusBarIsDarkBind() } - public override func viewWillAppear(_ animated: Bool) { + open override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) systemStatusBarIsDark.accept(systemStatusBarIsDark.value) } diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift new file mode 100644 index 00000000..d1751a93 --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift @@ -0,0 +1,12 @@ +import UIKit + +public protocol InOutputable: Inputable, Outputable { } + +public protocol Inputable { + associatedtype Input + func injection(with input: Input) +} + +public protocol Outputable { + associatedtype Output +} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionDecorationItem.swift similarity index 70% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionDecorationItem.swift index 5b37a557..586255b9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionDecorationItem.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionDecorationItem.swift @@ -9,22 +9,32 @@ import UIKit /// `SectionDecorationItem` 구조체는 섹션에 추가될 데코레이션 뷰에 대한 정보를 정의합니다. /// 제네릭 타입 `View`는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. -struct SectionDecorationItem: SectionDecorationItemable { - typealias ReusableView = View +public struct SectionDecorationItem: SectionDecorationItemable { + public init( + elementKind: String, + reusableView: ReusableView, + viewInput: ReusableView.Input + ) { + self.elementKind = elementKind + self.reusableView = reusableView + self.viewInput = viewInput + } + + public typealias ReusableView = View /// 데코레이션 뷰의 종류를 나타내는 문자열입니다. - var elementKind: String + public var elementKind: String /// 데코레이션 뷰의 인스턴스입니다. - var reusableView: ReusableView + public var reusableView: ReusableView /// 데코레이션 뷰에 주입될 데이터입니다. - var viewInput: ReusableView.Input + public var viewInput: ReusableView.Input } /// `SectionDecorationItemable` 프로토콜은 데코레이션 뷰에 대한 인터페이스를 정의합니다. /// 이 프로토콜을 준수하는 타입은 데코레이션 뷰를 설정하고 반환하는 기능을 가져야 합니다. -protocol SectionDecorationItemable { +public protocol SectionDecorationItemable { /// 데코레이션 뷰의 타입을 정의합니다. 이 뷰는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. associatedtype ReusableView: UICollectionReusableView & Inputable diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift similarity index 84% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index c37a5e4b..7cd7caa0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -4,19 +4,19 @@ import Infrastructure /// `SectionSupplementaryItem` 구조체는 섹션에 추가될 Supplementary View에 대한 정보를 정의합니다. /// 제네릭 타입 `View`는 `UICollectionReusableView`와 `InOutputable` 프로토콜을 준수해야 합니다. -struct SectionSupplementaryItem: SectionSupplementaryItemable { - typealias ReusableView = View - var widthDimension: NSCollectionLayoutDimension - var heightDimension: NSCollectionLayoutDimension - var elementKind: String - var alignment: NSRectAlignment - var reusableView: ReusableView - var viewInput: ReusableView.Input +public struct SectionSupplementaryItem: SectionSupplementaryItemable { + public typealias ReusableView = View + public var widthDimension: NSCollectionLayoutDimension + public var heightDimension: NSCollectionLayoutDimension + public var elementKind: String + public var alignment: NSRectAlignment + public var reusableView: ReusableView + public var viewInput: ReusableView.Input } /// `SectionSupplementaryItemable` 프로토콜은 Supplementary View에 대한 인터페이스를 정의합니다. /// 해당 프로토콜을 준수하는 타입은 Supplementary View를 설정하고 반환하는 기능을 가져야 합니다. -protocol SectionSupplementaryItemable { +public protocol SectionSupplementaryItemable { /// Supplementary View의 타입을 정의합니다. 이 뷰는 `UICollectionReusableView`와 `Inputable` 프로토콜을 준수해야 합니다. associatedtype ReusableView: UICollectionReusableView, Inputable diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/Sectionable.swift similarity index 99% rename from Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/Sectionable.swift index 7153b6a7..204f3cb0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/Sectionable/Sectionable.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/Sectionable.swift @@ -7,7 +7,7 @@ import SnapKit /// `Sectionable` 프로토콜은 컬렉션 뷰의 섹션 및 셀을 설정하기 위한 인터페이스를 정의합니다. /// 해당 프로토콜은 제네릭 타입 `CellType`을 사용하여 유연하게 컬렉션 뷰 셀을 처리할 수 있습니다. -protocol Sectionable { +public protocol Sectionable { /// 컬렉션 뷰 셀의 타입을 정의합니다. 이 셀은 `UICollectionViewCell`과 `Inputable` 프로토콜을 준수해야 합니다. associatedtype CellType: UICollectionViewCell, Inputable @@ -62,7 +62,7 @@ protocol Sectionable { ) -> UICollectionReusableView } -extension Sectionable { +public extension Sectionable { /// `setSection` 메서드를 호출하여 섹션 레이아웃을 설정하고, Supplementary View를 추가합니다. /// diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index d67c1f31..9de8fcd1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; + 051631302DC3D1FD00A6C0D1 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; }; + 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1E02DB67C8300B141FF /* RxSwift */; }; 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CB2DB6756500C1E192 /* SnapKit */; }; 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; @@ -31,6 +33,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -38,6 +41,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 058CC9042DB537960084221A /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -57,6 +61,7 @@ buildActionMask = 2147483647; files = ( 08B2A3672DB66BD400E57EFA /* Pageboy in Frameworks */, + 051631302DC3D1FD00A6C0D1 /* DesignSystem.framework in Frameworks */, 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */, 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, @@ -97,6 +102,7 @@ 05C1D6292DB53A8200508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */, 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */, 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */, ); @@ -372,11 +378,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Presentation; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -405,11 +415,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Presentation; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift b/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift deleted file mode 100644 index 3c10fbac..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/DesignSystem/Extension/UICollectionViewCell+.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// UICollectionViewCell+.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/20/24. -// - -import UIKit - -extension UICollectionViewCell { - static var identifiers: String { - return String(describing: self) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 69a94cbe..9d97750e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -1,6 +1,7 @@ import UIKit import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 89ee0c06..5fdf6a2b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -1,6 +1,7 @@ import UIKit import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift index e36be825..d74d2b44 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import SnapKit final class PopUpImagesCollectionView: UICollectionView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 6d4523e8..49287424 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -4,6 +4,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift index 2d13cc17..7393cd2a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminView.swift @@ -1,6 +1,9 @@ +import UIKit + +import DesignSystem + import SnapKit import Then -import UIKit final class AdminView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index ddf71a45..b3157905 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift index 04704d15..e7062d80 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DesignSystem + +import SnapKit + final class ImageCell: UICollectionViewCell { static let identifier = "ImageCell" diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift index f1e20e9f..68fd16d9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckController.swift @@ -1,12 +1,7 @@ -// -// CommentCheckController.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift index c16fb508..610435fd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentCheck/CommentCheckView.swift @@ -1,12 +1,7 @@ -// -// CommentCheckView.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentCheckView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift index 6c8b4cbc..aec59f84 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailController.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 510d5841..aa63388e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift index b127b716..49dbefca 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSection.swift @@ -1,12 +1,7 @@ -// -// CommentDetailContentSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import RxSwift struct CommentDetailContentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift index 43237524..96bea9e7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailContentSection/CommentDetailContentSectionCell.swift @@ -1,12 +1,7 @@ -// -// CommentDetailContentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift index a3c61126..d307848f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailImageSection.swift @@ -1,12 +1,7 @@ -// -// CommentDetailImageSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import RxSwift struct CommentDetailImageSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift index c75acd0f..efbe9281 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/View/CommentDetailView.swift @@ -1,12 +1,7 @@ -// -// CommentDetailView.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentDetailView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift index 088653fe..5d672f41 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuController.swift @@ -1,12 +1,7 @@ -// -// CommentMyMenuController.swift -// Poppool -// -// Created by SeoJunYoung on 2/1/25. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift index a6034782..96d8daa8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentMyMenu/CommentMyMenuView.swift @@ -1,12 +1,7 @@ -// -// CommentMyMenuView.swift -// Poppool -// -// Created by SeoJunYoung on 2/1/25. -// - import UIKit +import DesignSystem + import SnapKit final class CommentMyMenuView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift index 41bbe402..d5adc11a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoController.swift @@ -1,12 +1,7 @@ -// -// CommentUserInfoController.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift index f3a6927e..4ace72f5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentInfoMenu/CommentUserInfo/CommentUserInfoView.swift @@ -1,12 +1,7 @@ -// -// CommentUserInfoView.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentUserInfoView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift index a11d2f53..574bc41a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListController.swift @@ -1,12 +1,7 @@ -// -// CommentListController.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index e0b71792..c2bd8f9b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift index 74440198..9e5ee0ae 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSection.swift @@ -1,12 +1,7 @@ -// -// CommentListTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import RxSwift struct CommentListTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift index da43fc9a..a4d6fd3d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListTitleSection/CommentListTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// CommentListTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift index 66c5c957..2a8a2735 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/View/CommentListView.swift @@ -1,12 +1,7 @@ -// -// CommentListView.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentListView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift index 0756496e..03495b7b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedController.swift @@ -1,12 +1,7 @@ -// -// CommentSelectedController.swift -// Poppool -// -// Created by SeoJunYoung on 12/13/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift index e77d939b..0c6b341d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentSelected/CommentSelectedView.swift @@ -1,12 +1,7 @@ -// -// CommentSelectedView.swift -// Poppool -// -// Created by SeoJunYoung on 12/13/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentSelectedView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift index 75166c4d..8500bae7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockController.swift @@ -1,12 +1,7 @@ -// -// CommentUserBlockController.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift index 3ca9705b..f178c28a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentUserBlock/CommentUserBlockView.swift @@ -1,12 +1,7 @@ -// -// CommentUserBlockView.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import SnapKit final class CommentUserBlockView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift index c2cf2ca4..4545885d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddController.swift @@ -1,12 +1,7 @@ -// -// NormalCommentAddController.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxKeyboard diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index b68f78ed..b89b4f2c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -3,6 +3,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift index b7559602..ba483b34 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSection.swift @@ -1,12 +1,7 @@ -// -// AddCommentDescriptionSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift struct AddCommentDescriptionSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift index dd8cd85f..a42a0fbb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentDescriptionSection/AddCommentDescriptionSectionCell.swift @@ -1,12 +1,7 @@ -// -// AddCommentDescriptionSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift index f1df7177..52d47c0e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSection.swift @@ -1,12 +1,7 @@ -// -// AddCommentImageSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift struct AddCommentImageSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift index dc954aa0..8d732900 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentImageSection/AddCommentImageSectionCell.swift @@ -1,12 +1,7 @@ -// -// AddCommentImageSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift index 9ef3468c..ef8bdb14 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSection.swift @@ -1,12 +1,7 @@ -// -// AddCommentSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import UIKit +import DesignSystem + import RxSwift struct AddCommentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift index b81b98f3..a2447743 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentSection/AddCommentSectionCell.swift @@ -1,12 +1,7 @@ -// -// AddCommentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/15/24. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift index d8e99796..0b5f1f4a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSection.swift @@ -1,12 +1,7 @@ -// -// AddCommentTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift struct AddCommentTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift index b5847ee0..fbbf718b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/AddCommentTitleSection/AddCommentTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// AddCommentTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift index 819e9200..a969b3d2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/View/NormalCommentAddView.swift @@ -1,12 +1,7 @@ -// -// NormalCommentAddView.swift -// Poppool -// -// Created by SeoJunYoung on 12/14/24. -// - import UIKit +import DesignSystem + import SnapKit final class NormalCommentAddView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift index e451afc4..d7dc9399 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditController.swift @@ -1,12 +1,7 @@ -// -// NormalCommentEditController.swift -// Poppool -// -// Created by SeoJunYoung on 2/1/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxKeyboard diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift index 1ad07a70..378fcc05 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditReactor.swift @@ -1,6 +1,7 @@ import PhotosUI import UIKit +import DesignSystem import DomainInterface import Infrastructure diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift index ee06753a..93eaf9b5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentEdit/NormalCommentEditView.swift @@ -1,12 +1,7 @@ -// -// NormalCommentEditView.swift -// Poppool -// -// Created by SeoJunYoung on 2/1/25. -// - import UIKit +import DesignSystem + import SnapKit final class NormalCommentEditView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift index cbe375f7..9c5c5e0e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentController.swift @@ -1,12 +1,7 @@ -// -// OtherUserCommentController.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index 8d8506db..b101ead1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift index 42954f36..d6c4fcca 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSection.swift @@ -1,12 +1,7 @@ -// -// OtherUserCommentSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// - import UIKit +import DesignSystem + import RxSwift struct OtherUserCommentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift index 7f1a0770..4b232be9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentSection/OtherUserCommentSectionCell.swift @@ -1,12 +1,7 @@ -// -// OtherUserCommentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift index 0cd89426..782faaaf 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/View/OtherUserCommentView.swift @@ -1,12 +1,7 @@ -// -// OtherUserCommentView.swift -// Poppool -// -// Created by SeoJunYoung on 12/27/24. -// - import UIKit +import DesignSystem + import SnapKit final class OtherUserCommentView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift index ee001146..b10e56a6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 43b3121c..379eca54 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import LinkPresentation import ReactorKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift index 514b9bf9..e2702c91 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentImageCell.swift @@ -1,12 +1,7 @@ -// -// DetailCommentImageCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift index 44ec4a67..ea9b6839 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentProfileView.swift @@ -1,12 +1,7 @@ -// -// DetailCommentProfileView.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import SnapKit final class DetailCommentProfileView: UIStackView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift index 4f08bab2..478205b0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSection.swift @@ -1,12 +1,7 @@ -// -// DetailCommentSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailCommentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift index 4251a119..9af61fc9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailCommentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift index d2979800..48012717 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSection.swift @@ -1,12 +1,7 @@ -// -// DetailCommentTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/18/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailCommentTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift index f7c37325..b1c01a55 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailCommentTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/18/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift index 8d9c926c..5f1ee0cc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSection.swift @@ -1,12 +1,7 @@ -// -// DetailContentSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailContentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift index 02e98e1c..012be73b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailContentSection/DetailContentSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailContentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift index 3cda98e4..3cbc9fda 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSection.swift @@ -1,12 +1,7 @@ -// -// DetailEmptyCommetSection.swift -// Poppool -// -// Created by SeoJunYoung on 2/4/25. -// - import UIKit +import DesignSystem + import RxSwift struct DetailEmptyCommetSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift index 76e43892..782726dd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailEmptyCommetSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 2/4/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift index f869c617..0bc8ff41 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSection.swift @@ -1,12 +1,7 @@ -// -// DetailInfoSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/18/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailInfoSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift index 2a81ca35..160a5eee 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailInfoSection/DetailInfoSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailInfoSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/18/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift index c62203f8..245ade0c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSection.swift @@ -1,12 +1,7 @@ -// -// DetailSimilarSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailSimilarSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift index 3ca56575..265b2a10 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailSimilarSection/DetailSimilarSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailSimilarSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/19/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift index 8b2b7ee2..674ee593 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSection.swift @@ -1,12 +1,7 @@ -// -// DetailTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import UIKit +import DesignSystem + import RxSwift struct DetailTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift index 907df869..d014e76d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailTitleSection/DetailTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// DetailTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/10/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift index 0e286f01..fabc06b0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailView.swift @@ -1,12 +1,7 @@ -// -// DetailView.swift -// Poppool -// -// Created by SeoJunYoung on 12/9/24. -// - import UIKit +import DesignSystem + import SnapKit final class DetailView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift index 8fe382bf..50d0c9bc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListController.swift @@ -1,12 +1,7 @@ -// -// HomeListController.swift -// Poppool -// -// Created by SeoJunYoung on 12/2/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift index f958730d..8a6d666f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift index 92406df4..404ee0f9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeCardGridSection.swift @@ -7,6 +7,8 @@ import UIKit +import DesignSystem + import RxSwift struct HomeCardGridSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift index e4769564..3ff3adc7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/View/HomeListView.swift @@ -1,12 +1,7 @@ -// -// HomeListView.swift -// Poppool -// -// Created by SeoJunYoung on 12/2/24. -// - import UIKit +import DesignSystem + import SnapKit final class HomeListView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift index fcf6d180..990f7ce3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift index b27415c1..b3383fc2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift index 23e570ee..1b27d1b2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSection.swift @@ -1,12 +1,7 @@ -// -// HomeCardSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift struct HomeCardSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift index c69e6409..664f82c1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeCardSection/HomeCardSectionCell.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift index ed9aadf7..28dac37a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeHeaderView.swift @@ -1,12 +1,7 @@ -// -// HomeHeaderView.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import SnapKit final class HomeHeaderView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift index 4196db68..c64754ff 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSection.swift @@ -1,12 +1,7 @@ -// -// HomePopularCardSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift struct HomePopularCardSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift index 33170f64..a7f8816f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomePopularCardSection/HomePopularCardSectionCell.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift index 3be6b834..9ec1db9c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSection.swift @@ -1,12 +1,7 @@ -// -// HomeTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift struct HomeTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift index cf0f58cb..eeebf6c6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeTitleSection/HomeTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// HomeTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift index 3710e7e0..3a47888e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/HomeView.swift @@ -1,10 +1,3 @@ -// -// HomeView.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - import UIKit import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift index 8d323718..43cecf09 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSection.swift @@ -1,12 +1,7 @@ -// -// ImageBannerChildSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/29/24. -// - import UIKit +import DesignSystem + import RxSwift struct ImageBannerChildSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift index 173f05ed..15784d70 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerChildSection/ImageBannerChildSectionCell.swift @@ -1,12 +1,7 @@ -// -// ImageBannerChildSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/29/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift index e60a5a6d..2aaab02c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSection.swift @@ -1,12 +1,7 @@ -// -// ImageBannerSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/28/24. -// - import UIKit +import DesignSystem + import RxSwift struct ImageBannerSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift index c6d16cef..5b904b9f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/ImageBannerSection/ImageBannerSection/ImageBannerSectionCell.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift index 300f838a..3f47504a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SectionBackGroundDecorationView.swift @@ -1,12 +1,7 @@ -// -// SectionBackGroundDecorationView.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + class SectionBackGroundDecorationView: UICollectionReusableView { // Decoration view의 UI 요소를 추가합니다. override init(frame: CGRect) { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift index 0c7ae8ee..97591ef2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSection.swift @@ -1,12 +1,7 @@ -// -// SpacingSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift struct SpacingSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift index 5cb66eb4..a03d7330 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/View/SpacingSection/SpacingSectionCell.swift @@ -1,12 +1,7 @@ -// -// SpacingSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/30/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift index 49a2a231..c0e4035f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailController.swift @@ -1,12 +1,7 @@ -// -// ImageDetailController.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift index f94bb7bc..7af5a8c5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/ImageDetail/ImageDetailReactor.swift @@ -1,9 +1,4 @@ -// -// ImageDetailReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/25/24. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift index b8824c0d..79797d54 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift @@ -1,13 +1,7 @@ -// -// LoginController.swift -// Poppool -// -// Created by SeoJunYoung on 11/24/24. -// - import UIKit import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index e4c22d6e..f83e8db7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -1,5 +1,6 @@ import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift index 437232c9..62d5b8ad 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginView.swift @@ -1,12 +1,7 @@ -// -// LoginView.swift -// Poppool -// -// Created by SeoJunYoung on 11/24/24. -// - import UIKit +import DesignSystem + import SnapKit final class LoginView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift index 37e32b43..f1d42ee9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginController.swift @@ -1,12 +1,7 @@ -// -// SubLoginController.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 8b194b1b..3dee4356 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -1,5 +1,6 @@ import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift index c1a34e01..cea96869 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginView.swift @@ -1,12 +1,7 @@ -// -// SubLoginView.swift -// Poppool -// -// Created by SeoJunYoung on 12/28/24. -// - import UIKit +import DesignSystem + import SnapKit final class SubLoginView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 964e5995..4cd3c98a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DesignSystem + +import SnapKit + final class BalloonChipCell: UICollectionViewCell { static let identifier = "BalloonChipCell" diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift index b7414a4b..fd0aa4fa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DesignSystem + +import SnapKit + final class FilterBottomSheetView: UIView { // MARK: - UI Components private let containerView: UIView = { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift index 9e05b052..03c81149 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterChipsView.swift @@ -1,6 +1,9 @@ -import SnapKit import UIKit +import DesignSystem + +import SnapKit + final class FilterChipsView: UIView { // MARK: - Components var onRemoveChip: ((String) -> Void)? diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index b6f8989a..dd32b8a4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -3,6 +3,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import NMapsMap import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift index be525bb3..6461df04 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapMarker.swift @@ -1,6 +1,9 @@ +import UIKit + +import DesignSystem + import NMapsMap import SnapKit -import UIKit final class MapMarker: UIView { // MARK: - Components diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 39e20a31..13533caf 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -3,6 +3,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import FloatingPanel import NMapsMap diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift index 7b12cfb5..bdfb71b8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/StoreListView/StoreListCell.swift @@ -1,7 +1,10 @@ +import UIKit + +import DesignSystem + import ReactorKit import RxSwift import SnapKit -import UIKit final class StoreListCell: UICollectionViewCell { static let identifier = "StoreListCell" diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift index ac172875..4c7cecc7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageController.swift @@ -1,12 +1,7 @@ -// -// BlockUserManageController.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index 7d8bc987..797d71e4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -1,6 +1,7 @@ import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift index 43dd9f31..f5ceb6a5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSection.swift @@ -1,12 +1,7 @@ -// -// BlockUserListSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import RxSwift struct BlockUserListSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift index dc08a239..947a3d99 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserListSection/BlockUserListSectionCell.swift @@ -1,12 +1,7 @@ -// -// BlockUserListSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift index 4fb424f1..97277402 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/View/BlockUserManageView.swift @@ -1,12 +1,7 @@ -// -// BlockUserManageView.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import SnapKit final class BlockUserManageView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift index 55e98c2b..03425945 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkController.swift @@ -1,12 +1,7 @@ -// -// MyPageBookmarkController.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index fd82c499..5a284efc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift index 1bf108c0..8bec6e51 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift @@ -1,12 +1,7 @@ -// -// MyPageBookmarkView.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyPageBookmarkView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift index 7d2168e8..b566d1f0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/CountButtonView.swift @@ -1,12 +1,7 @@ -// -// CountButtonView.swift -// Poppool -// -// Created by SeoJunYoung on 1/16/25. -// - import UIKit +import DesignSystem + import SnapKit final class CountButtonView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift index bb6347c8..77da9b6e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSection.swift @@ -1,12 +1,7 @@ -// -// PopUpCardSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import RxSwift struct PopUpCardSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift index 33ac884c..7409806f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardSectionCell.swift @@ -1,12 +1,7 @@ -// -// PopUpCardSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift index c24d05f2..4fad8477 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/View/PopUpCardSection/PopUpCardView.swift @@ -1,12 +1,7 @@ -// -// PopUpCardView.swift -// Poppool -// -// Created by SeoJunYoung on 1/22/25. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift index 67a61a01..73cf0f6f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalController.swift @@ -1,12 +1,7 @@ -// -// BookMarkPopUpViewTypeModalController.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift index 8d879bdd..e2298095 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalReactor.swift @@ -1,9 +1,4 @@ -// -// BookMarkPopUpViewTypeModalReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift index f57b0988..f2542b86 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/ViewTypeModal/BookMarkPopUpViewTypeModalView.swift @@ -1,12 +1,7 @@ -// -// BookMarkPopUpViewTypeModalView.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import SnapKit final class BookMarkPopUpViewTypeModalView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift index 3057e6d8..8a9620f8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQController.swift @@ -1,12 +1,7 @@ -// -// FAQController.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift index ba9947a4..f1d6158f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/FAQReactor.swift @@ -1,12 +1,7 @@ -// -// FAQReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift index 2ba04ea5..8e160d4e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSection.swift @@ -1,12 +1,7 @@ -// -// FAQDropdownSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import RxSwift struct FAQDropdownSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift index 36493d0c..06543581 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQDropdownSection/FAQDropdownSectionCell.swift @@ -1,12 +1,7 @@ -// -// FAQDropdownSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift index 77680e11..bec56ebf 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/FAQ/View/FAQView.swift @@ -1,12 +1,7 @@ -// -// FAQView.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import SnapKit final class FAQView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift index e699c2d3..a555a37c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageController.swift @@ -1,12 +1,7 @@ -// -// MyPageController.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index 26fe5b7b..ee231366 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 DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift index f92f51fc..8a85d6c5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSection.swift @@ -1,12 +1,7 @@ -// -// MyPageCommentSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift struct MyPageCommentSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift index 1415b4f1..f97564cb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageCommentSection/MyPageCommentSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyPageCommentSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift index 441344c6..12063019 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSection.swift @@ -1,12 +1,7 @@ -// -// MyPageListSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift struct MyPageListSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift index 0cd90311..f04cd680 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageListSection/MyPageListSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyPageListSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift index 89795be7..39ca5dfb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSection.swift @@ -1,12 +1,7 @@ -// -// MyPageLogoutSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/4/25. -// - import UIKit +import DesignSystem + import RxSwift struct MyPageLogoutSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift index 3c737be7..ffffb3fc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageLogoutSection/MyPageLogoutSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyPageLogoutSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/4/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift index 20d2a2b2..8d198680 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSection.swift @@ -1,12 +1,7 @@ -// -// MyPageMyCommentTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift struct MyPageMyCommentTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift index db582281..e38af1f4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyPageMyCommentTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/2/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift index 430fae01..36598dff 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSection.swift @@ -1,12 +1,7 @@ -// -// MyPageProfileSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/31/24. -// - import UIKit +import DesignSystem + import RxSwift struct MyPageProfileSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift index 639252c2..33b14aa4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageProfileSection/MyPageProfileSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyPageProfileSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/31/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift index 832979d9..88645b9a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageView.swift @@ -1,12 +1,7 @@ -// -// MyPageView.swift -// Poppool -// -// Created by SeoJunYoung on 12/30/24. -// - import UIKit +import DesignSystem + import SnapKit final class MyPageView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift index 1673c99e..2dd1b715 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentController.swift @@ -1,12 +1,7 @@ -// -// MyCommentController.swift -// Poppool -// -// Created by SeoJunYoung on 1/8/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index 2b4911db..efd34009 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift index bbe9f9af..1972644e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSection.swift @@ -1,12 +1,7 @@ -// -// ListCountButtonSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import RxSwift struct ListCountButtonSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift index 35d9658e..2f31bed1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/ListCountButtonSection/ListCountButtonSectionCell.swift @@ -1,12 +1,7 @@ -// -// ListCountButtonSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift index f1cf8955..31a5aec7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentView.swift @@ -1,12 +1,7 @@ -// -// MyCommentView.swift -// Poppool -// -// Created by SeoJunYoung on 1/8/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyCommentView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift index 96612580..4e811a51 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSection.swift @@ -1,12 +1,7 @@ -// -// MyCommentedPopUpGridSection.swift -// Poppool -// -// Created by SeoJunYoung on 2/6/25. -// - import UIKit +import DesignSystem + import RxSwift struct MyCommentedPopUpGridSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift index f8dfa86a..19cd3a8f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/View/MyCommentedPopUpGridSection/MyCommentedPopUpGridSectionCell.swift @@ -1,12 +1,7 @@ -// -// MyCommentedPopUpGridSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 2/6/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift index 04030d12..02455b41 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalController.swift @@ -1,12 +1,7 @@ -// -// MyCommentSortedModalController.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift index 60106273..9e71d6b0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalReactor.swift @@ -1,9 +1,4 @@ -// -// MyCommentSortedModalReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift index f5bf99ee..d3e2af1d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/SortedModal/MyCommentSortedModalView.swift @@ -1,12 +1,7 @@ -// -// MyCommentSortedModalView.swift -// Poppool -// -// Created by SeoJunYoung on 1/12/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyCommentSortedModalView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift index 678153d9..b2324a50 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailController.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeDetailController.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift index cb16df03..f2aa7f83 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift @@ -1,4 +1,5 @@ import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift index 64125f43..145a3cf0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailView.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeDetailView.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyPageNoticeDetailView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift index 10a2ffd7..0fcce206 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeController.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeController.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index 8af96c6a..f9458b92 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -1,6 +1,7 @@ import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift index 874e228c..0880ac21 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/MyPageNoticeView.swift @@ -1,12 +1,7 @@ -// -// MyPageNoticeView.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyPageNoticeView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift index c1c6f4a9..e29ef831 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSection.swift @@ -1,12 +1,7 @@ -// -// NoticeListSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import RxSwift struct NoticeListSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift index 6708b6a8..134965d9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/View/NoticeListSection/NoticeListSectionCell.swift @@ -1,12 +1,7 @@ -// -// NoticeListSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/13/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift index 1ecd1e02..f0f36fb3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalController.swift @@ -1,12 +1,7 @@ -// -// CategoryEditModalController.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index e6ae9422..625fa98d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -1,13 +1,7 @@ -// -// CategoryEditModalReactor.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift index 537ab835..6e7bf640 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalView.swift @@ -1,12 +1,7 @@ -// -// CategoryEditModalView.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import UIKit +import DesignSystem + import SnapKit final class CategoryEditModalView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift index 92b2c880..b2a5a0a9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalController.swift @@ -1,12 +1,7 @@ -// -// InfoEditModalController.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift index 61d22876..7114c25d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift @@ -1,6 +1,7 @@ import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift index 71d05ee9..99298377 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalView.swift @@ -1,12 +1,7 @@ -// -// InfoEditModalView.swift -// Poppool -// -// Created by SeoJunYoung on 1/10/25. -// - import UIKit +import DesignSystem + import SnapKit final class InfoEditModalView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index 9dc41725..77434360 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -2,6 +2,7 @@ import PhotosUI import UIKit import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index 42702d27..dfc95e4e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -3,6 +3,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift index ea14a361..9b51c288 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift @@ -1,12 +1,7 @@ -// -// ProfileEditView.swift -// Poppool -// -// Created by SeoJunYoung on 1/4/25. -// - import UIKit +import DesignSystem + import SnapKit final class ProfileEditView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift index 12bb9237..561ece27 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentController.swift @@ -1,12 +1,7 @@ -// -// MyPageRecentController.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index 94b3f651..e31b64f3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift index d9f8fe3b..52d15d11 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/MyPageRecentView.swift @@ -1,12 +1,7 @@ -// -// MyPageRecentView.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import SnapKit final class MyPageRecentView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift index a744e56e..23f0db9e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/View/RecentPopUpSection/RecentPopUpSection.swift @@ -1,12 +1,7 @@ -// -// RecentPopUpSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/14/25. -// - import UIKit +import DesignSystem + import RxSwift struct RecentPopUpSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift index 2cd74d2e..4667c827 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsController.swift @@ -1,12 +1,7 @@ -// -// MyPageTermsController.swift -// Poppool -// -// Created by SeoJunYoung on 2/4/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift index 18bdf23e..a0230242 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Terms/MyPageTermsReactor.swift @@ -1,11 +1,7 @@ -// -// MyPageTermsReactor.swift -// Poppool -// -// Created by SeoJunYoung on 2/4/25. -// - import Foundation + +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift index c0d6bdd2..b08b5514 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalController.swift @@ -1,11 +1,7 @@ -// -// WithdrawlCheckModalController.swift -// Poppool -// -// Created by SeoJunYoung on 1/6/25. -// import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift index bf7e88f2..a073fca3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/CheckModal/WithdrawlCheckModalView.swift @@ -1,12 +1,7 @@ -// -// WithdrawlCheckModalView.swift -// Poppool -// -// Created by SeoJunYoung on 1/6/25. -// - import UIKit +import DesignSystem + import SnapKit final class WithdrawlCheckModalView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift index 8bc6c8c8..8fc0cb03 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteController.swift @@ -1,12 +1,7 @@ -// -// WithdrawlCompleteController.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift index 0fd5844a..87ec7dbe 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/Complete/WithdrawlCompleteView.swift @@ -1,12 +1,7 @@ -// -// WithdrawlCompleteView.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import SnapKit final class WithdrawlCompleteView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift index de4f14c0..7b1f5672 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSection.swift @@ -1,12 +1,7 @@ -// -// WithdrawlCheckSection.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import RxSwift struct WithdrawlCheckSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift index 8ef20cb3..6438f709 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlCheckSectionCell.swift @@ -1,12 +1,7 @@ -// -// WithdrawlCheckSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift index 250a933b..1f830fb4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/View/WithdrawlReasonView.swift @@ -1,12 +1,7 @@ -// -// WithdrawlReasonView.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import SnapKit final class WithdrawlReasonView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift index c3ecb0f2..e469604a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonController.swift @@ -1,12 +1,7 @@ -// -// WithdrawlReasonController.swift -// Poppool -// -// Created by SeoJunYoung on 1/7/25. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxGesture 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 8cb10ad6..64ee7025 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift index 92b01182..840bd835 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift @@ -1,12 +1,7 @@ -// -// SearchResultController.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift index cb8c25b1..e41dd648 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift index e3555ab5..267ac61e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift @@ -1,12 +1,7 @@ -// -// SearchResultCountSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DesignSystem + import RxSwift struct SearchResultCountSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift index d7c5d211..70a240b9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift @@ -1,12 +1,7 @@ -// -// SearchResultCountSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift index 43bbd685..4f1aae0b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift @@ -1,12 +1,7 @@ -// -// SearchResultView.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - import UIKit +import DesignSystem + import SnapKit final class SearchResultView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift index cc473a5c..0d4b80fe 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift @@ -1,12 +1,7 @@ -// -// SearchController.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxGesture diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 63c8179b..87cc6fc1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift index 7a26b80f..675b947e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift @@ -1,12 +1,7 @@ -// -// CancelableTagSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DesignSystem + import RxSwift struct CancelableTagSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift index c7773c88..0f6c98fd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift @@ -1,12 +1,7 @@ -// -// CancelableTagSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift index 41617011..e72a0dd7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift @@ -1,12 +1,7 @@ -// -// SearchCountTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/5/24. -// - import UIKit +import DesignSystem + import RxSwift struct SearchCountTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift index bc765fe4..da021fbc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// SearchCountTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/5/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift index 3731bbaa..d52d9334 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift @@ -1,12 +1,7 @@ -// -// SearchTitleSection.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DesignSystem + import RxSwift struct SearchTitleSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift index 63dad364..e18fc6fc 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift @@ -1,12 +1,7 @@ -// -// SearchTitleSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift index c7ab66a9..f39fe557 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift @@ -1,12 +1,7 @@ -// -// SearchCategoryController.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift index 267f5dcb..91646a85 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift @@ -1,6 +1,7 @@ import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift index f8a452ca..d6ac716e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift @@ -1,12 +1,7 @@ -// -// SearchCategoryView.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import UIKit +import DesignSystem + import SnapKit final class SearchCategoryView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift index 33d083b1..9b2af457 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import Pageboy import ReactorKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift index 8798ec65..16428a54 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import SnapKit import Then diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift index 4fb31f8a..2501bb22 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift @@ -1,12 +1,7 @@ -// -// SearchSortedController.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift index 12dfc2d5..e2bc6d42 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift @@ -1,9 +1,4 @@ -// -// SearchSortedReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift index 243560f1..01f5471e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift @@ -1,12 +1,7 @@ -// -// SearchSortedView.swift -// Poppool -// -// Created by SeoJunYoung on 12/6/24. -// - import UIKit +import DesignSystem + import SnapKit final class SearchSortedView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift index e72b9a4c..101a9a0c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift @@ -2,6 +2,7 @@ import UIKit import DomainInterface import Infrastructure +import DesignSystem import Pageboy import ReactorKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index c4f3db3a..0838183d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -1,5 +1,6 @@ import DomainInterface import Infrastructure +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift index a4928637..ffe88df1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/View/SignUpMainView.swift @@ -1,12 +1,7 @@ -// -// SignUpMainView.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpMainView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift index 7556c572..4b2e36a8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift @@ -1,12 +1,7 @@ -// -// SignUpCompleteController.swift -// Poppool -// -// Created by SeoJunYoung on 11/27/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift index 9269d52a..d26997ba 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteReactor.swift @@ -1,9 +1,4 @@ -// -// SignUpCompleteReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/27/24. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift index b0ee7a2a..b89f2ffa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteView.swift @@ -1,12 +1,7 @@ -// -// SignUpCompleteView.swift -// Poppool -// -// Created by SeoJunYoung on 11/27/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpCompleteView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift index 1fd6c671..ad6ab703 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Controller.swift @@ -1,12 +1,7 @@ -// -// SignUpStep1Controller.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift index b4f838db..788b5e0b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/SignUpStep1Reactor.swift @@ -1,11 +1,7 @@ -// -// SignUpStep1Reactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import Foundation + +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift index 3a5e5a4b..064b423f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpCheckBoxButton.swift @@ -1,12 +1,7 @@ -// -// SignUpCheckBoxButton.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift index 5bbab051..c7b46b7e 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpStep1View.swift @@ -1,12 +1,7 @@ -// -// SignUpStep1View.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpStep1View: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift index cdfd3a56..03a16047 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step1/View/SignUpTermsView.swift @@ -1,12 +1,7 @@ -// -// SignUpTermsView.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift index 42293868..8aefa684 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2Controller.swift @@ -1,12 +1,7 @@ -// -// SignUpStep2Controller.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxGesture diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift index 984e0d56..3654affd 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift @@ -1,12 +1,7 @@ -// -// SignUpStep2View.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpStep2View: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift index 4b85d7c3..246c49f3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Controller.swift @@ -1,12 +1,7 @@ -// -// SignUpStep3Controller.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxGesture diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 078e6d52..6f8b11e1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -1,6 +1,7 @@ import UIKit import DomainInterface +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift index 04c18968..5388a368 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/SignUpStep3View.swift @@ -1,12 +1,7 @@ -// -// SignUpStep3View.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpStep3View: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift index 2f508831..59afe696 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSection.swift @@ -1,12 +1,7 @@ -// -// TagSection.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import RxSwift struct TagSection: Sectionable { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift index 28e1b592..ea33ac45 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift @@ -1,12 +1,7 @@ -// -// TagSectionCell.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift index 2a2d3d8b..4024ecea 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedController.swift @@ -1,12 +1,7 @@ -// -// AgeSelectedController.swift -// Poppool -// -// Created by SeoJunYoung on 11/26/24. -// - import UIKit +import DesignSystem + import PanModal import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift index 08a1c326..907e51d1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedReactor.swift @@ -1,9 +1,4 @@ -// -// AgeSelectedReactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/26/24. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift index 6322c58c..fbb5cabe 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/AgeSelectedModal/AgeSelectedView.swift @@ -1,12 +1,7 @@ -// -// AgeSelectedView.swift -// Poppool -// -// Created by SeoJunYoung on 11/26/24. -// - import UIKit +import DesignSystem + import SnapKit final class AgeSelectedView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift index 772bf195..9294c172 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Controller.swift @@ -1,12 +1,7 @@ -// -// SignUpStep4Controller.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import ReactorKit import RxCocoa import RxSwift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift index 02d72a7f..8f848425 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/SignUpStep4Reactor.swift @@ -1,9 +1,4 @@ -// -// SignUpStep4Reactor.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// +import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift index 43774bdf..ac5bf151 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/AgeSelectedButton.swift @@ -1,12 +1,7 @@ -// -// AgeSelectedButton.swift -// Poppool -// -// Created by SeoJunYoung on 11/26/24. -// - import UIKit +import DesignSystem + import SnapKit final class AgeSelectedButton: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift index 52453a36..a7d8df1d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step4/Main/View/SignUpStep4View.swift @@ -1,12 +1,7 @@ -// -// SignUpStep4View.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class SignUpStep4View: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift index 79f1c5f2..818b7eea 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailController.swift @@ -1,12 +1,7 @@ -// -// TermsDetailController.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import RxCocoa import RxSwift import SnapKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift index 6c5b7d7c..75772762 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/TermsDetail/TermsDetailView.swift @@ -1,12 +1,7 @@ -// -// TermsDetailView.swift -// Poppool -// -// Created by SeoJunYoung on 11/25/24. -// - import UIKit +import DesignSystem + import SnapKit final class TermsDetailView: UIView { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift index 4d688b1a..006b1446 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Splash/SplashController.swift @@ -1,5 +1,6 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift deleted file mode 100644 index 4bfbc829..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/Interfaces/InOutputable.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// InOutputable.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/29/24. -// - -import UIKit - -protocol InOutputable: Inputable, Outputable { } - -protocol Inputable { - associatedtype Input - func injection(with input: Input) -} - -protocol Outputable { - associatedtype Output -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift index 27f6d3df..114aa5a1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Utills/ToastMaker/BookMarkToastView.swift @@ -1,12 +1,7 @@ -// -// BookMarkToastView.swift -// Poppool -// -// Created by SeoJunYoung on 1/21/25. -// - import UIKit +import DesignSystem + import SnapKit final class BookMarkToastView: UIView { From 55e2f4181984e625bd53a40f7883bb60784da75a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 11:12:20 +0900 Subject: [PATCH 216/393] =?UTF-8?q?feat/#131:=20SearchFeature=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 별도의 DemoApp까지 추가 --- .../contents.xcworkspacedata | 7 +- .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../SearchFeature.xcodeproj/project.pbxproj | 558 ++++++++++++++++++ .../xcschemes/SearchFeatureDemo.xcscheme | 78 +++ .../Source/View/SearchViewController.swift | 9 + .../SearchFeatureDemo/App/AppDelegate.swift | 16 + .../SearchFeatureDemo/App/SceneDelegate.swift | 17 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 35 ++ .../Resource/Assets.xcassets/Contents.json | 6 + .../Base.lproj/LaunchScreen.storyboard | 25 + .../SearchFeatureDemo/Resource/Info.plist | 23 + 12 files changed, 791 insertions(+), 2 deletions(-) create mode 100644 Poppool/Poppool.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/xcshareddata/xcschemes/SearchFeatureDemo.xcscheme create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata index 11addf63..67537ffe 100644 --- a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -22,10 +22,13 @@ location = "group:PresentationLayer" name = "PresentationLayer"> + location = "group:Presentation/Presentation.xcodeproj"> + location = "group:SearchFeature/SearchFeature.xcodeproj"> + + + + + + IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj new file mode 100644 index 00000000..78a4d726 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -0,0 +1,558 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; + 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 0516364D2DC45E6300A6C0D1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 051633642DC457A900A6C0D1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 0516336C2DC457A900A6C0D1; + remoteInfo = SearchFeature; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 0516364F2DC45E6400A6C0D1 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ + 051636402DC457DF00A6C0D1 /* Exceptions for "SearchFeatureDemo" folder in "SearchFeatureDemo" target */ = { + isa = PBXFileSystemSynchronizedBuildFileExceptionSet; + membershipExceptions = ( + Resource/Info.plist, + ); + target = 0516362E2DC457DE00A6C0D1 /* SearchFeatureDemo */; + }; +/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 0516336F2DC457A900A6C0D1 /* SearchFeature */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = SearchFeature; + sourceTree = ""; + }; + 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */ = { + isa = PBXFileSystemSynchronizedRootGroup; + exceptions = ( + 051636402DC457DF00A6C0D1 /* Exceptions for "SearchFeatureDemo" folder in "SearchFeatureDemo" target */, + ); + path = SearchFeatureDemo; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 0516336A2DC457A900A6C0D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0516362C2DC457DE00A6C0D1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 051633632DC457A900A6C0D1 = { + isa = PBXGroup; + children = ( + 0516336F2DC457A900A6C0D1 /* SearchFeature */, + 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */, + 0516364A2DC45E6300A6C0D1 /* Frameworks */, + 0516336E2DC457A900A6C0D1 /* Products */, + ); + sourceTree = ""; + }; + 0516336E2DC457A900A6C0D1 /* Products */ = { + isa = PBXGroup; + children = ( + 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */, + 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */, + ); + name = Products; + sourceTree = ""; + }; + 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 051633682DC457A900A6C0D1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 0516336C2DC457A900A6C0D1 /* SearchFeature */ = { + isa = PBXNativeTarget; + buildConfigurationList = 051633742DC457A900A6C0D1 /* Build configuration list for PBXNativeTarget "SearchFeature" */; + buildPhases = ( + 051633682DC457A900A6C0D1 /* Headers */, + 051633692DC457A900A6C0D1 /* Sources */, + 0516336A2DC457A900A6C0D1 /* Frameworks */, + 0516336B2DC457A900A6C0D1 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 0516336F2DC457A900A6C0D1 /* SearchFeature */, + ); + name = SearchFeature; + packageProductDependencies = ( + ); + productName = SearchFeature; + productReference = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; + productType = "com.apple.product-type.framework"; + }; + 0516362E2DC457DE00A6C0D1 /* SearchFeatureDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 051636412DC457DF00A6C0D1 /* Build configuration list for PBXNativeTarget "SearchFeatureDemo" */; + buildPhases = ( + 0516362B2DC457DE00A6C0D1 /* Sources */, + 0516362C2DC457DE00A6C0D1 /* Frameworks */, + 0516362D2DC457DE00A6C0D1 /* Resources */, + 0516364F2DC45E6400A6C0D1 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 0516364E2DC45E6300A6C0D1 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */, + ); + name = SearchFeatureDemo; + packageProductDependencies = ( + ); + productName = SearchFeatureDemo; + productReference = 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 051633642DC457A900A6C0D1 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 0516336C2DC457A900A6C0D1 = { + CreatedOnToolsVersion = 16.3; + LastSwiftMigration = 1630; + }; + 0516362E2DC457DE00A6C0D1 = { + CreatedOnToolsVersion = 16.3; + }; + }; + }; + buildConfigurationList = 051633672DC457A900A6C0D1 /* Build configuration list for PBXProject "SearchFeature" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 051633632DC457A900A6C0D1; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 0516336E2DC457A900A6C0D1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 0516336C2DC457A900A6C0D1 /* SearchFeature */, + 0516362E2DC457DE00A6C0D1 /* SearchFeatureDemo */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 0516336B2DC457A900A6C0D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0516362D2DC457DE00A6C0D1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 051633692DC457A900A6C0D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 0516362B2DC457DE00A6C0D1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 0516364E2DC45E6300A6C0D1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 0516336C2DC457A900A6C0D1 /* SearchFeature */; + targetProxy = 0516364D2DC45E6300A6C0D1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 051633722DC457A900A6C0D1 /* 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.4; + 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; + }; + 051633732DC457A900A6C0D1 /* 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.4; + 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; + }; + 051633752DC457A900A6C0D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + 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.SearchFeature; + 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_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 051633762DC457A900A6C0D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + 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.SearchFeature; + 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; + }; + 051636422DC457DF00A6C0D1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SearchFeatureDemo/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.SearchFeatureDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + 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; + }; + 051636432DC457DF00A6C0D1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = SearchFeatureDemo/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.SearchFeatureDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + 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 */ + 051633672DC457A900A6C0D1 /* Build configuration list for PBXProject "SearchFeature" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 051633722DC457A900A6C0D1 /* Debug */, + 051633732DC457A900A6C0D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 051633742DC457A900A6C0D1 /* Build configuration list for PBXNativeTarget "SearchFeature" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 051633752DC457A900A6C0D1 /* Debug */, + 051633762DC457A900A6C0D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 051636412DC457DF00A6C0D1 /* Build configuration list for PBXNativeTarget "SearchFeatureDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 051636422DC457DF00A6C0D1 /* Debug */, + 051636432DC457DF00A6C0D1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 051633642DC457A900A6C0D1 /* Project object */; +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/xcshareddata/xcschemes/SearchFeatureDemo.xcscheme b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/xcshareddata/xcschemes/SearchFeatureDemo.xcscheme new file mode 100644 index 00000000..66400cdd --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/xcshareddata/xcschemes/SearchFeatureDemo.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift new file mode 100644 index 00000000..cce02dff --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift @@ -0,0 +1,9 @@ +import UIKit + +public class SearchViewController: UIViewController { + + public override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .red + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift new file mode 100644 index 00000000..0f2b4b09 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -0,0 +1,16 @@ +import UIKit + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } +} + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift new file mode 100644 index 00000000..d00de8f3 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -0,0 +1,17 @@ +import UIKit + +import SearchFeature + +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) + window?.rootViewController = SearchViewController() + window?.makeKeyAndVisible() + } +} + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..23058801 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/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/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist new file mode 100644 index 00000000..0eb786dc --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist @@ -0,0 +1,23 @@ + + + + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + + From 90ea3a64e4789ab70e8a63225025b64a7da05549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Thu, 1 May 2025 19:05:58 +0900 Subject: [PATCH 217/393] =?UTF-8?q?fix/#129:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=ED=95=A9=EC=B3=90=EC=A7=80=EB=8D=98=20?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FillterSheetView/FilterBottomSheetView.swift | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift index b7414a4b..0ecff3df 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift @@ -42,13 +42,13 @@ final class FilterBottomSheetView: UIView { let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), - heightDimension: .absolute(36) + heightDimension: .estimated(36) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(36) + heightDimension: .estimated(36) ) let group = NSCollectionLayoutGroup.horizontal( layoutSize: groupSize, @@ -391,16 +391,22 @@ extension FilterBottomSheetView { var filters: [String] = [] if let locationText = locationText, !locationText.isEmpty { - filters.append(locationText) + let locations = locationText + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } + filters += locations } + if let categoryText = categoryText, !categoryText.isEmpty { - filters.append(categoryText) + let categories = categoryText + .split(separator: ",") + .map { $0.trimmingCharacters(in: .whitespaces) } + filters += categories } filterChipsView.updateChips(with: filters) } } - extension FilterBottomSheetView: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { updateSelectedButtonPosition() From 52dce128acde363abbd4d6af5184de6f4ed0de64 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 17:32:26 +0900 Subject: [PATCH 218/393] =?UTF-8?q?feat/#131:=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EB=B0=94=20View=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/PPSearchBarView.swift | 72 +++++++++++++++++++ .../Assets.xcassets/SearchBar/Contents.json | 6 ++ .../icon_clear_button.imageset/Contents.json | 12 ++++ .../icon_clear_button.imageset/solid.svg | 5 ++ .../icon_search_gray.imageset/Contents.json | 12 ++++ .../icon_search_gray.imageset/line.svg | 4 ++ 6 files changed, 111 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/solid.svg create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/line.svg diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift new file mode 100644 index 00000000..b842c9be --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift @@ -0,0 +1,72 @@ +import UIKit + +import DesignSystem + +import SnapKit +import Then + +public class PPSearchBarView: UIView { + + private let stackView = UIStackView().then { + $0.spacing = 16 + $0.alignment = .center + $0.axis = .horizontal + } + + let searchBar = UISearchBar().then { + $0.placeholder = "팝업스토어명을 입력해보세요" + $0.tintColor = .g400 + $0.backgroundColor = .g50 + $0.setImage(UIImage(named: "icon_search_gray"), for: .search, state: .normal) + $0.setImage(UIImage(named: "icon_clear_button"), for: .clear, state: .normal) + $0.searchBarStyle = .minimal + if let searchBarTextFieldBackgroundView = $0.searchTextField.subviews.first { + searchBarTextFieldBackgroundView.isHidden = true + } + } + + let cancelButton = UIButton(type: .system).then { + $0.setTitle("취소", for: .normal) + $0.tintColor = .g1000 + $0.titleLabel?.font = .korFont(style: .regular, size: 14) + } + + public init() { + super.init(frame: .zero) + + self.addViews() + self.setupConstraints() + } + + @available(*, unavailable) + public required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +private extension PPSearchBarView { + func addViews() { + [stackView].forEach { + self.addSubview($0) + } + + [searchBar, cancelButton].forEach { + self.stackView.addArrangedSubview($0) + } + } + + func setupConstraints() { + stackView.snp.makeConstraints { make in + make.edges.equalToSuperview() + make.height.equalTo(37) + } + + searchBar.snp.makeConstraints { make in + make.height.equalToSuperview() + } + + cancelButton.snp.makeConstraints { make in + make.height.equalToSuperview() + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/Contents.json new file mode 100644 index 00000000..f64986bb --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "solid.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/solid.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/solid.svg new file mode 100644 index 00000000..71a10b2e --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_clear_button.imageset/solid.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/Contents.json new file mode 100644 index 00000000..d86eb988 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "line.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/line.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/line.svg new file mode 100644 index 00000000..6dd06be5 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/SearchBar/icon_search_gray.imageset/line.svg @@ -0,0 +1,4 @@ + + + + From caa58933c0bdccd6f654b5b34b9fe933889c58e3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 17:33:01 +0900 Subject: [PATCH 219/393] =?UTF-8?q?chore/#131:=20=EA=B4=80=EB=A0=A8=20?= =?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EB=B0=8F=20=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=9E=84=EC=9B=8C=ED=81=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 111 +++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 78a4d726..d1fe6f50 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -9,6 +9,15 @@ /* Begin PBXBuildFile section */ 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233B2DC49A7600C761A5 /* RxCocoa */; }; + 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233D2DC49A7600C761A5 /* RxSwift */; }; + 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC23402DC49A8B00C761A5 /* ReactorKit */; }; + 05EC23432DC49AA200C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; + 05EC23442DC49AA200C761A5 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23462DC49AA800C761A5 /* DomainInterface.framework */; }; + 05EC23482DC49AA800C761A5 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23462DC49AA800C761A5 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC234B2DC49AB400C761A5 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234A2DC49AB400C761A5 /* Then */; }; + 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234D2DC49AC100C761A5 /* SnapKit */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -33,11 +42,25 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + 05EC23452DC49AA200C761A5 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05EC23482DC49AA800C761A5 /* DomainInterface.framework in Embed Frameworks */, + 05EC23442DC49AA200C761A5 /* DesignSystem.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC23462DC49AA800C761A5 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -71,6 +94,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, + 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, + 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, + 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, + 05EC23432DC49AA200C761A5 /* DesignSystem.framework in Frameworks */, + 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, + 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -107,6 +137,8 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 05EC23462DC49AA800C761A5 /* DomainInterface.framework */, + 05EC23422DC49AA200C761A5 /* DesignSystem.framework */, ); name = Frameworks; sourceTree = ""; @@ -132,6 +164,7 @@ 051633692DC457A900A6C0D1 /* Sources */, 0516336A2DC457A900A6C0D1 /* Frameworks */, 0516336B2DC457A900A6C0D1 /* Resources */, + 05EC23452DC49AA200C761A5 /* Embed Frameworks */, ); buildRules = ( ); @@ -142,6 +175,11 @@ ); name = SearchFeature; packageProductDependencies = ( + 05EC233B2DC49A7600C761A5 /* RxCocoa */, + 05EC233D2DC49A7600C761A5 /* RxSwift */, + 05EC23402DC49A8B00C761A5 /* ReactorKit */, + 05EC234A2DC49AB400C761A5 /* Then */, + 05EC234D2DC49AC100C761A5 /* SnapKit */, ); productName = SearchFeature; productReference = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; @@ -199,6 +237,12 @@ ); mainGroup = 051633632DC457A900A6C0D1; minimizedProjectReferenceProxies = 1; + packageReferences = ( + 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */, + 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */, + 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */, + 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */, + ); preferredProjectObjectVersion = 77; productRefGroup = 0516336E2DC457A900A6C0D1 /* Products */; projectDirPath = ""; @@ -381,7 +425,7 @@ 051633752DC457A900A6C0D1 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -421,7 +465,7 @@ 051633762DC457A900A6C0D1 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; @@ -553,6 +597,69 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactiveX/RxSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.9.0; + }; + }; + 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactorKit/ReactorKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; + 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SnapKit/SnapKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.7.1; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 05EC233B2DC49A7600C761A5 /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 05EC233D2DC49A7600C761A5 /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 05EC23402DC49A8B00C761A5 /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 05EC234A2DC49AB400C761A5 /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; + 05EC234D2DC49AC100C761A5 /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 051633642DC457A900A6C0D1 /* Project object */; } From 38fa728a3d0cc3a42646050157573878e68c8860 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 17:33:23 +0900 Subject: [PATCH 220/393] =?UTF-8?q?feat/#131:=20ViewController,=20Reactor?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=B4=88=EA=B8=B0=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 36 +++++++++++ .../View/PopupSearchViewController.swift | 59 +++++++++++++++++++ .../Source/View/SearchViewController.swift | 9 --- .../SearchFeatureDemo/App/SceneDelegate.swift | 2 +- 4 files changed, 96 insertions(+), 10 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift delete mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift new file mode 100644 index 00000000..c2396caf --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -0,0 +1,36 @@ +import ReactorKit +import RxSwift +import RxCocoa + +public final class PopupSearchReactor: Reactor { + + // MARK: - Reactor + public enum Action { } + + public enum Mutation { + } + + public struct State { } + + // MARK: - properties + public var initialState: State + var disposeBag = DisposeBag() + + // MARK: - init + public init() { + self.initialState = State() + } + + // MARK: - Reactor Methods + public func mutate(action: Action) -> Observable { + switch action { } + } + + public func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { } + + return newState + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift new file mode 100644 index 00000000..1ee3f819 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -0,0 +1,59 @@ +import UIKit + +import DesignSystem + +import ReactorKit +import RxSwift + +public final class PopupSearchViewController: BaseViewController, View { + + public typealias Reactor = PopupSearchReactor + + // MARK: - Properties + public var disposeBag = DisposeBag() + + private let searchBar = PPSearchBarView() +} + +// MARK: - Life Cycle +extension PopupSearchViewController { + + public override func viewDidLoad() { + super.viewDidLoad() + + self.addViews() + self.setupConstraints() + self.configureUI() + } +} + +// MARK: - SetUp +private extension PopupSearchViewController { + func addViews() { + self.view.addSubview(searchBar) + } + + func setupConstraints() { + searchBar.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(20) + make.trailing.equalToSuperview().inset(16) + make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(12) + } + } + + func configureUI() { } +} + +// MARK: - Bind +extension PopupSearchViewController { + public func bind(reactor: Reactor) { + searchBar.cancelButton.rx.tap + .withUnretained(self) + .subscribe { (owner, _) in + owner.navigationController?.popViewController(animated: true) + } + .disposed(by: disposeBag) + } +} + +extension PopupSearchViewController: UISearchBarDelegate { } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift deleted file mode 100644 index cce02dff..00000000 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchViewController.swift +++ /dev/null @@ -1,9 +0,0 @@ -import UIKit - -public class SearchViewController: UIViewController { - - public override func viewDidLoad() { - super.viewDidLoad() - view.backgroundColor = .red - } -} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index d00de8f3..c75dd504 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -10,7 +10,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = SearchViewController() + window?.rootViewController = PopupSearchViewController() window?.makeKeyAndVisible() } } From 94f41887902f90cda1c75e466cfdc1e93d941040 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 22:50:41 +0900 Subject: [PATCH 221/393] =?UTF-8?q?chore/#131:=20SearchFeatureDemo?= =?UTF-8?q?=EC=9A=A9=20Provisioning=20=EB=93=B1=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/SearchFeature.xcodeproj/project.pbxproj | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index d1fe6f50..bf59747b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -507,9 +507,11 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_IDENTITY = "Apple Development"; - 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*]" = W5QTRMS954; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -525,6 +527,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolSearchFeatureDemoProvisioning; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From cbb41a00cd2cf0b4b02a1fe98162ac902677822e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 23:11:54 +0900 Subject: [PATCH 222/393] =?UTF-8?q?feat/#131:=20=EB=B0=98=EB=B3=B5=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EB=90=A0=20=ED=8C=9D=EC=97=85=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EB=93=9C=20=EB=B7=B0=20compositionalLayout=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PPPopupGridCollectionView.swift | 73 +++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift new file mode 100644 index 00000000..3bda6bdc --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift @@ -0,0 +1,73 @@ +import UIKit + +import SnapKit + +public final class PPPopupGridCollecView: UICollectionView { + // MARK: - Properties + + // MARK: - init + public init() { + super.init(frame: .zero, collectionViewLayout: Self.makeLayout()) + + self.addViews() + self.setupConstraints() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension PPPopupGridCollecView { + func addViews() { } + + func setupConstraints() { } + + func configureUI() { } +} + +// MARK: - Layout +private extension PPPopupGridCollecView { + static func makeLayout() -> UICollectionViewCompositionalLayout { + return UICollectionViewCompositionalLayout { _, _ in + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(0.5), + heightDimension: .absolute(249) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(249) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item, item] + ) + group.interItemSpacing = .fixed(16) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 24 + + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(22) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: Self.elementKindSectionHeader, + alignment: .top + ) + section.boundarySupplementaryItems = [header] + + return section + } + } +} From a23bbf79b639549afec6b92fbb28b4d6ad7d1355 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 23:28:29 +0900 Subject: [PATCH 223/393] =?UTF-8?q?feat/#131:=20PopupCollectionViewCell=20?= =?UTF-8?q?=EC=9D=B4=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PPPopupGridCollectionViewCell.swift | 161 ++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift new file mode 100644 index 00000000..3fde47d3 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift @@ -0,0 +1,161 @@ +import UIKit + +import DesignSystem + +import RxSwift +import SnapKit + +final class PPPopupGridCollectionViewCell: UICollectionViewCell { + + // MARK: - Properties + + var disposeBag = DisposeBag() + + private let imageView = UIImageView().then { + $0.contentMode = .scaleAspectFill + } + + private let categoryLabel = PPLabel(style: .bold, fontSize: 11).then { + $0.textColor = .blu500 + } + + private let titleLabel = PPLabel(style: .bold, fontSize: 14).then { + $0.numberOfLines = 2 + $0.lineBreakMode = .byTruncatingTail + } + + private let addressLabel = PPLabel(style: .medium, fontSize: 11).then { + $0.numberOfLines = 1 + $0.lineBreakMode = .byTruncatingTail + $0.textColor = .g400 + } + + private let dateLabel = PPLabel(style: .medium, fontSize: 11).then { + $0.lineBreakMode = .byTruncatingTail + $0.textColor = .g400 + } + + let bookmarkButton = UIButton() + + private let rankLabel = UILabel().then { + $0.backgroundColor = .w10 + $0.layer.cornerRadius = 12 + $0.clipsToBounds = true + $0.isHidden = true + $0.textColor = .w100 + } + + // MARK: - init + override init(frame: CGRect) { + super.init(frame: frame) + + self.addViews() + self.setupConstraints() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension PPPopupGridCollectionViewCell { + func addViews() { + [imageView, categoryLabel, titleLabel, dateLabel, addressLabel, bookmarkButton].forEach { + self.contentView.addSubview($0) + } + + [rankLabel].forEach { + imageView.addSubview($0) + } + } + + func setupConstraints() { + imageView.snp.makeConstraints { make in + make.width.equalTo(contentView.bounds.width) + make.height.equalTo(140) + make.top.equalToSuperview() + make.centerX.equalToSuperview() + } + + categoryLabel.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.top.equalTo(imageView.snp.bottom).offset(12) + make.height.equalTo(15) + } + + titleLabel.snp.makeConstraints { make in + make.top.equalTo(categoryLabel.snp.bottom).offset(4) + make.leading.trailing.equalToSuperview() + } + + dateLabel.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.height.equalTo(15).priority(.high) + make.bottom.equalToSuperview() + } + + addressLabel.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview() + make.bottom.equalTo(dateLabel.snp.top) + make.height.equalTo(17).priority(.high) + } + + bookmarkButton.snp.makeConstraints { make in + make.size.equalTo(24) + make.top.trailing.equalToSuperview().inset(8) + } + + rankLabel.snp.makeConstraints { make in + make.height.equalTo(24) + make.width.equalTo(37) + make.leading.bottom.equalToSuperview().inset(12) + } + } + + func configureUI() { + self.contentView.layer.cornerRadius = 4 + self.contentView.clipsToBounds = true + + imageView.layer.cornerRadius = 4 + imageView.clipsToBounds = true + } +} + +extension PPPopupGridCollectionViewCell: Inputable { + struct Input { + var imagePath: String? + var id: Int64 + var category: String? + var title: String? + var address: String? + var startDate: String? + var endDate: String? + var isBookmark: Bool + var isLogin: Bool + var isPopular: Bool = false + var row: Int? + } + + func injection(with input: Input) { + categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .korFont(style: .bold, size: 11)) + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 14)) + addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .medium, size: 11)) + let date = input.startDate.toDate().toPPDateString() + " ~ " + input.endDate.toDate().toPPDateString() + dateLabel.setLineHeightText(text: date, font: .korFont(style: .medium, size: 11)) + let bookmarkImage = input.isBookmark ? UIImage(named: "icon_bookmark_fill") : UIImage(named: "icon_bookmark") + bookmarkButton.setImage(bookmarkImage, for: .normal) + imageView.setPPImage(path: input.imagePath) + bookmarkButton.isHidden = !input.isLogin + + rankLabel.isHidden = !input.isPopular + let rank = input.row ?? 0 + rankLabel.setLineHeightText(text: "\(rank + 1)위", font: .korFont(style: .medium, size: 11), lineHeight: 1) + rankLabel.textAlignment = .center + if rank > 2 { + rankLabel.isHidden = true + } + } +} + From 7d3ca881a549037350ee3ce8a9976de9276d2433 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 23:34:42 +0900 Subject: [PATCH 224/393] =?UTF-8?q?add/#131:=20=ED=95=84=EC=9A=94=ED=95=9C?= =?UTF-8?q?=20=EB=94=94=EC=9E=90=EC=9D=B8=20=ED=8C=8C=EC=9D=BC=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_bookmark.imageset/Contents.json | 12 ++++++++++++ .../icon_bookmark.imageset/solid.svg | 17 +++++++++++++++++ .../icon_bookmark_fill.imageset/Contents.json | 12 ++++++++++++ .../icon_bookmark_fill.imageset/solid-1.svg | 17 +++++++++++++++++ 4 files changed, 58 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/solid.svg create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/solid-1.svg diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/Contents.json new file mode 100644 index 00000000..f64986bb --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "solid.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/solid.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/solid.svg new file mode 100644 index 00000000..1ef76aed --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark.imageset/solid.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/Contents.json new file mode 100644 index 00000000..44808aa2 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "solid-1.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/solid-1.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/solid-1.svg new file mode 100644 index 00000000..6d836093 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_bookmark_fill.imageset/solid-1.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From e4a86521709ca367777899e7d8f6e4840c5c7f0a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 2 May 2025 23:38:46 +0900 Subject: [PATCH 225/393] =?UTF-8?q?fix/#131:=20HeaderSection=20layout=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 --- .../Source/View/PPPopupGridCollectionView.swift | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift index 3bda6bdc..f415f515 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift @@ -55,18 +55,6 @@ private extension PPPopupGridCollecView { section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 24 - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(22) - ) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: Self.elementKindSectionHeader, - alignment: .top - ) - section.boundarySupplementaryItems = [header] - return section } } From 62d35a623c279847b4ae2714456b698258eaa741 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 00:01:13 +0900 Subject: [PATCH 226/393] =?UTF-8?q?refactor/#131:=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=EC=A0=9C=EC=96=B4=EC=9E=90=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/PPPopupGridCollectionViewCell.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift index 3fde47d3..f7f194c8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift @@ -5,7 +5,7 @@ import DesignSystem import RxSwift import SnapKit -final class PPPopupGridCollectionViewCell: UICollectionViewCell { +public final class PPPopupGridCollectionViewCell: UICollectionViewCell { // MARK: - Properties @@ -124,7 +124,7 @@ private extension PPPopupGridCollectionViewCell { } extension PPPopupGridCollectionViewCell: Inputable { - struct Input { + public struct Input: Hashable { var imagePath: String? var id: Int64 var category: String? @@ -138,7 +138,7 @@ extension PPPopupGridCollectionViewCell: Inputable { var row: Int? } - func injection(with input: Input) { + public func injection(with input: Input) { categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .korFont(style: .bold, size: 11)) titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 14)) addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .medium, size: 11)) From f731e4fbe48f656651b583ebcf63c813f5e560fc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 00:02:03 +0900 Subject: [PATCH 227/393] =?UTF-8?q?feat/#131:=20DiffableDatasource=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PPPopupGridCollectionView.swift | 39 ++++++++++++++++--- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift index f415f515..c8ed9fb6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift @@ -5,27 +5,56 @@ import SnapKit public final class PPPopupGridCollecView: UICollectionView { // MARK: - Properties + public enum Section: Int, CaseIterable { + case main + } + + private lazy var diffableDataSource: UICollectionViewDiffableDataSource = { + return UICollectionViewDiffableDataSource(collectionView: self) { collectionView, indexPath, model in + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, + for: indexPath + ) as! PPPopupGridCollectionViewCell + cell.injection(with: model) + return cell + } + }() + // MARK: - init public init() { super.init(frame: .zero, collectionViewLayout: Self.makeLayout()) self.addViews() - self.setupConstraints() self.configureUI() } required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } + + /// 모델을 입력받으면 collectionView를 업데이트함 + public func apply( + _ models: [PPPopupGridCollectionViewCell.Input], + animating: Bool = true + ) { + var snapshot = diffableDataSource.snapshot() + snapshot.appendItems(models, toSection: .main) + diffableDataSource.apply(snapshot, animatingDifferences: animating) + } } // MARK: - SetUp private extension PPPopupGridCollecView { - func addViews() { } - - func setupConstraints() { } + func addViews() { + self.register( + PPPopupGridCollectionViewCell.self, + forCellWithReuseIdentifier: PPPopupGridCollectionViewCell.identifiers + ) + } - func configureUI() { } + func configureUI() { + super.dataSource = diffableDataSource + } } // MARK: - Layout From 519b187f18f02311f9eb659808f3ac7657a1013f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 00:28:01 +0900 Subject: [PATCH 228/393] =?UTF-8?q?feat/#131:=20=EC=9E=AC=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=EB=90=98=EB=8A=94=20TagCollectionView=20=EC=9D=B4?= =?UTF-8?q?=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/TagCollectionView.swift | 102 ++++++++++++++++++ .../Source/View/TagCollectionViewCell.swift | 99 +++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift new file mode 100644 index 00000000..1fae0f49 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift @@ -0,0 +1,102 @@ +import UIKit + +import SnapKit + +public final class TagCollectionView: UICollectionView { + // MARK: - Properties + + public enum Section: Int, CaseIterable { + case main + } + + private lazy var diffableDataSource: UICollectionViewDiffableDataSource = { + return UICollectionViewDiffableDataSource(collectionView: self) { collectionView, indexPath, model in + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: TagCollectionViewCell.identifiers, + for: indexPath + ) as! TagCollectionViewCell + cell.injection(with: model) + return cell + } + }() + + // MARK: - init + public init() { + super.init(frame: .zero, collectionViewLayout: Self.makeLayout()) + + self.addViews() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } + + /// 모델을 입력받으면 collectionView를 업데이트함 + public func apply( + _ models: [TagCollectionViewCell.Input], + animating: Bool = true + ) { + var snapshot = diffableDataSource.snapshot() + snapshot.appendItems(models, toSection: .main) + diffableDataSource.apply(snapshot, animatingDifferences: animating) + } +} + +// MARK: - SetUp +private extension TagCollectionView { + func addViews() { + self.register( + TagCollectionViewCell.self, + forCellWithReuseIdentifier: TagCollectionViewCell.identifiers + ) + } + + func configureUI() { + super.dataSource = diffableDataSource + } +} + +// MARK: - Layout +private extension TagCollectionView { + static func makeLayout() -> UICollectionViewCompositionalLayout { + return UICollectionViewCompositionalLayout { _, _ in + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .absolute(31) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(31) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0) + section.interGroupSpacing = 6 + + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(24) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: Self.elementKindSectionHeader, + alignment: .top + ) + section.boundarySupplementaryItems = [header] + + return section + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift new file mode 100644 index 00000000..714829a5 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -0,0 +1,99 @@ +import UIKit + +import DesignSystem + +import RxSwift +import SnapKit + +public final class TagCollectionViewCell: UICollectionViewCell { + + // MARK: - Components + + var disposeBag = DisposeBag() + + private let titleLabel = PPLabel(style: .medium, fontSize: 11) + + let cancelButton = UIButton() + + private let contentStackView = UIStackView().then { + $0.alignment = .center + $0.spacing = 2 + } + + // MARK: - init + + override init(frame: CGRect) { + super.init(frame: frame) + setUpConstraints() + } + + required init?(coder: NSCoder) { + fatalError() + } + + public override func prepareForReuse() { + super.prepareForReuse() + disposeBag = DisposeBag() + } +} + +// MARK: - SetUp +private extension TagCollectionViewCell { + func setUpConstraints() { + contentView.layer.cornerRadius = 15.5 + contentView.clipsToBounds = true + contentView.layer.borderWidth = 1 + + contentView.addSubview(contentStackView) + contentStackView.snp.makeConstraints { make in + make.top.bottom.equalToSuperview() + make.leading.equalToSuperview().inset(12) + make.trailing.equalToSuperview().inset(8) + } + contentStackView.addArrangedSubview(titleLabel) + contentStackView.addArrangedSubview(cancelButton) + + titleLabel.snp.makeConstraints { make in + make.height.equalTo(18) + } + cancelButton.snp.makeConstraints { make in + make.size.equalTo(16) + } + } +} + +extension TagCollectionViewCell: Inputable { + public struct Input: Hashable { + var title: String? + var id: Int64? = nil + var isSelected: Bool = false + var isCancelAble: Bool = true + } + + public func injection(with input: Input) { + let xmarkImage = input.isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") + cancelButton.setImage(xmarkImage, for: .normal) + if input.isSelected { + contentView.backgroundColor = .blu500 + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) + titleLabel.textColor = .w100 + contentView.layer.borderColor = UIColor.blu500.cgColor + } else { + contentView.backgroundColor = .clear + titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) + titleLabel.textColor = .g400 + contentView.layer.borderColor = UIColor.g200.cgColor + } + cancelButton.isHidden = !input.isCancelAble + + if input.isCancelAble { + contentStackView.snp.updateConstraints { make in + make.trailing.equalToSuperview().inset(8) + } + } else { + contentStackView.snp.updateConstraints { make in + make.trailing.equalToSuperview().inset(12) + } + } + } +} From 6abccbf5c08dbb580b3fb5427b960bfdb7d4c1c2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 02:13:28 +0900 Subject: [PATCH 229/393] =?UTF-8?q?refactor/#131:=20GridCell=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PPPopupGridCollectionViewCell.swift | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift index f7f194c8..1c8bffd4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift @@ -17,22 +17,26 @@ public final class PPPopupGridCollectionViewCell: UICollectionViewCell { private let categoryLabel = PPLabel(style: .bold, fontSize: 11).then { $0.textColor = .blu500 + $0.setLineHeightText(text: "category", font: .korFont(style: .bold, size: 11)) } private let titleLabel = PPLabel(style: .bold, fontSize: 14).then { $0.numberOfLines = 2 $0.lineBreakMode = .byTruncatingTail + $0.setLineHeightText(text: "title", font: .korFont(style: .bold, size: 14)) } private let addressLabel = PPLabel(style: .medium, fontSize: 11).then { $0.numberOfLines = 1 $0.lineBreakMode = .byTruncatingTail $0.textColor = .g400 + $0.setLineHeightText(text: "address", font: .korFont(style: .medium, size: 11)) } private let dateLabel = PPLabel(style: .medium, fontSize: 11).then { $0.lineBreakMode = .byTruncatingTail $0.textColor = .g400 + $0.setLineHeightText(text: "date", font: .korFont(style: .medium, size: 11)) } let bookmarkButton = UIButton() @@ -43,6 +47,8 @@ public final class PPPopupGridCollectionViewCell: UICollectionViewCell { $0.clipsToBounds = true $0.isHidden = true $0.textColor = .w100 + $0.textAlignment = .center + $0.setLineHeightText(text: "rank", font: .korFont(style: .medium, size: 11), lineHeight: 1) } // MARK: - init @@ -57,6 +63,11 @@ public final class PPPopupGridCollectionViewCell: UICollectionViewCell { required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } + + public override func prepareForReuse() { + super.prepareForReuse() + disposeBag = DisposeBag() + } } // MARK: - SetUp @@ -139,22 +150,24 @@ extension PPPopupGridCollectionViewCell: Inputable { } public func injection(with input: Input) { - categoryLabel.setLineHeightText(text: "#" + (input.category ?? ""), font: .korFont(style: .bold, size: 11)) - titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 14)) - addressLabel.setLineHeightText(text: input.address, font: .korFont(style: .medium, size: 11)) + categoryLabel.text = "#" + (input.category ?? "") + titleLabel.text = input.title + addressLabel.text = input.address + let date = input.startDate.toDate().toPPDateString() + " ~ " + input.endDate.toDate().toPPDateString() - dateLabel.setLineHeightText(text: date, font: .korFont(style: .medium, size: 11)) + dateLabel.text = date + let bookmarkImage = input.isBookmark ? UIImage(named: "icon_bookmark_fill") : UIImage(named: "icon_bookmark") bookmarkButton.setImage(bookmarkImage, for: .normal) + imageView.setPPImage(path: input.imagePath) - bookmarkButton.isHidden = !input.isLogin + bookmarkButton.isHidden = !input.isLogin rankLabel.isHidden = !input.isPopular - let rank = input.row ?? 0 - rankLabel.setLineHeightText(text: "\(rank + 1)위", font: .korFont(style: .medium, size: 11), lineHeight: 1) - rankLabel.textAlignment = .center - if rank > 2 { - rankLabel.isHidden = true + + if let rank = input.row { + rankLabel.text = "\(rank)" + rankLabel.isHidden = rank > 2 } } } From 93306802b84e6fee41941a427b8f71e6392e950e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 02:15:17 +0900 Subject: [PATCH 230/393] =?UTF-8?q?refactor/#131:=20Inputable=EC=97=90=20H?= =?UTF-8?q?ashable=20=EC=B1=84=ED=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 추후 diffableDataSource를 위함 --- .../DesignSystem/Utills/Interfaces/InOutputable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift index d1751a93..e78b7ffe 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/InOutputable.swift @@ -2,7 +2,7 @@ import UIKit public protocol InOutputable: Inputable, Outputable { } -public protocol Inputable { +public protocol Inputable: Hashable { associatedtype Input func injection(with input: Input) } From 9b7b191945da07f59968180148f065382c8f03ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sat, 3 May 2025 02:52:48 +0900 Subject: [PATCH 231/393] =?UTF-8?q?fix/#130:=20=EB=A7=A4=EC=A7=81=EB=84=98?= =?UTF-8?q?=EB=B2=84=20=EC=83=81=EC=88=98=ED=99=94=20=EB=B0=8F=20Attribute?= =?UTF-8?q?d=20String=20+=20Baseline=20Offset=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FillterSheetView/BalloonChipCell.swift | 181 +++++++++++------- 1 file changed, 111 insertions(+), 70 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 81b1f4b5..d29dc6db 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -1,88 +1,129 @@ +import Infrastructure import SnapKit import UIKit final class BalloonChipCell: UICollectionViewCell { - static let identifier = "BalloonChipCell" + static let identifier = "BalloonChipCell" - private let button: PPButton = { - let button = PPButton( - style: .secondary, - text: "", - font: .korFont(style: .medium, size: 11), - cornerRadius: 15 - ) + private enum Constant { + static let verticalInset: CGFloat = 6 + static let selectedLeftInset: CGFloat = 10 + static let normalLeftInset: CGFloat = 12 + static let rightInset: CGFloat = 12 + static let checkIconSize: CGSize = .init(width: 16, height: 16) + static let baselineOffset: CGFloat = -1 + static let fontSize: CGFloat = 11 + } - button.titleLabel?.lineBreakMode = .byTruncatingTail - button.titleLabel?.adjustsFontSizeToFitWidth = false - return button - }() + private let button: PPButton = { + let button = PPButton( + style: .secondary, + text: "", + font: .korFont(style: .medium, size: Constant.fontSize), + cornerRadius: 15 + ) + button.titleLabel?.lineBreakMode = .byTruncatingTail + button.titleLabel?.adjustsFontSizeToFitWidth = false + return button + }() - override init(frame: CGRect) { - super.init(frame: frame) - contentView.addSubview(button) - setupLayout() - } + private var currentAction: UIAction? - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } + override init(frame: CGRect) { + super.init(frame: frame) + contentView.addSubview(button) + setupLayout() + } - private func setupLayout() { - button.snp.makeConstraints { make in - make.edges.equalToSuperview() - } - } + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } - func configure(with title: String, isSelected: Bool) { - button.setTitle(title, for: .normal) - if isSelected { - let checkImage = UIImage(named: "icon_check_white")?.withRenderingMode(.alwaysOriginal) - let resizedImage = checkImage?.resize(to: CGSize(width: 16, height: 16)) - button.setImage(resizedImage, for: .normal) - button.semanticContentAttribute = .forceRightToLeft - button.imageEdgeInsets = UIEdgeInsets(top: 0, left: 2, bottom: 0, right: 0) - button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 10, bottom: 6, right: 12) - button.setBackgroundColor(.blu500, for: .normal) - button.setTitleColor(.white, for: .normal) - button.layer.borderWidth = 0 - button.titleLabel?.font = .korFont(style: .bold, size: 11) + private func setupLayout() { + button.snp.makeConstraints { make in + make.edges.equalToSuperview() + } + } - } else { - button.setImage(nil, for: .normal) - button.semanticContentAttribute = .unspecified - button.imageEdgeInsets = .zero - button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 12, bottom: 6, right: 12) - button.setBackgroundColor(.white, for: .normal) - button.setTitleColor(.g400, for: .normal) - button.layer.borderWidth = 1 - button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .korFont(style: .medium, size: 11) + func configure(with title: String, isSelected: Bool) { + let attributedTitle = NSMutableAttributedString(string: title) + attributedTitle.addAttribute( + .baselineOffset, + value: Constant.baselineOffset, + range: NSRange(location: .zero, length: attributedTitle.length) + ) - } - } + if isSelected { + let checkImage = UIImage(named: "icon_check_white")? + .withRenderingMode(.alwaysOriginal) + .resize(to: Constant.checkIconSize) + self.button.setImage(checkImage, for: .normal) + self.button.semanticContentAttribute = .forceRightToLeft + self.button.imageEdgeInsets = .init( + top: .zero, + left: 1, + bottom: .zero, + right: .zero + ) + self.button.contentEdgeInsets = .init( + top: Constant.verticalInset, + left: Constant.selectedLeftInset, + bottom: Constant.verticalInset, + right: Constant.rightInset + ) + self.button.setBackgroundColor(.blu500, for: .normal) + self.button.setTitleColor(.white, for: .normal) + self.button.layer.borderWidth = .zero - private var currentAction: UIAction? + attributedTitle.addAttribute( + .font, + value: UIFont.korFont(style: .bold, size: Constant.fontSize)!, + range: NSRange(location: .zero, length: attributedTitle.length) + ) + } else { + self.button.setImage(nil, for: .normal) + self.button.semanticContentAttribute = .unspecified + self.button.imageEdgeInsets = .zero + self.button.contentEdgeInsets = .init( + top: Constant.verticalInset, + left: Constant.normalLeftInset, + bottom: Constant.verticalInset, + right: Constant.rightInset + ) + self.button.setBackgroundColor(.white, for: .normal) + self.button.setTitleColor(.g400, for: .normal) + self.button.layer.borderWidth = 1 + self.button.layer.borderColor = UIColor.g200.cgColor - var buttonAction: (() -> Void)? { - didSet { - if let oldAction = currentAction { - button.removeAction(oldAction, for: .touchUpInside) - } + attributedTitle.addAttribute( + .font, + value: UIFont.korFont(style: .medium, size: Constant.fontSize)!, + range: NSRange(location: .zero, length: attributedTitle.length) + ) + } - let action = UIAction { [weak self] _ in - self?.buttonAction?() - } - button.addAction(action, for: .touchUpInside) - currentAction = action - } - } -} + self.button.setAttributedTitle(attributedTitle, for: .normal) + } + var buttonAction: (() -> Void)? { + didSet { + if let oldAction = currentAction { + self.button.removeAction(oldAction, for: .touchUpInside) + } + let action = UIAction { [weak self] _ in + guard let self = self else { return } + self.buttonAction?() + } + self.button.addAction(action, for: .touchUpInside) + self.currentAction = action + } + } +} extension UIImage { - func resize(to size: CGSize) -> UIImage? { - UIGraphicsBeginImageContextWithOptions(size, false, 0.0) - defer { UIGraphicsEndImageContext() } - draw(in: CGRect(origin: .zero, size: size)) - return UIGraphicsGetImageFromCurrentImageContext() - } + func resize(to size: CGSize) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(size, false, 0.0) + defer { UIGraphicsEndImageContext() } + self.draw(in: CGRect(origin: .zero, size: size)) + return UIGraphicsGetImageFromCurrentImageContext() + } } From b3fbe66f7b0a13e1206db36ef4525bc32fc636f4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:07:03 +0900 Subject: [PATCH 232/393] =?UTF-8?q?chore/#131:=20=ED=94=84=EB=A0=88?= =?UTF-8?q?=EC=9E=84=EC=9B=8C=ED=81=AC=20embed=20=EC=84=A4=EC=A0=95=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 --- .../SearchFeature.xcodeproj/project.pbxproj | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index bf59747b..22d86e5e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -12,12 +12,11 @@ 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233B2DC49A7600C761A5 /* RxCocoa */; }; 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233D2DC49A7600C761A5 /* RxSwift */; }; 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC23402DC49A8B00C761A5 /* ReactorKit */; }; - 05EC23432DC49AA200C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; - 05EC23442DC49AA200C761A5 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23462DC49AA800C761A5 /* DomainInterface.framework */; }; - 05EC23482DC49AA800C761A5 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23462DC49AA800C761A5 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC234B2DC49AB400C761A5 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234A2DC49AB400C761A5 /* Then */; }; 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234D2DC49AC100C761A5 /* SnapKit */; }; + 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; + 05EC28602DC5C1CF00C761A5 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -42,14 +41,13 @@ name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; - 05EC23452DC49AA200C761A5 /* Embed Frameworks */ = { + 05EC28612DC5C1CF00C761A5 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 05EC23482DC49AA800C761A5 /* DomainInterface.framework in Embed Frameworks */, - 05EC23442DC49AA200C761A5 /* DesignSystem.framework in Embed Frameworks */, + 05EC28602DC5C1CF00C761A5 /* DesignSystem.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -94,11 +92,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */, 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, - 05EC23432DC49AA200C761A5 /* DesignSystem.framework in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); @@ -164,7 +162,7 @@ 051633692DC457A900A6C0D1 /* Sources */, 0516336A2DC457A900A6C0D1 /* Frameworks */, 0516336B2DC457A900A6C0D1 /* Resources */, - 05EC23452DC49AA200C761A5 /* Embed Frameworks */, + 05EC28612DC5C1CF00C761A5 /* Embed Frameworks */, ); buildRules = ( ); From e11325bf34fd2c901088fdfe492d7bf8cd7fba18 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:07:48 +0900 Subject: [PATCH 233/393] =?UTF-8?q?feat/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20View=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 하나의 CompositionalLayout과 diffableDataSource를 사용하는 방식으로 수정 --- .../Source/View/PopupSearchView.swift | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift new file mode 100644 index 00000000..c13dda9e --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -0,0 +1,269 @@ +import UIKit + +import SnapKit +import Then + +final class PopupSearchView: UIView { + enum Section: CaseIterable, Hashable { + case recentSearch + case category + case searchResult + } + + enum SectionItem: Hashable { + case recentSearchItem(TagCollectionViewCell.Input) + case categoryItem(TagCollectionViewCell.Input) + case searchResultItem(PPPopupGridCollectionViewCell.Input) + } + + enum SectionHeaderKind: String, CaseIterable { + case recentSearch = "recentSearchElementKind" + case category = "categoryElementKind" + } + + // MARK: - Properties + let searchBar = PPSearchBarView() + let collectionView = UICollectionView( + frame: .zero, + collectionViewLayout: PopupSearchView.makeLayout() + ).then { + $0.register( + TagCollectionViewCell.self, + forCellWithReuseIdentifier: TagCollectionViewCell.identifiers + ) + + $0.register( + PPPopupGridCollectionViewCell.self, + forCellWithReuseIdentifier: PPPopupGridCollectionViewCell.identifiers + ) + + $0.register( + TagCollectionHeaderView.self, + forSupplementaryViewOfKind: SectionHeaderKind.recentSearch.rawValue, + withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.identifer + ) + + $0.register( + TagCollectionHeaderView.self, + forSupplementaryViewOfKind: SectionHeaderKind.category.rawValue, + withReuseIdentifier: TagCollectionHeaderView.Identifier.category.identifer + ) + } + + private var dataSource: UICollectionViewDiffableDataSource? + + // 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 PopupSearchView { + func addViews() { + [searchBar, collectionView].forEach { + self.addSubview($0) + } + } + + func setupConstraints() { + searchBar.snp.makeConstraints { make in + make.top.equalTo(self.safeAreaLayoutGuide).inset(12) + make.horizontalEdges.equalToSuperview() + make.height.equalTo(56) + } + + collectionView.snp.makeConstraints { make in + make.top.equalTo(searchBar.snp.bottom).offset(24) + make.horizontalEdges.equalToSuperview() + make.bottom.equalToSuperview() + } + } + + func configureUI() { + self.searchBar.backgroundColor = .yellow + self.collectionView.backgroundColor = UIColor.green + + self.configurationDataSourceItem() + self.configureDataSourceHeader() + } +} + +// MARK: - Layout +private extension PopupSearchView { + static func makeLayout() -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout { sectionIndex, environment in + switch Section.allCases[sectionIndex] { + case .recentSearch: return self.makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) + case .category: return self.makeTagSectionLayout(SectionHeaderKind.category.rawValue) + case .searchResult: return self.makeSearchResultSectionLayout() + } + } + } + + static func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .absolute(31) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(31) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0) + section.interGroupSpacing = 6 + + section.boundarySupplementaryItems = [makeHeaderLayout(headerKind)] + + return section + } + + static func makeSearchResultSectionLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(0.5), + heightDimension: .absolute(249) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(249) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item, item] + ) + group.interItemSpacing = .fixed(16) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 24 + + return section + } + + static func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(24) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + + return header + } +} + +// MARK: - DataSource +extension PopupSearchView { + private func configurationDataSourceItem() { + print("HEADER DEBUG:", #function, #line, "data source is", self.dataSource == nil) + self.dataSource = UICollectionViewDiffableDataSource( + collectionView: collectionView + ) { (collectionView, indexPath, item) -> UICollectionViewCell? in + switch item { + case .recentSearchItem(let recentRearchItem): + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: TagCollectionViewCell.identifiers, + for: indexPath + ) as! TagCollectionViewCell + cell.injection(with: recentRearchItem) + return cell + + case .categoryItem(let categoryItem): + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: TagCollectionViewCell.identifiers, + for: indexPath + ) as! TagCollectionViewCell + cell.injection(with: categoryItem) + return cell + + case .searchResultItem(let searchResultItem): + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, + for: indexPath + ) as! PPPopupGridCollectionViewCell + cell.injection(with: searchResultItem) + return cell + } + } + + print("HEADER DEBUG:", #function, #line, "data source is", self.dataSource == nil) + + self.collectionView.dataSource = self.dataSource + } + + private func configureDataSourceHeader() { + print("HEADER DEBUG:", #function, #line) + dataSource?.supplementaryViewProvider = { (collectionView, elementKind, indexPath) -> UICollectionReusableView? in + + print("HEADER DEBUG:", #function, #line, "elementKind is", elementKind) + + switch SectionHeaderKind(rawValue: elementKind)! { + case .recentSearch: + print("HEADER DEBUG:", #function, #line) + guard let header = collectionView.dequeueReusableSupplementaryView( + ofKind: elementKind, + withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.identifer, + for: indexPath + ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error")} + header.setupHeader(title: "최근 검색어", buttonTitle: "모두삭제") + + return header + + case .category: + print("HEADER DEBUG:", #function, #line) + guard let header = collectionView.dequeueReusableSupplementaryView( + ofKind: elementKind, + withReuseIdentifier: TagCollectionHeaderView.Identifier.category.identifer, + for: indexPath + ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error")} + header.setupHeader(title: "팝업스토어 찾기") + + return header + } + } + } + + private func updateSnapshot( + recentSearchItems: [SectionItem], + categoryItems: [SectionItem], + searchResultItems: [SectionItem] + ) { + guard var snapshot = dataSource?.snapshot() else { return } + + snapshot.appendSections(PopupSearchView.Section.allCases) + snapshot.appendItems(recentSearchItems, toSection: .recentSearch) + snapshot.appendItems(categoryItems, toSection: .category) + snapshot.appendItems(searchResultItems, toSection: .searchResult) + + dataSource?.apply(snapshot, animatingDifferences: true) + } +} From f0c651f230ffaa07293a7568ca1d4fb0d639a897 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:08:39 +0900 Subject: [PATCH 234/393] =?UTF-8?q?fix/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20ViewController=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 다른 ViewController의 컨벤션과 동일하게 하나의 mainView를 사용하도록 함 --- .../View/PopupSearchViewController.swift | 35 +++---------------- 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 1ee3f819..bdb52734 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -12,48 +12,23 @@ public final class PopupSearchViewController: BaseViewController, View { // MARK: - Properties public var disposeBag = DisposeBag() - private let searchBar = PPSearchBarView() + private let mainView = PopupSearchView() } // MARK: - Life Cycle extension PopupSearchViewController { + public override func loadView() { + self.view = mainView + } public override func viewDidLoad() { super.viewDidLoad() - - self.addViews() - self.setupConstraints() - self.configureUI() } } -// MARK: - SetUp -private extension PopupSearchViewController { - func addViews() { - self.view.addSubview(searchBar) - } - - func setupConstraints() { - searchBar.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.trailing.equalToSuperview().inset(16) - make.top.equalTo(self.view.safeAreaLayoutGuide.snp.top).offset(12) - } - } - - func configureUI() { } -} - // MARK: - Bind extension PopupSearchViewController { public func bind(reactor: Reactor) { - searchBar.cancelButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.navigationController?.popViewController(animated: true) - } - .disposed(by: disposeBag) + } } - -extension PopupSearchViewController: UISearchBarDelegate { } From e6f3f0e391751dcbebdbe2115119bc8807827fe4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:09:02 +0900 Subject: [PATCH 235/393] =?UTF-8?q?remove/#131:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20View=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 --- .../View/PPPopupGridCollectionView.swift | 90 ---------------- .../Source/View/TagCollectionView.swift | 102 ------------------ 2 files changed, 192 deletions(-) delete mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift delete mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift deleted file mode 100644 index c8ed9fb6..00000000 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionView.swift +++ /dev/null @@ -1,90 +0,0 @@ -import UIKit - -import SnapKit - -public final class PPPopupGridCollecView: UICollectionView { - // MARK: - Properties - - public enum Section: Int, CaseIterable { - case main - } - - private lazy var diffableDataSource: UICollectionViewDiffableDataSource = { - return UICollectionViewDiffableDataSource(collectionView: self) { collectionView, indexPath, model in - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, - for: indexPath - ) as! PPPopupGridCollectionViewCell - cell.injection(with: model) - return cell - } - }() - - // MARK: - init - public init() { - super.init(frame: .zero, collectionViewLayout: Self.makeLayout()) - - self.addViews() - self.configureUI() - } - - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } - - /// 모델을 입력받으면 collectionView를 업데이트함 - public func apply( - _ models: [PPPopupGridCollectionViewCell.Input], - animating: Bool = true - ) { - var snapshot = diffableDataSource.snapshot() - snapshot.appendItems(models, toSection: .main) - diffableDataSource.apply(snapshot, animatingDifferences: animating) - } -} - -// MARK: - SetUp -private extension PPPopupGridCollecView { - func addViews() { - self.register( - PPPopupGridCollectionViewCell.self, - forCellWithReuseIdentifier: PPPopupGridCollectionViewCell.identifiers - ) - } - - func configureUI() { - super.dataSource = diffableDataSource - } -} - -// MARK: - Layout -private extension PPPopupGridCollecView { - static func makeLayout() -> UICollectionViewCompositionalLayout { - return UICollectionViewCompositionalLayout { _, _ in - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(0.5), - heightDimension: .absolute(249) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(249) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item, item] - ) - group.interItemSpacing = .fixed(16) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) - section.interGroupSpacing = 24 - - return section - } - } -} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift deleted file mode 100644 index 1fae0f49..00000000 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionView.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import SnapKit - -public final class TagCollectionView: UICollectionView { - // MARK: - Properties - - public enum Section: Int, CaseIterable { - case main - } - - private lazy var diffableDataSource: UICollectionViewDiffableDataSource = { - return UICollectionViewDiffableDataSource(collectionView: self) { collectionView, indexPath, model in - let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: TagCollectionViewCell.identifiers, - for: indexPath - ) as! TagCollectionViewCell - cell.injection(with: model) - return cell - } - }() - - // MARK: - init - public init() { - super.init(frame: .zero, collectionViewLayout: Self.makeLayout()) - - self.addViews() - self.configureUI() - } - - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } - - /// 모델을 입력받으면 collectionView를 업데이트함 - public func apply( - _ models: [TagCollectionViewCell.Input], - animating: Bool = true - ) { - var snapshot = diffableDataSource.snapshot() - snapshot.appendItems(models, toSection: .main) - diffableDataSource.apply(snapshot, animatingDifferences: animating) - } -} - -// MARK: - SetUp -private extension TagCollectionView { - func addViews() { - self.register( - TagCollectionViewCell.self, - forCellWithReuseIdentifier: TagCollectionViewCell.identifiers - ) - } - - func configureUI() { - super.dataSource = diffableDataSource - } -} - -// MARK: - Layout -private extension TagCollectionView { - static func makeLayout() -> UICollectionViewCompositionalLayout { - return UICollectionViewCompositionalLayout { _, _ in - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(31) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .continuous - section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0) - section.interGroupSpacing = 6 - - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(24) - ) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: Self.elementKindSectionHeader, - alignment: .top - ) - section.boundarySupplementaryItems = [header] - - return section - } - } -} From 6dfe6833eca4a16fb9023c1567ed573ad0ed069d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:09:41 +0900 Subject: [PATCH 236/393] =?UTF-8?q?refactor/#131:=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PPSearchBarView.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift index b842c9be..7223572e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift @@ -57,8 +57,10 @@ private extension PPSearchBarView { func setupConstraints() { stackView.snp.makeConstraints { make in - make.edges.equalToSuperview() - make.height.equalTo(37) + make.top.equalToSuperview().inset(12) + make.leading.equalToSuperview().inset(20) + make.trailing.equalToSuperview().inset(16) + make.bottom.equalToSuperview().inset(7) } searchBar.snp.makeConstraints { make in From be5d716936b040ec1e21631b6049c23ec25d46bd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:10:04 +0900 Subject: [PATCH 237/393] =?UTF-8?q?feat/#131:=20TagCollectionView=20?= =?UTF-8?q?=EB=8F=9A=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/TagCollectionViewCell.swift | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift index 714829a5..b134317f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -24,11 +24,14 @@ public final class TagCollectionViewCell: UICollectionViewCell { override init(frame: CGRect) { super.init(frame: frame) - setUpConstraints() + + self.addViews() + self.setupConstraints() + self.configureUI() } required init?(coder: NSCoder) { - fatalError() + fatalError("\(#file), \(#function) Error") } public override func prepareForReuse() { @@ -39,27 +42,37 @@ public final class TagCollectionViewCell: UICollectionViewCell { // MARK: - SetUp private extension TagCollectionViewCell { - func setUpConstraints() { - contentView.layer.cornerRadius = 15.5 - contentView.clipsToBounds = true - contentView.layer.borderWidth = 1 + func addViews() { + [contentStackView].forEach { + self.contentView.addSubview($0) + } + + [titleLabel, cancelButton].forEach { + contentStackView.addArrangedSubview($0) + } + } - contentView.addSubview(contentStackView) + func setupConstraints() { contentStackView.snp.makeConstraints { make in make.top.bottom.equalToSuperview() make.leading.equalToSuperview().inset(12) make.trailing.equalToSuperview().inset(8) } - contentStackView.addArrangedSubview(titleLabel) - contentStackView.addArrangedSubview(cancelButton) titleLabel.snp.makeConstraints { make in make.height.equalTo(18) } + cancelButton.snp.makeConstraints { make in make.size.equalTo(16) } } + + func configureUI() { + contentView.layer.cornerRadius = 15.5 + contentView.clipsToBounds = true + contentView.layer.borderWidth = 1 + } } extension TagCollectionViewCell: Inputable { @@ -67,7 +80,7 @@ extension TagCollectionViewCell: Inputable { var title: String? var id: Int64? = nil var isSelected: Bool = false - var isCancelAble: Bool = true + var isCancelable: Bool = true } public func injection(with input: Input) { @@ -84,9 +97,9 @@ extension TagCollectionViewCell: Inputable { titleLabel.textColor = .g400 contentView.layer.borderColor = UIColor.g200.cgColor } - cancelButton.isHidden = !input.isCancelAble + cancelButton.isHidden = !input.isCancelable - if input.isCancelAble { + if input.isCancelable { contentStackView.snp.updateConstraints { make in make.trailing.equalToSuperview().inset(8) } From a9adab4f080a23dc1fc42f133706824a34e082ef Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 15:10:23 +0900 Subject: [PATCH 238/393] =?UTF-8?q?feat/#131:=20TagCollectionView=EC=97=90?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=EB=90=A0=20HeaderView=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 --- .../Source/View/TagCollectionHeaderView.swift | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift new file mode 100644 index 00000000..af439ecf --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -0,0 +1,97 @@ +import UIKit + +import DesignSystem + +import RxSwift +import SnapKit + +final class TagCollectionHeaderView: UICollectionReusableView { + + enum Identifier: String { + var identifer: String { + switch self { + case .recentSearch: return "TagCollectionHeaderView.recentSearch" + case .category: return "TagCollectionHeaderView.category" + } + } + + case recentSearch + case category + } + + // MARK: - Components + var disposeBag = DisposeBag() + + private let sectionTitleLabel = UILabel().then { + $0.font = .korFont(style: .bold, size: 16) + } + + let titleButton = UIButton().then { + $0.isHidden = true + + } + // MARK: - init + + override init(frame: CGRect) { + super.init(frame: frame) + + self.addViews() + self.setupConstraints() + + self.backgroundColor = .red + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } + + override func prepareForReuse() { + super.prepareForReuse() + disposeBag = DisposeBag() + } +} + +// MARK: - SetUp +private extension TagCollectionHeaderView { + func addViews() { + [sectionTitleLabel, titleButton].forEach { + self.addSubview($0) + } + } + + func setupConstraints() { + sectionTitleLabel.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.centerY.equalToSuperview() + make.height.equalTo(22) + } + + titleButton.snp.makeConstraints { make in + make.centerY.equalToSuperview() + make.trailing.equalToSuperview() + make.height.equalTo(20) + } + } + + func configureUI() { + + } +} + +extension TagCollectionHeaderView { + func setupHeader(title: String, buttonTitle: String? = nil) { + sectionTitleLabel.text = title + + if let buttonTitle = buttonTitle { + titleButton.isHidden = false + + let attributes: [NSAttributedString.Key: Any] = [ + .underlineStyle: NSUnderlineStyle.single.rawValue, + .font: UIFont.korFont(style: .regular, size: 13)! + ] + + let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) + titleButton.setAttributedTitle(attributedTitle, for: .normal) + } + } +} From 2a056b3f557c57123fbb9272260b8f720e0a20a3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:33:56 +0900 Subject: [PATCH 239/393] =?UTF-8?q?refactor/#131:=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/TagCollectionHeaderView.swift | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index af439ecf..b3018435 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -21,7 +21,7 @@ final class TagCollectionHeaderView: UICollectionReusableView { // MARK: - Components var disposeBag = DisposeBag() - + private let sectionTitleLabel = UILabel().then { $0.font = .korFont(style: .bold, size: 16) } @@ -72,24 +72,17 @@ private extension TagCollectionHeaderView { make.height.equalTo(20) } } - - func configureUI() { - - } } extension TagCollectionHeaderView { func setupHeader(title: String, buttonTitle: String? = nil) { sectionTitleLabel.text = title - if let buttonTitle = buttonTitle { titleButton.isHidden = false - let attributes: [NSAttributedString.Key: Any] = [ .underlineStyle: NSUnderlineStyle.single.rawValue, .font: UIFont.korFont(style: .regular, size: 13)! ] - let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) titleButton.setAttributedTitle(attributedTitle, for: .normal) } From 8929ecf320c2df32a00447884326d6eb2478fdd5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:34:53 +0900 Subject: [PATCH 240/393] =?UTF-8?q?feat/#131:=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=9D=98=EC=A1=B4=EC=84=B1=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EB=B0=8F=20reactor=20=EC=A3=BC=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 32 ++++++++++ .../SearchFeatureDemo/App/AppDelegate.swift | 61 +++++++++++++++++++ .../SearchFeatureDemo/App/SceneDelegate.swift | 10 ++- 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 22d86e5e..ac702aa8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -17,6 +17,14 @@ 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234D2DC49AC100C761A5 /* SnapKit */; }; 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; 05EC28602DC5C1CF00C761A5 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC28662DC5FDF800C761A5 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; }; + 05EC28672DC5FDF800C761A5 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC28682DC5FDF800C761A5 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; }; + 05EC28692DC5FDF800C761A5 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC286B2DC5FE5B00C761A5 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; }; + 05EC286C2DC5FE5B00C761A5 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; }; + 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -36,6 +44,10 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 05EC286C2DC5FE5B00C761A5 /* Data.framework in Embed Frameworks */, + 05EC28692DC5FDF800C761A5 /* DomainInterface.framework in Embed Frameworks */, + 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */, + 05EC28672DC5FDF800C761A5 /* Domain.framework in Embed Frameworks */, 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -59,6 +71,10 @@ 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23462DC49AA800C761A5 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC28642DC5FDF800C761A5 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC286A2DC5FE5B00C761A5 /* Data.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Data.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -106,6 +122,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC286B2DC5FE5B00C761A5 /* Data.framework in Frameworks */, + 05EC28682DC5FDF800C761A5 /* DomainInterface.framework in Frameworks */, + 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */, + 05EC28662DC5FDF800C761A5 /* Domain.framework in Frameworks */, 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -135,6 +155,10 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */, + 05EC286A2DC5FE5B00C761A5 /* Data.framework */, + 05EC28642DC5FDF800C761A5 /* Domain.framework */, + 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */, 05EC23462DC49AA800C761A5 /* DomainInterface.framework */, 05EC23422DC49AA200C761A5 /* DesignSystem.framework */, ); @@ -297,6 +321,8 @@ /* Begin XCBuildConfiguration section */ 051633722DC457A900A6C0D1 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */; + baseConfigurationReferenceRelativePath = Resource/Debug.xcconfig; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -422,6 +448,8 @@ }; 051633752DC457A900A6C0D1 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */; + baseConfigurationReferenceRelativePath = Resource/Debug.xcconfig; buildSettings = { BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; @@ -501,6 +529,8 @@ }; 051636422DC457DF00A6C0D1 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReferenceAnchor = 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */; + baseConfigurationReferenceRelativePath = Resource/Debug.xcconfig; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -511,6 +541,7 @@ DEVELOPMENT_TEAM = ""; "DEVELOPMENT_TEAM[sdk=iphoneos*]" = W5QTRMS954; GENERATE_INFOPLIST_FILE = YES; + IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; @@ -544,6 +575,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; + IBSC_COMPILER_AUTO_ACTIVATE_CUSTOM_FONTS = YES; INFOPLIST_FILE = SearchFeatureDemo/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index 0f2b4b09..8211412f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -1,9 +1,17 @@ import UIKit +import Data +import Domain +import DomainInterface +import Infrastructure + @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + self.registerDependencies() + return true } @@ -14,3 +22,56 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } +// MARK: - Dependency +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 + + // MARK: Register repository + DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } + DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } + DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } + DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } + DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } + DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } + DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } + DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } + DIContainer.register(MapDirectionRepository.self) { return MapDirectionRepositoryImpl(provider: provider) } + DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } + DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } + DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } + + // MARK: Resolve repository + @Dependency var mapRepository: MapRepository + @Dependency var adminRepository: AdminRepository + @Dependency var userAPIRepository: UserAPIRepository + @Dependency var popUpAPIRepository: PopUpAPIRepository + @Dependency var commentAPIRepository: CommentAPIRepository + @Dependency var homeAPIRepository: HomeAPIRepository + @Dependency var authAPIRepository: AuthAPIRepository + @Dependency var signUpRepository: SignUpRepository + @Dependency var preSignedRepository: PreSignedRepository + @Dependency var kakaoLoginRepository: KakaoLoginRepository + @Dependency var appleLoginRepository: AppleLoginRepository + + // MARK: Register UseCase + DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } + DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } + DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } + DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } + DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } + DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } + DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } + DIContainer.register(SignUpAPIUseCase.self) { return SignUpAPIUseCaseImpl(repository: signUpRepository) } + DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } + DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } + DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } + } +} + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index c75dd504..242654ab 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -1,5 +1,9 @@ import UIKit +import Domain +import DomainInterface +import Infrastructure + import SearchFeature class SceneDelegate: UIResponder, UIWindowSceneDelegate { @@ -10,7 +14,11 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = PopupSearchViewController() + let vc = PopupSearchViewController() + vc.reactor = PopupSearchReactor( + useCase: DIContainer.resolve(PopUpAPIUseCase.self) + ) + window?.rootViewController = vc window?.makeKeyAndVisible() } } From 24bfa12bb0dc5ae0a3e1581341f878f3515d3cce Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:39:25 +0900 Subject: [PATCH 241/393] =?UTF-8?q?chore/#131:=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=ED=8F=B0=ED=8A=B8=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=EC=9C=BC=EB=A1=9C=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool/Resource/Info.plist | 11 --------- .../Resource/Font/GothicA1-Bold.ttf | Bin .../Resource/Font/GothicA1-Light.ttf | Bin .../Resource/Font/GothicA1-Medium.ttf | Bin .../Resource/Font/GothicA1-Regular.ttf | Bin .../Resource/Font/Poppins-Bold.ttf | Bin .../Resource/Font/Poppins-Light.ttf | Bin .../Resource/Font/Poppins-Medium.ttf | Bin .../Resource/Font/Poppins-Regular.ttf | Bin .../SearchFeatureDemo/Resource/Info.plist | 21 ++++++++++++++++++ 10 files changed, 21 insertions(+), 11 deletions(-) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/GothicA1-Bold.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/GothicA1-Light.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/GothicA1-Medium.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/GothicA1-Regular.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/Poppins-Bold.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/Poppins-Light.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/Poppins-Medium.ttf (100%) rename Poppool/{Poppool => PresentationLayer/SearchFeature/SearchFeatureDemo}/Resource/Font/Poppins-Regular.ttf (100%) diff --git a/Poppool/Poppool/Resource/Info.plist b/Poppool/Poppool/Resource/Info.plist index 560ae1a9..3f04e5cc 100644 --- a/Poppool/Poppool/Resource/Info.plist +++ b/Poppool/Poppool/Resource/Info.plist @@ -39,17 +39,6 @@ ${POPPOOL_BASE_URL} POPPOOL_S3_BASE_URL ${POPPOOL_S3_BASE_URL} - UIAppFonts - - GothicA1-Bold.ttf - GothicA1-Light.ttf - GothicA1-Medium.ttf - GothicA1-Regular.ttf - Poppins-Bold.ttf - Poppins-Light.ttf - Poppins-Medium.ttf - Poppins-Regular.ttf - UIApplicationSceneManifest UIApplicationSupportsMultipleScenes diff --git a/Poppool/Poppool/Resource/Font/GothicA1-Bold.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Bold.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/GothicA1-Bold.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Bold.ttf diff --git a/Poppool/Poppool/Resource/Font/GothicA1-Light.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Light.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/GothicA1-Light.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Light.ttf diff --git a/Poppool/Poppool/Resource/Font/GothicA1-Medium.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Medium.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/GothicA1-Medium.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Medium.ttf diff --git a/Poppool/Poppool/Resource/Font/GothicA1-Regular.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Regular.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/GothicA1-Regular.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Regular.ttf diff --git a/Poppool/Poppool/Resource/Font/Poppins-Bold.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Bold.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/Poppins-Bold.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Bold.ttf diff --git a/Poppool/Poppool/Resource/Font/Poppins-Light.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Light.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/Poppins-Light.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Light.ttf diff --git a/Poppool/Poppool/Resource/Font/Poppins-Medium.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Medium.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/Poppins-Medium.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Medium.ttf diff --git a/Poppool/Poppool/Resource/Font/Poppins-Regular.ttf b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Regular.ttf similarity index 100% rename from Poppool/Poppool/Resource/Font/Poppins-Regular.ttf rename to Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Regular.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist index 0eb786dc..b0c124ea 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist @@ -2,6 +2,27 @@ + UIAppFonts + + GothicA1-Bold.ttf + GothicA1-Light.ttf + GothicA1-Medium.ttf + GothicA1-Regular.ttf + Poppins-Bold.ttf + Poppins-Light.ttf + Poppins-Medium.ttf + Poppins-Regular.ttf + + NAVER_MAP_CLIENT_ID + ${NAVER_MAP_CLIENT_ID} + POPPOOL_S3_BASE_URL + ${POPPOOL_S3_BASE_URL} + POPPOOL_BASE_URL + ${POPPOOL_BASE_URL} + POPPOOL_API_KEY + ${POPPOOL_API_KEY} + KAKAO_AUTH_APP_KEY + ${KAKAO_AUTH_APP_KEY} UIApplicationSceneManifest UIApplicationSupportsMultipleScenes From 150585d1c4cd64cd5c8618409f642d08d5a93738 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:56:36 +0900 Subject: [PATCH 242/393] =?UTF-8?q?fix/#131:=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=EC=9D=B4=20=ED=8F=B0=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EC=B0=BE=EC=A7=80=20=EB=AA=BB=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - extension 변경으로 인한 관련 문제 대응 --- .../DesignSystem/Extension/UIFont+.swift | 39 ++++++++++++++----- .../Source/View/TagCollectionHeaderView.swift | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift index 134846c0..ff86a031 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift @@ -1,18 +1,39 @@ import UIKit +private final class BundleFinder { + static let module = Bundle(for: BundleFinder.self) +} + +fileprivate extension Bundle { + static let module = BundleFinder.module +} + public extension UIFont { - static func korFont(style: FontStyle, size: CGFloat) -> UIFont? { - return UIFont(name: "GothicA1\(style.rawValue)", size: size) + static func korFont(style: FontStyle, size: CGFloat) -> UIFont { + let fontName = "GothicA1-\(style.rawValue)" + + if let font = UIFont(name: fontName, size: size) { return font } + else { return registerAndGetFont(name: fontName, size: size) } } - static func engFont(style: FontStyle, size: CGFloat) -> UIFont? { - return UIFont(name: "Poppins\(style.rawValue)", size: size) + static func engFont(style: FontStyle, size: CGFloat) -> UIFont { + let fontName = "Poppins-\(style.rawValue)" + + if let font = UIFont(name: fontName, size: size) { return font } + else { return registerAndGetFont(name: fontName, size: size) } + } + + private static func registerAndGetFont(name: String, size: CGFloat) -> UIFont { + let url = Bundle.module.url(forResource: name, withExtension: "ttf")! + CTFontManagerRegisterFontURLs([url as CFURL] as CFArray, .process, true, nil) + return UIFont(name: name, size: size)! + } - public enum FontStyle: String { - case bold = "-Bold" - case medium = "-Medium" - case regular = "-Regular" - case light = "-Light" + enum FontStyle: String { + case bold = "Bold" + case medium = "Medium" + case regular = "Regular" + case light = "Light" } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index b3018435..f7686537 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -81,7 +81,7 @@ extension TagCollectionHeaderView { titleButton.isHidden = false let attributes: [NSAttributedString.Key: Any] = [ .underlineStyle: NSUnderlineStyle.single.rawValue, - .font: UIFont.korFont(style: .regular, size: 13)! + .font: UIFont.korFont(style: .regular, size: 13) ] let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) titleButton.setAttributedTitle(attributedTitle, for: .normal) From d485a11af5b4105700059b618a6ef2521554198b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:59:33 +0900 Subject: [PATCH 243/393] =?UTF-8?q?feat/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EC=8A=A4=ED=86=A0=EC=96=B4=20=EA=B2=80=EC=83=89=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B0=94=EC=9D=B8?= =?UTF-8?q?=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 76 +++++++++++++++++-- .../View/PopupSearchViewController.swift | 21 ++++- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index c2396caf..42f90b59 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -1,3 +1,8 @@ +import Foundation + +import DomainInterface +import Infrastructure + import ReactorKit import RxSwift import RxCocoa @@ -5,32 +10,91 @@ import RxCocoa public final class PopupSearchReactor: Reactor { // MARK: - Reactor - public enum Action { } + public enum Action { + case viewDidLoad + } public enum Mutation { + case setInitialState( + recentSearch: [TagCollectionViewCell.Input], + category: [TagCollectionViewCell.Input], + results: [PPPopupGridCollectionViewCell.Input] + ) } - public struct State { } + public struct State { + var recentSearchItems: [TagCollectionViewCell.Input] = [] + var categoryItems: [TagCollectionViewCell.Input] = [] + var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] + } // MARK: - properties public var initialState: State + var disposeBag = DisposeBag() + private let userDefaultService = UserDefaultService() + private let useCase: PopUpAPIUseCase + // MARK: - init - public init() { + public init(useCase: PopUpAPIUseCase) { + self.useCase = useCase self.initialState = State() } // MARK: - Reactor Methods public func mutate(action: Action) -> Observable { - switch action { } + switch action { + case .viewDidLoad: + print("ViewDidLoad") + return useCase.getSearchBottomPopUpList( + isOpen: true, + categories: [], + page: 0, + size: 10, + sort: "NEWEST" + ) + .withUnretained(self) + .map { owner, response in + return .setInitialState( + recentSearch: [], + category: [], + results: owner.convertResponseToSearchResultInput(response: response)) + } + } } public func reduce(state: State, mutation: Mutation) -> State { var newState = state + switch mutation { + case let .setInitialState(recentSearch, category, results): + newState.recentSearchItems = recentSearch + newState.categoryItems = category + newState.searchResultItems = results + } + return newState + } +} - switch mutation { } +// MARK: - Functions +private extension PopupSearchReactor { + func getRecentSearchKeywords() -> [String] { + return userDefaultService.fetchArray(key: "searchList") ?? [] + } - return newState + func convertResponseToSearchResultInput(response: GetSearchBottomPopUpListResponse) -> [PPPopupGridCollectionViewCell.Input] { + return response.popUpStoreList.map { + PPPopupGridCollectionViewCell.Input( + imagePath: $0.mainImageUrl, + id: $0.id, + category: $0.category, + title: $0.name, + address: $0.address, + startDate: $0.startDate, + endDate: $0.endDate, + isBookmark: $0.bookmarkYn, + isLogin: response.loginYn + ) + } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index bdb52734..f5085cf7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -29,6 +29,25 @@ extension PopupSearchViewController { // MARK: - Bind extension PopupSearchViewController { public func bind(reactor: Reactor) { - + rx.viewDidLoad + .map { Reactor.Action.viewDidLoad } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + reactor.state + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.updateSnapshot( + recentSearchItems: state.recentSearchItems + .map(PopupSearchView.SectionItem.recentSearchItem), + categoryItems: state.categoryItems + .map(PopupSearchView.SectionItem.categoryItem), + searchResultItems: state.searchResultItems + .map(PopupSearchView.SectionItem.searchResultItem) + ) + } + .disposed(by: disposeBag) + + } } From 373f6163bad2b4966789254b8cd1f541ee9367fd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 21:59:54 +0900 Subject: [PATCH 244/393] =?UTF-8?q?fix/#131:=20snapShot=EC=9D=B4=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index c13dda9e..95c051c5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -252,13 +252,12 @@ extension PopupSearchView { } } - private func updateSnapshot( + func updateSnapshot( recentSearchItems: [SectionItem], categoryItems: [SectionItem], searchResultItems: [SectionItem] ) { - guard var snapshot = dataSource?.snapshot() else { return } - + var snapshot = NSDiffableDataSourceSnapshot() snapshot.appendSections(PopupSearchView.Section.allCases) snapshot.appendItems(recentSearchItems, toSection: .recentSearch) snapshot.appendItems(categoryItems, toSection: .category) From 1d90d081cae2c31df18dc06328d8b8ebf1a61327 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 22:25:54 +0900 Subject: [PATCH 245/393] =?UTF-8?q?add/#131:=20=ED=95=84=EC=9A=94=ED=95=9C?= =?UTF-8?q?=20=EC=97=90=EC=85=8B=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../icon_dropdown.imageset/Contents.json | 12 ++++++++++++ .../Assets.xcassets/icon_dropdown.imageset/solid.svg | 3 +++ 2 files changed, 15 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/solid.svg diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/Contents.json new file mode 100644 index 00000000..f64986bb --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "solid.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/solid.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/solid.svg new file mode 100644 index 00000000..ec4c794b --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_dropdown.imageset/solid.svg @@ -0,0 +1,3 @@ + + + From c185aef2d139c7f2ece4f1bc8c08e4468921f6f9 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 22:26:15 +0900 Subject: [PATCH 246/393] =?UTF-8?q?feat/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20=ED=97=A4=EB=8D=94=20=EB=B7=B0=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 --- .../View/PopupGridCollectionHeaderView.swift | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift new file mode 100644 index 00000000..3868b10f --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift @@ -0,0 +1,92 @@ +import UIKit + +import DesignSystem + +import SnapKit +import RxSwift +import Then + +final class PopupGridCollectionHeaderView: UICollectionReusableView { + // MARK: - Properties + + var disposeBag = DisposeBag() + + private let cellCountLabel = PPLabel(style: .regular, fontSize: 13).then { + $0.textColor = .g400 + } + + private let sortedTitleLabel = PPLabel(style: .regular, fontSize: 13) + + private let dropDownImageView = UIImageView().then { + $0.image = UIImage(named: "icon_dropdown") + $0.isUserInteractionEnabled = false + + } + + let sortedButton = UIButton() + + // MARK: - init + init() { + super.init(frame: .zero) + + self.addViews() + self.setupConstraints() + self.configureUI() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension PopupGridCollectionHeaderView { + func addViews() { + [cellCountLabel, sortedButton].forEach { + addSubview($0) + } + + [sortedTitleLabel, dropDownImageView].forEach { + sortedButton.addSubview($0) + } + } + + func setupConstraints() { + cellCountLabel.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.verticalEdges.equalToSuperview() + } + + sortedButton.snp.makeConstraints { make in + make.trailing.equalToSuperview() + make.verticalEdges.equalToSuperview() + } + + sortedTitleLabel.snp.makeConstraints { make in + make.leading.equalToSuperview() + make.verticalEdges.equalToSuperview() + } + + dropDownImageView.snp.makeConstraints { make in + make.verticalEdges.equalToSuperview() + make.width.equalTo(dropDownImageView.snp.height) + make.leading.equalTo(sortedTitleLabel.snp.trailing).offset(6) + make.trailing.equalToSuperview() + } + } + + func configureUI() { } +} + +extension PopupGridCollectionHeaderView: Inputable { + struct Input { + var count: Int64 + var sortedTitle: String? + } + + func injection(with input: Input) { + sortedTitleLabel.text = input.sortedTitle + cellCountLabel.text = "총 \(input.count)개" + } +} From 03cf353d82ba97e33f7a3f2abd1e8ba6bacfc29e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 3 May 2025 23:29:12 +0900 Subject: [PATCH 247/393] =?UTF-8?q?feat/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EA=B2=B0=EA=B3=BC=20=ED=97=A4=EB=8D=94=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 --- .../View/PopupGridCollectionHeaderView.swift | 17 +++- .../Source/View/PopupSearchView.swift | 86 ++++++++++++++----- .../Source/View/TagCollectionHeaderView.swift | 11 +-- 3 files changed, 80 insertions(+), 34 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift index 3868b10f..189776cf 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift @@ -7,6 +7,11 @@ import RxSwift import Then final class PopupGridCollectionHeaderView: UICollectionReusableView { + + enum Identifier: String { + case searchResult = "PopupGridCollectionHeaderView.searchResult" + } + // MARK: - Properties var disposeBag = DisposeBag() @@ -26,18 +31,24 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { let sortedButton = UIButton() // MARK: - init - init() { + override init(frame: CGRect) { super.init(frame: .zero) self.addViews() self.setupConstraints() - self.configureUI() + + self.backgroundColor = .blue } @available(*, unavailable) required init?(coder: NSCoder) { fatalError("\(#file), \(#function) Error") } + + override func prepareForReuse() { + super.prepareForReuse() + disposeBag = DisposeBag() + } } // MARK: - SetUp @@ -75,8 +86,6 @@ private extension PopupGridCollectionHeaderView { make.trailing.equalToSuperview() } } - - func configureUI() { } } extension PopupGridCollectionHeaderView: Inputable { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 95c051c5..bed6f1bd 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -16,9 +16,10 @@ final class PopupSearchView: UIView { case searchResultItem(PPPopupGridCollectionViewCell.Input) } - enum SectionHeaderKind: String, CaseIterable { + enum SectionHeaderKind: String { case recentSearch = "recentSearchElementKind" case category = "categoryElementKind" + case searchResult = "searchResultElementKind" } // MARK: - Properties @@ -40,13 +41,19 @@ final class PopupSearchView: UIView { $0.register( TagCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.recentSearch.rawValue, - withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.identifer + withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.rawValue ) $0.register( TagCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.category.rawValue, - withReuseIdentifier: TagCollectionHeaderView.Identifier.category.identifer + withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue + ) + + $0.register( + PopupGridCollectionHeaderView.self, + forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, + withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue ) } @@ -82,7 +89,7 @@ private extension PopupSearchView { } collectionView.snp.makeConstraints { make in - make.top.equalTo(searchBar.snp.bottom).offset(24) + make.top.equalTo(searchBar.snp.bottom) make.horizontalEdges.equalToSuperview() make.bottom.equalToSuperview() } @@ -104,7 +111,7 @@ private extension PopupSearchView { switch Section.allCases[sectionIndex] { case .recentSearch: return self.makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) case .category: return self.makeTagSectionLayout(SectionHeaderKind.category.rawValue) - case .searchResult: return self.makeSearchResultSectionLayout() + case .searchResult: return self.makeSearchResultSectionLayout(SectionHeaderKind.searchResult.rawValue) } } } @@ -133,12 +140,12 @@ private extension PopupSearchView { section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0) section.interGroupSpacing = 6 - section.boundarySupplementaryItems = [makeHeaderLayout(headerKind)] + section.boundarySupplementaryItems = [getHeaderViewLayoutWithContentInset(headerKind)] return section } - static func makeSearchResultSectionLayout() -> NSCollectionLayoutSection { + static func makeSearchResultSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.5), @@ -162,10 +169,12 @@ private extension PopupSearchView { section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 24 + section.boundarySupplementaryItems = [getHeaderViewLayoutWithContentInset(headerKind)] + return section } - static func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + static func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { // Header let headerSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -179,12 +188,45 @@ private extension PopupSearchView { return header } + + static func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(22) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + + return header + } + + static func getHeaderViewLayoutWithContentInset(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + switch SectionHeaderKind(rawValue: elementKind)! { + case .recentSearch: + let header = makeTagCollectionHeaderLayout(elementKind) + header.contentInsets = NSDirectionalEdgeInsets(top: 24, leading: 0, bottom: 16, trailing: 0) + return header + + case .category: + let header = makeTagCollectionHeaderLayout(elementKind) + header.contentInsets = NSDirectionalEdgeInsets(top: 48, leading: 0, bottom: 16, trailing: 0) + return header + + case .searchResult: + let header = makePopupGridCollectionHeaderLayout(elementKind) + header.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0) + return header + } + } } // MARK: - DataSource extension PopupSearchView { private func configurationDataSourceItem() { - print("HEADER DEBUG:", #function, #line, "data source is", self.dataSource == nil) self.dataSource = UICollectionViewDiffableDataSource( collectionView: collectionView ) { (collectionView, indexPath, item) -> UICollectionViewCell? in @@ -215,38 +257,40 @@ extension PopupSearchView { } } - print("HEADER DEBUG:", #function, #line, "data source is", self.dataSource == nil) - self.collectionView.dataSource = self.dataSource } private func configureDataSourceHeader() { - print("HEADER DEBUG:", #function, #line) dataSource?.supplementaryViewProvider = { (collectionView, elementKind, indexPath) -> UICollectionReusableView? in - - print("HEADER DEBUG:", #function, #line, "elementKind is", elementKind) - switch SectionHeaderKind(rawValue: elementKind)! { case .recentSearch: - print("HEADER DEBUG:", #function, #line) guard let header = collectionView.dequeueReusableSupplementaryView( ofKind: elementKind, - withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.identifer, + withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.rawValue, for: indexPath - ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error")} + ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } header.setupHeader(title: "최근 검색어", buttonTitle: "모두삭제") return header case .category: - print("HEADER DEBUG:", #function, #line) guard let header = collectionView.dequeueReusableSupplementaryView( ofKind: elementKind, - withReuseIdentifier: TagCollectionHeaderView.Identifier.category.identifer, + withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue, for: indexPath - ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error")} + ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } header.setupHeader(title: "팝업스토어 찾기") + return header + + case .searchResult: + guard let header = collectionView.dequeueReusableSupplementaryView( + ofKind: elementKind, + withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue, + for: indexPath + ) as? PopupGridCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } + header.injection(with: PopupGridCollectionHeaderView.Input(count: 0, sortedTitle: "Hello")) + return header } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index f7686537..33891703 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -8,15 +8,8 @@ import SnapKit final class TagCollectionHeaderView: UICollectionReusableView { enum Identifier: String { - var identifer: String { - switch self { - case .recentSearch: return "TagCollectionHeaderView.recentSearch" - case .category: return "TagCollectionHeaderView.category" - } - } - - case recentSearch - case category + case recentSearch = "TagCollectionHeaderView.recentSearch" + case category = "TagCollectionHeaderView.category" } // MARK: - Components From cd9c91f2ae11655f87c884cae6b2571b390b3415 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 02:47:55 +0900 Subject: [PATCH 248/393] =?UTF-8?q?feat/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=84=A0=ED=83=9D=20=EB=AA=A8=EB=8B=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/Model/Category.swift | 28 +++++ .../Reactor/SearchCategoryReactor.swift | 95 ++++++++++++++ .../View/SearchCategoryController.swift | 119 ++++++++++++++++++ .../Source/View/SearchCategoryView.swift | 113 +++++++++++++++++ .../icon_xmark.imageset/Contents.json | 12 ++ .../icon_xmark.imageset/line.svg | 4 + 6 files changed, 371 insertions(+) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/line.svg diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift new file mode 100644 index 00000000..75070834 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -0,0 +1,28 @@ +import Foundation + +final class Category: NSCopying, Equatable { + static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } + + var items: [TagCollectionViewCell.Input] + + init(items: [TagCollectionViewCell.Input] = []) { + self.items = items + } + + func contains(id: Int64) -> Bool { + items.contains { $0.id == id } + } + + func copy(with zone: NSZone? = nil) -> Any { + return Category(items: self.items) + } + + func toggleItemSelection(by categoryID: Int64) { + guard let index = items.firstIndex(where: { $0.id == categoryID }) else { return } + items[index].isSelected.toggle() + } + + func turnOffAllItemSelection() { + for index in items.indices { items[index].isSelected = false } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift new file mode 100644 index 00000000..a9a3c3f8 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift @@ -0,0 +1,95 @@ +import Foundation + +import DomainInterface +import DesignSystem + +import ReactorKit +import RxCocoa +import RxSwift + +final class SearchCategoryReactor: Reactor { + + // MARK: - Reactor + enum Action { + case viewWillAppear + case resetButtonTapped + case saveButtonTapped + case cellTapped(categoryID: Int64) + } + + enum Mutation { + case setupCategotyTag(items: [TagCollectionViewCell.Input]) + case resetCategory + case saveCategory + case toggleTappedCell(categoryID: Int64) + } + + struct State { + var category: Category + var saveButtonIsEnable: Bool = false + } + + // MARK: - properties + + var initialState: State + var disposeBag = DisposeBag() + + let originCategory: Category + private let signUpAPIUseCase: SignUpAPIUseCase + + // MARK: - init + init( + originCategory: Category, + signUpAPIUseCase: SignUpAPIUseCase + ) { + self.initialState = State(category: originCategory.copy() as! Category) + self.originCategory = originCategory + self.signUpAPIUseCase = signUpAPIUseCase + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { + case .viewWillAppear: + return signUpAPIUseCase.fetchCategoryList() + .withUnretained(self) + .map { (owner, response) in + let items = response.map { + let isSelected = owner.originCategory.contains(id: $0.categoryId) + return TagCollectionViewCell.Input(title: $0.category, id: $0.categoryId, isSelected: isSelected) + } + return .setupCategotyTag(items: items) + } + + case .resetButtonTapped: + return Observable.just(.resetCategory) + + case .saveButtonTapped: + return Observable.just(.saveCategory) + + case .cellTapped(let categoryID): + return Observable.just(.toggleTappedCell(categoryID: categoryID)) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .setupCategotyTag(let items): + newState.category.items = items + + case .resetCategory: + newState.category.turnOffAllItemSelection() + + case .saveCategory: + self.originCategory.items = newState.category.items + + case .toggleTappedCell(let categoryID): + newState.category.toggleItemSelection(by: categoryID) + } + + newState.saveButtonIsEnable = (originCategory != newState.category) + return newState + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift new file mode 100644 index 00000000..46aa82fa --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift @@ -0,0 +1,119 @@ +import UIKit + +import DesignSystem + +import PanModal +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit + +final class SearchCategoryController: BaseViewController, View { + + typealias Reactor = SearchCategoryReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + + private var mainView = SearchCategoryView() +} + +// MARK: - Life Cycle +extension SearchCategoryController { + override func loadView() { + self.view = mainView + } + + override func viewDidLoad() { + super.viewDidLoad() + setUp() + } +} + +// MARK: - SetUp +private extension SearchCategoryController { + func setUp() { + mainView.snp.makeConstraints { make in + make.edges.equalTo(view.safeAreaLayoutGuide) + } + } +} + +// MARK: - Methods +extension SearchCategoryController { + func bind(reactor: Reactor) { + rx.viewWillAppear + .map { Reactor.Action.viewWillAppear } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + reactor.state + .map { $0.category.items } + .distinctUntilChanged() + .bind(to: mainView.collectionView.rx.items( + cellIdentifier: TagCollectionViewCell.identifiers, + cellType: TagCollectionViewCell.self + )) { index, input, cell in + cell.injection(with: input) + } + .disposed(by: disposeBag) + + mainView.collectionView.rx.itemSelected + .withLatestFrom( + reactor.state.map { Array($0.category.items) }, + resultSelector: { indexPath, items in items[indexPath.item] } + ) + .compactMap { $0.id } + .map { Reactor.Action.cellTapped(categoryID: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.closeButton.rx.tap + .withUnretained(self) + .subscribe { (owner, _) in owner.dismiss(animated: true) } + .disposed(by: disposeBag) + + + mainView.resetButton.rx.tap + .withUnretained(self) + .do { (owner, _) in owner.dismiss(animated: true) } + .map { (owner, _) in Reactor.Action.resetButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.saveButton.rx.tap + .withUnretained(self) + .do { (owner, _) in owner.dismiss(animated: true) } + .map { (owner, _) in Reactor.Action.saveButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + + reactor.state + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable + // owner.mainView.collectionView.reloadItems + } + .disposed(by: disposeBag) + } +} + +// MARK: - PanModalPresentable +extension SearchCategoryController: PanModalPresentable { + var panScrollable: UIScrollView? { + return nil + } + var longFormHeight: PanModalHeight { + return .intrinsicHeight + } + var shortFormHeight: PanModalHeight { + return .intrinsicHeight + } + var showDragIndicator: Bool { + return false + } + var cornerRadius: CGFloat { + return 20 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift new file mode 100644 index 00000000..3c7c2f3d --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift @@ -0,0 +1,113 @@ +import UIKit + +import DesignSystem + +import SnapKit +import Then + +final class SearchCategoryView: UIView { + + // MARK: - Components + private let titleLabel: PPLabel = { + return PPLabel(style: .bold, fontSize: 18, text: "카테고리를 선택해주세요") + }() + + let closeButton = UIButton().then { + $0.setImage(UIImage(named: "icon_xmark"), for: .normal) + } + + let collectionView = UICollectionView(frame: .zero, collectionViewLayout: SearchCategoryView.makeLayout()).then { + $0.isScrollEnabled = false + + $0.register( + TagCollectionViewCell.self, + forCellWithReuseIdentifier: TagCollectionViewCell.identifiers + ) + } + + let buttonStackView = UIStackView().then { + $0.distribution = .fillEqually + $0.spacing = 12 + } + + let resetButton = PPButton(style: .secondary, text: "초기화") + + let saveButton = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") + + // MARK: - init + init() { + super.init(frame: .zero) + + self.addViews() + self.setupConstraints() + } + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension SearchCategoryView { + + func addViews() { + [titleLabel, closeButton, collectionView, buttonStackView].forEach { + self.addSubview($0) + } + + [resetButton, saveButton].forEach { + buttonStackView.addArrangedSubview($0) + } + } + + func setupConstraints() { + titleLabel.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(20) + make.top.equalToSuperview().inset(12) + } + + closeButton.snp.makeConstraints { make in + make.size.equalTo(24) + make.trailing.equalToSuperview().inset(20) + make.centerY.equalTo(titleLabel) + } + + collectionView.snp.makeConstraints { make in + make.leading.trailing.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(10) + make.height.equalTo(195) + } + + buttonStackView.snp.makeConstraints { make in + make.top.equalTo(collectionView.snp.bottom).offset(32) + make.leading.trailing.equalToSuperview().inset(20) + make.height.equalTo(50) + make.bottom.equalToSuperview() + } + } +} + +private extension SearchCategoryView { + + static func makeLayout() -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout { sectionIndex, environment in + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(26), + heightDimension: .absolute(36) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(1000) + ) + let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) + group.interItemSpacing = .fixed(12) + + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 16 + + return section + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/Contents.json new file mode 100644 index 00000000..d86eb988 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "line.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/line.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/line.svg new file mode 100644 index 00000000..c95be2c3 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark.imageset/line.svg @@ -0,0 +1,4 @@ + + + + From 96923efd9ae86b4349198ded0e852a5cc0f05f21 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 4 May 2025 14:27:20 +0900 Subject: [PATCH 249/393] =?UTF-8?q?fix/#132:=20=EC=83=90=20=EC=9E=AC?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EB=B0=A9=EC=96=B4=20=EB=B0=8F=20=EC=BA=90?= =?UTF-8?q?=EC=8B=B1=20=ED=9E=88=ED=8A=B8=20=EC=8B=9C=EC=A0=90=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/UIImageView+.swift | 131 ++++++++++++++++-- 1 file changed, 117 insertions(+), 14 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift index cacd212c..f526f8a1 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift @@ -1,34 +1,137 @@ +import ObjectiveC import UIKit +private var currentURLKey: UInt8 = 0 +private var placeholderImageKey: UInt8 = 0 + public extension UIImageView { + + private var currentImageURL: String? { + get { objc_getAssociatedObject(self, ¤tURLKey) as? String } + set { objc_setAssociatedObject(self, ¤tURLKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + + private var placeholderImage: UIImage? { + get { objc_getAssociatedObject(self, &placeholderImageKey) as? UIImage } + set { objc_setAssociatedObject(self, &placeholderImageKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } + func setPPImage(path: String?) { - guard let path = path else { - self.image = UIImage(named: "image_default") + // 기본 이미지 저장 + if placeholderImage == nil { + placeholderImage = UIImage(named: "image_default") + } + + guard let path = path, !path.isEmpty else { + image = placeholderImage + currentImageURL = nil return } + let imageURLString = Secrets.popPoolS3BaseURL + path - if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { - ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in - DispatchQueue.main.async { - self?.image = image + guard let encodedURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { + image = placeholderImage + currentImageURL = nil + return + } + + // 이미 같은 URL을 로딩했고 이미지가 있으면 재로딩 방지 + if currentImageURL == encodedURL && self.image != nil && self.image != placeholderImage { + return + } + + // 현재 이미지 URL을 업데이트 + currentImageURL = encodedURL + + // 먼저 메모리 캐시 확인 + if let cachedImage = MemoryStorage.shared.fetchImage(url: encodedURL) { + self.image = cachedImage + return + } + + // 다음으로 디스크 캐시 확인 + if let diskImage = DiskStorage.shared.fetchImage(url: encodedURL) { + MemoryStorage.shared.store(image: diskImage, url: encodedURL) + self.image = diskImage + return + } + + ImageLoader.shared.loadImage(with: encodedURL, defaultImage: placeholderImage, imageQuality: .origin) { [weak self] image in + guard let self else { return } + + DispatchQueue.main.async { + // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) + if self.currentImageURL == encodedURL { + if let image = image { + self.image = image + } else if self.image == nil { + // 이미지 로드 실패 시 기본 이미지 표시 + self.image = self.placeholderImage + } } } } } func setPPImage(path: String?, completion: @escaping () -> Void) { - guard let path = path else { - self.image = UIImage(named: "image_default") + // 기본 이미지 저장 + if placeholderImage == nil { + placeholderImage = UIImage(named: "image_default") + } + + guard let path = path, !path.isEmpty else { + image = placeholderImage + currentImageURL = nil completion() return } + let imageURLString = Secrets.popPoolS3BaseURL + path - if let cenvertimageURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) { - let imageURL = URL(string: cenvertimageURL) - ImageLoader.shared.loadImage(with: cenvertimageURL, defaultImage: UIImage(named: "image_default"), imageQuality: .origin) { [weak self] image in - DispatchQueue.main.async { - completion() - self?.image = image + guard let encodedURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { + image = placeholderImage + currentImageURL = nil + completion() + return + } + + // 이미 같은 URL을 로딩했고 이미지가 있으면 재로딩 방지 + if currentImageURL == encodedURL && self.image != nil && self.image != placeholderImage { + completion() + return + } + + // 현재 이미지 URL을 업데이트 + currentImageURL = encodedURL + + // 먼저 메모리 캐시 확인 + if let cachedImage = MemoryStorage.shared.fetchImage(url: encodedURL) { + self.image = cachedImage + completion() + return + } + + // 다음으로 디스크 캐시 확인 + if let diskImage = DiskStorage.shared.fetchImage(url: encodedURL) { + MemoryStorage.shared.store(image: diskImage, url: encodedURL) + self.image = diskImage + completion() + return + } + + ImageLoader.shared.loadImage(with: encodedURL, defaultImage: placeholderImage, imageQuality: .origin) { [weak self] image in + DispatchQueue.main.async { + defer { completion() } + + guard let self else { return } + + // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) + if self.currentImageURL == encodedURL { + if let image = image { + self.image = image + } else if self.image == nil { + // 이미지 로드 실패 시 기본 이미지 표시 + self.image = self.placeholderImage + } } } } From 6f8adc76f41ab1a9a02d85924c24f43096297462 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 14:17:13 +0900 Subject: [PATCH 250/393] =?UTF-8?q?fix/#131:=20=EA=B2=80=EC=83=89=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 최근 검색어가 없을때 사라지도록 함 - 화면이 항상 24 크기만큼 떨어지고 시작하도록 함 - 사이사이 빈 영역 잡아주기 --- .../View/PopupGridCollectionHeaderView.swift | 6 +- .../Source/View/PopupSearchView.swift | 90 ++++++++++--------- .../Source/View/TagCollectionHeaderView.swift | 2 +- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift index 189776cf..253c45af 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift @@ -66,12 +66,14 @@ private extension PopupGridCollectionHeaderView { func setupConstraints() { cellCountLabel.snp.makeConstraints { make in make.leading.equalToSuperview() - make.verticalEdges.equalToSuperview() + make.height.equalTo(22) + make.centerY.equalToSuperview() } sortedButton.snp.makeConstraints { make in make.trailing.equalToSuperview() - make.verticalEdges.equalToSuperview() + make.height.equalTo(22) + make.centerY.equalToSuperview() } sortedTitleLabel.snp.makeConstraints { make in diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index bed6f1bd..8aa905b1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -24,10 +24,9 @@ final class PopupSearchView: UIView { // MARK: - Properties let searchBar = PPSearchBarView() - let collectionView = UICollectionView( - frame: .zero, - collectionViewLayout: PopupSearchView.makeLayout() - ).then { + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { + $0.setCollectionViewLayout(self.makeLayout(), animated: false) + $0.register( TagCollectionViewCell.self, forCellWithReuseIdentifier: TagCollectionViewCell.identifiers @@ -49,12 +48,16 @@ final class PopupSearchView: UIView { forSupplementaryViewOfKind: SectionHeaderKind.category.rawValue, withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue ) - $0.register( PopupGridCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue ) + $0.backgroundColor = UIColor.green + + // UICollectionView 최 상단 빈 영역 + $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 0, right: 0) + $0.contentInsetAdjustmentBehavior = .never } private var dataSource: UICollectionViewDiffableDataSource? @@ -106,17 +109,23 @@ private extension PopupSearchView { // MARK: - Layout private extension PopupSearchView { - static func makeLayout() -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout { sectionIndex, environment in - switch Section.allCases[sectionIndex] { - case .recentSearch: return self.makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) - case .category: return self.makeTagSectionLayout(SectionHeaderKind.category.rawValue) - case .searchResult: return self.makeSearchResultSectionLayout(SectionHeaderKind.searchResult.rawValue) + private func makeLayout() -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout { [weak self] sectionIndex, environment in + guard let self else { return nil } + let sections = self.dataSource?.snapshot().sectionIdentifiers ?? [] + guard sectionIndex < sections.count else { return nil } + switch sections[sectionIndex] { + case .recentSearch: + return makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) + case .category: + return makeTagSectionLayout(SectionHeaderKind.category.rawValue) + case .searchResult: + return makeSearchResultSectionLayout(SectionHeaderKind.searchResult.rawValue) } } } - static func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { + func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(100), @@ -137,15 +146,21 @@ private extension PopupSearchView { // Section let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous - section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 0) + + if headerKind == SectionHeaderKind.recentSearch.rawValue { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 0) + } else { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 0) + } + section.interGroupSpacing = 6 - section.boundarySupplementaryItems = [getHeaderViewLayoutWithContentInset(headerKind)] + section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] return section } - static func makeSearchResultSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { + func makeSearchResultSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(0.5), @@ -166,15 +181,15 @@ private extension PopupSearchView { // Section let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 24 - section.boundarySupplementaryItems = [getHeaderViewLayoutWithContentInset(headerKind)] + section.boundarySupplementaryItems = [makePopupGridCollectionHeaderLayout(headerKind)] return section } - static func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { // Header let headerSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -189,7 +204,7 @@ private extension PopupSearchView { return header } - static func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { // Header let headerSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -203,25 +218,6 @@ private extension PopupSearchView { return header } - - static func getHeaderViewLayoutWithContentInset(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - switch SectionHeaderKind(rawValue: elementKind)! { - case .recentSearch: - let header = makeTagCollectionHeaderLayout(elementKind) - header.contentInsets = NSDirectionalEdgeInsets(top: 24, leading: 0, bottom: 16, trailing: 0) - return header - - case .category: - let header = makeTagCollectionHeaderLayout(elementKind) - header.contentInsets = NSDirectionalEdgeInsets(top: 48, leading: 0, bottom: 16, trailing: 0) - return header - - case .searchResult: - let header = makePopupGridCollectionHeaderLayout(elementKind) - header.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0) - return header - } - } } // MARK: - DataSource @@ -302,10 +298,20 @@ extension PopupSearchView { searchResultItems: [SectionItem] ) { var snapshot = NSDiffableDataSourceSnapshot() - snapshot.appendSections(PopupSearchView.Section.allCases) - snapshot.appendItems(recentSearchItems, toSection: .recentSearch) - snapshot.appendItems(categoryItems, toSection: .category) - snapshot.appendItems(searchResultItems, toSection: .searchResult) + if !recentSearchItems.isEmpty { + snapshot.appendSections([PopupSearchView.Section.recentSearch]) + snapshot.appendItems(recentSearchItems, toSection: .recentSearch) + } + + if !categoryItems.isEmpty { + snapshot.appendSections([PopupSearchView.Section.category]) + snapshot.appendItems(categoryItems, toSection: .category) + } + + if !searchResultItems.isEmpty { + snapshot.appendSections([PopupSearchView.Section.searchResult]) + snapshot.appendItems(searchResultItems, toSection: .searchResult) + } dataSource?.apply(snapshot, animatingDifferences: true) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index 33891703..1777e985 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -60,8 +60,8 @@ private extension TagCollectionHeaderView { } titleButton.snp.makeConstraints { make in - make.centerY.equalToSuperview() make.trailing.equalToSuperview() + make.centerY.equalTo(sectionTitleLabel) make.height.equalTo(20) } } From bd9310e37d3c3c7b1d9ddcfda3e82ba35ec6210d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 14:33:32 +0900 Subject: [PATCH 251/393] =?UTF-8?q?fix/#131:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=EC=84=A0=ED=83=9D=20=ED=99=94=EB=A9=B4=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 17 +++++++++++++++++ .../Source/View/SearchCategoryController.swift | 10 ---------- .../Source/View/SearchCategoryView.swift | 17 +++++++++-------- 3 files changed, 26 insertions(+), 18 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index ac702aa8..45f84f65 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -25,6 +25,7 @@ 05EC286C2DC5FE5B00C761A5 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; }; 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC28E32DC696E000C761A5 /* PanModal */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -112,6 +113,7 @@ 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, + 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, @@ -202,6 +204,7 @@ 05EC23402DC49A8B00C761A5 /* ReactorKit */, 05EC234A2DC49AB400C761A5 /* Then */, 05EC234D2DC49AC100C761A5 /* SnapKit */, + 05EC28E32DC696E000C761A5 /* PanModal */, ); productName = SearchFeature; productReference = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; @@ -264,6 +267,7 @@ 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */, 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */, 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */, + 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */, ); preferredProjectObjectVersion = 77; productRefGroup = 0516336E2DC457A900A6C0D1 /* Products */; @@ -664,6 +668,14 @@ minimumVersion = 5.7.1; }; }; + 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/slackhq/PanModal"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.7; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -692,6 +704,11 @@ package = 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; + 05EC28E32DC696E000C761A5 /* PanModal */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */; + productName = PanModal; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 051633642DC457A900A6C0D1 /* Project object */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift index 46aa82fa..b1830c9a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift @@ -26,16 +26,6 @@ extension SearchCategoryController { override func viewDidLoad() { super.viewDidLoad() - setUp() - } -} - -// MARK: - SetUp -private extension SearchCategoryController { - func setUp() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift index 3c7c2f3d..1825f612 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift @@ -16,7 +16,8 @@ final class SearchCategoryView: UIView { $0.setImage(UIImage(named: "icon_xmark"), for: .normal) } - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: SearchCategoryView.makeLayout()).then { + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { + $0.setCollectionViewLayout(makeLayout(), animated: false) $0.isScrollEnabled = false $0.register( @@ -62,7 +63,7 @@ private extension SearchCategoryView { func setupConstraints() { titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) - make.top.equalToSuperview().inset(12) + make.top.equalToSuperview().inset(32) } closeButton.snp.makeConstraints { make in @@ -72,23 +73,23 @@ private extension SearchCategoryView { } collectionView.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview() - make.top.equalTo(titleLabel.snp.bottom).offset(10) + make.horizontalEdges.equalToSuperview() + make.top.equalTo(titleLabel.snp.bottom).offset(24) make.height.equalTo(195) } buttonStackView.snp.makeConstraints { make in - make.top.equalTo(collectionView.snp.bottom).offset(32) + make.top.equalTo(collectionView.snp.bottom).offset(24) make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - make.bottom.equalToSuperview() + make.height.equalTo(52) + make.bottom.equalTo(self.safeAreaLayoutGuide) } } } private extension SearchCategoryView { - static func makeLayout() -> UICollectionViewLayout { + func makeLayout() -> UICollectionViewLayout { return UICollectionViewCompositionalLayout { sectionIndex, environment in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), From 5eaa06d82929029a57769ca21094f23680300796 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 4 May 2025 14:36:49 +0900 Subject: [PATCH 252/393] =?UTF-8?q?fix/#132:=20=EB=B0=98=EB=B3=B5=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EB=A9=94=EC=84=9C=EB=93=9C=EB=A1=9C=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/UIImageView+.swift | 76 ++++--------------- 1 file changed, 13 insertions(+), 63 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift index f526f8a1..cfe9e1f8 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift @@ -17,72 +17,24 @@ public extension UIImageView { } func setPPImage(path: String?) { - // 기본 이미지 저장 - if placeholderImage == nil { - placeholderImage = UIImage(named: "image_default") - } - - guard let path = path, !path.isEmpty else { - image = placeholderImage - currentImageURL = nil - return - } - - let imageURLString = Secrets.popPoolS3BaseURL + path - guard let encodedURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { - image = placeholderImage - currentImageURL = nil - return - } - - // 이미 같은 URL을 로딩했고 이미지가 있으면 재로딩 방지 - if currentImageURL == encodedURL && self.image != nil && self.image != placeholderImage { - return - } - - // 현재 이미지 URL을 업데이트 - currentImageURL = encodedURL - - // 먼저 메모리 캐시 확인 - if let cachedImage = MemoryStorage.shared.fetchImage(url: encodedURL) { - self.image = cachedImage - return - } - - // 다음으로 디스크 캐시 확인 - if let diskImage = DiskStorage.shared.fetchImage(url: encodedURL) { - MemoryStorage.shared.store(image: diskImage, url: encodedURL) - self.image = diskImage - return - } - - ImageLoader.shared.loadImage(with: encodedURL, defaultImage: placeholderImage, imageQuality: .origin) { [weak self] image in - guard let self else { return } - - DispatchQueue.main.async { - // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) - if self.currentImageURL == encodedURL { - if let image = image { - self.image = image - } else if self.image == nil { - // 이미지 로드 실패 시 기본 이미지 표시 - self.image = self.placeholderImage - } - } - } - } + loadImageFromImageLoader(path: path, completion: nil) } func setPPImage(path: String?, completion: @escaping () -> Void) { + loadImageFromImageLoader(path: path, completion: completion) + } + + func loadImageFromImageLoader(path: String?, completion: (() -> Void)? = nil) { // 기본 이미지 저장 if placeholderImage == nil { placeholderImage = UIImage(named: "image_default") + completion?() } guard let path = path, !path.isEmpty else { image = placeholderImage currentImageURL = nil - completion() + completion?() return } @@ -90,13 +42,13 @@ public extension UIImageView { guard let encodedURL = imageURLString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { image = placeholderImage currentImageURL = nil - completion() + completion?() return } // 이미 같은 URL을 로딩했고 이미지가 있으면 재로딩 방지 if currentImageURL == encodedURL && self.image != nil && self.image != placeholderImage { - completion() + completion?() return } @@ -106,7 +58,7 @@ public extension UIImageView { // 먼저 메모리 캐시 확인 if let cachedImage = MemoryStorage.shared.fetchImage(url: encodedURL) { self.image = cachedImage - completion() + completion?() return } @@ -114,16 +66,14 @@ public extension UIImageView { if let diskImage = DiskStorage.shared.fetchImage(url: encodedURL) { MemoryStorage.shared.store(image: diskImage, url: encodedURL) self.image = diskImage - completion() + completion?() return } ImageLoader.shared.loadImage(with: encodedURL, defaultImage: placeholderImage, imageQuality: .origin) { [weak self] image in + guard let self else { return } + defer { completion?() } DispatchQueue.main.async { - defer { completion() } - - guard let self else { return } - // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) if self.currentImageURL == encodedURL { if let image = image { From 5ba98dd1a3bd0f54aa099fec02f5de2bcdf2917c Mon Sep 17 00:00:00 2001 From: JunYoung Date: Sun, 4 May 2025 14:38:42 +0900 Subject: [PATCH 253/393] =?UTF-8?q?style/#132:=20lint=20=EB=A3=B0=EC=97=90?= =?UTF-8?q?=20=EB=A7=9E=EA=B2=8C=20=EC=8A=A4=ED=83=80=EC=9D=BC=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/Infrastructure/Extension/UIImageView+.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift index cfe9e1f8..b1c65770 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift @@ -23,7 +23,7 @@ public extension UIImageView { func setPPImage(path: String?, completion: @escaping () -> Void) { loadImageFromImageLoader(path: path, completion: completion) } - + func loadImageFromImageLoader(path: String?, completion: (() -> Void)? = nil) { // 기본 이미지 저장 if placeholderImage == nil { From ffc349f34a8e95dad42324ce886e82681462e1f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 16:39:25 +0900 Subject: [PATCH 254/393] =?UTF-8?q?refactor/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=ED=97=A4=EB=8D=94=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Search/BeforeSearch/SearchReactor.swift | 3 +- .../SearchFeature/Source/Model/Category.swift | 6 +- .../Source/Model/PopupStatus.swift | 55 +++++++++++++++++++ .../Source/Reactor/PopupSearchReactor.swift | 35 +++++++----- .../View/PopupGridCollectionHeaderView.swift | 22 ++++---- .../Source/View/PopupSearchView.swift | 17 +++++- .../View/PopupSearchViewController.swift | 24 +++++++- 7 files changed, 127 insertions(+), 35 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 87cc6fc1..14f3a94a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -293,7 +293,8 @@ final class SearchReactor: Reactor { func setSearchList() { let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - recentKeywordSection.inputDataList = searchList.map { return .init(title: $0) } + recentKeywordSection.inputDataList = searchList.map { return + CancelableTagSectionCell.Input(title: $0) } } func appendSearchList(text: String?) { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index 75070834..37b399d9 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -1,7 +1,7 @@ import Foundation -final class Category: NSCopying, Equatable { - static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } +public final class Category: NSCopying, Equatable { + public static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } var items: [TagCollectionViewCell.Input] @@ -13,7 +13,7 @@ final class Category: NSCopying, Equatable { items.contains { $0.id == id } } - func copy(with zone: NSZone? = nil) -> Any { + public func copy(with zone: NSZone? = nil) -> Any { return Category(items: self.items) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift new file mode 100644 index 00000000..4c837492 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift @@ -0,0 +1,55 @@ +import Foundation + +/// 팝업 상점이 현재 열려 있는지 또는 닫혀 있는지 여부를 나타냅니다 +enum PopupStatus: CaseIterable { + case open + case closed + + /// UI 용 문자열 표시 (예 : 세그먼트 제목) + var title: String { + switch self { + case .open: return "오픈" + case .closed: return "종료" + } + } + + /// API 요청에 포함 할 값 + var requestValue: Bool { + switch self { + case .open: return true + case .closed: return false + } + } + + /// UISegmentedControl과 같은 UI 구성 요소의 색인 + var index: Int { + return Self.allCases.firstIndex(of: self)! + } +} + +/// 팝업 검색 결과를위한 정렬 옵션을 나타냅니다 +enum PopupSortOption: CaseIterable { + case newest + case popularity + + /// UI 용 문자열 표시 (예 : 세그먼트 제목) + var title: String { + switch self { + case .newest: return "신규순" + case .popularity: return "인기순" + } + } + + /// API 요청에 포함 할 값 + var requestValue: String { + switch self { + case .newest: return "NEWEST" + case .popularity: return "MOST_VIEWED,MOST_COMMENTED,MOST_BOOKMARKED" + } + } + + /// UISegmentedControl과 같은 UI 구성 요소의 색인 + var index: Int { + return Self.allCases.firstIndex(of: self)! + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 42f90b59..330fea29 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -17,7 +17,7 @@ public final class PopupSearchReactor: Reactor { public enum Mutation { case setInitialState( recentSearch: [TagCollectionViewCell.Input], - category: [TagCollectionViewCell.Input], + categoryItems: [TagCollectionViewCell.Input], results: [PPPopupGridCollectionViewCell.Input] ) } @@ -26,6 +26,8 @@ public final class PopupSearchReactor: Reactor { var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] + var openTitle: String = PopupStatus.open.title + var sortOptionTitle: String = PopupSortOption.newest.title } // MARK: - properties @@ -36,30 +38,34 @@ public final class PopupSearchReactor: Reactor { private let userDefaultService = UserDefaultService() private let useCase: PopUpAPIUseCase + public let sourceOfTruthCategory: Category = Category( + items: [TagCollectionViewCell.Input(title: "카테고리", isSelected: false, isCancelable: false)] + ) + // MARK: - init public init(useCase: PopUpAPIUseCase) { self.useCase = useCase - self.initialState = State() + self.initialState = State(categoryItems: self.sourceOfTruthCategory.items) } // MARK: - Reactor Methods public func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - print("ViewDidLoad") return useCase.getSearchBottomPopUpList( - isOpen: true, + isOpen: PopupStatus.open.requestValue, categories: [], page: 0, size: 10, - sort: "NEWEST" + sort: PopupSortOption.newest.requestValue ) .withUnretained(self) .map { owner, response in return .setInitialState( - recentSearch: [], - category: [], - results: owner.convertResponseToSearchResultInput(response: response)) + recentSearch: owner.getRecentSearchKeywords(), + categoryItems: owner.sourceOfTruthCategory.items, + results: owner.convertResponseToSearchResultInput(response: response) + ) } } } @@ -67,10 +73,10 @@ public final class PopupSearchReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case let .setInitialState(recentSearch, category, results): - newState.recentSearchItems = recentSearch - newState.categoryItems = category - newState.searchResultItems = results + case let .setInitialState(recentSearchItems, categoryItems, searchResultItems): + newState.recentSearchItems = recentSearchItems + newState.categoryItems = categoryItems + newState.searchResultItems = searchResultItems } return newState } @@ -78,8 +84,9 @@ public final class PopupSearchReactor: Reactor { // MARK: - Functions private extension PopupSearchReactor { - func getRecentSearchKeywords() -> [String] { - return userDefaultService.fetchArray(key: "searchList") ?? [] + func getRecentSearchKeywords() -> [TagCollectionViewCell.Input] { + let searchKeywords = userDefaultService.fetchArray(key: "searchList") ?? [] + return searchKeywords.map { TagCollectionViewCell.Input(title: $0) } } func convertResponseToSearchResultInput(response: GetSearchBottomPopUpListResponse) -> [PPPopupGridCollectionViewCell.Input] { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift index 253c45af..924e9229 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift @@ -20,7 +20,7 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { $0.textColor = .g400 } - private let sortedTitleLabel = PPLabel(style: .regular, fontSize: 13) + private let filterOptionLabel = PPLabel(style: .regular, fontSize: 13) private let dropDownImageView = UIImageView().then { $0.image = UIImage(named: "icon_dropdown") @@ -28,7 +28,7 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { } - let sortedButton = UIButton() + let filterOptionButton = UIButton() // MARK: - init override init(frame: CGRect) { @@ -54,12 +54,12 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { // MARK: - SetUp private extension PopupGridCollectionHeaderView { func addViews() { - [cellCountLabel, sortedButton].forEach { + [cellCountLabel, filterOptionButton].forEach { addSubview($0) } - [sortedTitleLabel, dropDownImageView].forEach { - sortedButton.addSubview($0) + [filterOptionLabel, dropDownImageView].forEach { + filterOptionButton.addSubview($0) } } @@ -70,21 +70,21 @@ private extension PopupGridCollectionHeaderView { make.centerY.equalToSuperview() } - sortedButton.snp.makeConstraints { make in + filterOptionButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.height.equalTo(22) make.centerY.equalToSuperview() } - sortedTitleLabel.snp.makeConstraints { make in + filterOptionLabel.snp.makeConstraints { make in make.leading.equalToSuperview() - make.verticalEdges.equalToSuperview() + make.centerY.equalToSuperview() } dropDownImageView.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.width.equalTo(dropDownImageView.snp.height) - make.leading.equalTo(sortedTitleLabel.snp.trailing).offset(6) + make.leading.equalTo(filterOptionLabel.snp.trailing).offset(6) make.trailing.equalToSuperview() } } @@ -92,12 +92,12 @@ private extension PopupGridCollectionHeaderView { extension PopupGridCollectionHeaderView: Inputable { struct Input { - var count: Int64 + var count: Int var sortedTitle: String? } func injection(with input: Input) { - sortedTitleLabel.text = input.sortedTitle cellCountLabel.text = "총 \(input.count)개" + filterOptionLabel.text = input.sortedTitle } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 8aa905b1..3775df37 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -61,6 +61,7 @@ final class PopupSearchView: UIView { } private var dataSource: UICollectionViewDiffableDataSource? + private var popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? // MARK: - init init() { @@ -257,7 +258,8 @@ extension PopupSearchView { } private func configureDataSourceHeader() { - dataSource?.supplementaryViewProvider = { (collectionView, elementKind, indexPath) -> UICollectionReusableView? in + dataSource?.supplementaryViewProvider = { [weak self] (collectionView, elementKind, indexPath) -> UICollectionReusableView? in + guard let self else { return nil } switch SectionHeaderKind(rawValue: elementKind)! { case .recentSearch: guard let header = collectionView.dequeueReusableSupplementaryView( @@ -285,7 +287,9 @@ extension PopupSearchView { withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue, for: indexPath ) as? PopupGridCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } - header.injection(with: PopupGridCollectionHeaderView.Input(count: 0, sortedTitle: "Hello")) + if let input = self.popupGridCollectionHeaderInput { + header.injection(with: input) + } else { header.injection(with: PopupGridCollectionHeaderView.Input(count: 0, sortedTitle: "nil")) } return header } @@ -295,9 +299,15 @@ extension PopupSearchView { func updateSnapshot( recentSearchItems: [SectionItem], categoryItems: [SectionItem], - searchResultItems: [SectionItem] + searchResultItems: [SectionItem], + headerInput popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() + + if let input = popupGridCollectionHeaderInput { + self.popupGridCollectionHeaderInput = input + } + if !recentSearchItems.isEmpty { snapshot.appendSections([PopupSearchView.Section.recentSearch]) snapshot.appendItems(recentSearchItems, toSection: .recentSearch) @@ -311,6 +321,7 @@ extension PopupSearchView { if !searchResultItems.isEmpty { snapshot.appendSections([PopupSearchView.Section.searchResult]) snapshot.appendItems(searchResultItems, toSection: .searchResult) + snapshot.reloadSections([.searchResult]) } dataSource?.apply(snapshot, animatingDifferences: true) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index f5085cf7..25baa49a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -1,6 +1,8 @@ import UIKit import DesignSystem +import DomainInterface +import Infrastructure import ReactorKit import RxSwift @@ -34,6 +36,20 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.collectionView.rx.itemSelected + .subscribe(onNext: { [weak self, reactor] _ in + guard let self = self else { return } + let sharedCategory = reactor.sourceOfTruthCategory + let categoryReactor = SearchCategoryReactor( + originCategory: sharedCategory, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + let viewController = SearchCategoryController() + viewController.reactor = categoryReactor + self.presentPanModal(viewController) + }) + .disposed(by: disposeBag) + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -43,11 +59,13 @@ extension PopupSearchViewController { categoryItems: state.categoryItems .map(PopupSearchView.SectionItem.categoryItem), searchResultItems: state.searchResultItems - .map(PopupSearchView.SectionItem.searchResultItem) + .map(PopupSearchView.SectionItem.searchResultItem), + headerInput: PopupGridCollectionHeaderView.Input( + count: state.searchResultItems.count, + sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") + ) ) } .disposed(by: disposeBag) - - } } From 058046ceb3cf4bafff804dcc368f322eecc797e1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 16:54:49 +0900 Subject: [PATCH 255/393] =?UTF-8?q?refactor/#131:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Model/{PopupStatus.swift => FilterOption.swift} | 13 +++++++++++++ ...oryReactor.swift => CategorySelectReactor.swift} | 3 +-- ...hCategoryView.swift => CategorySelectView.swift} | 6 +++--- ...ler.swift => CategorySelectViewController.swift} | 12 ++++++------ .../Source/View/PopupSearchViewController.swift | 4 ++-- 5 files changed, 25 insertions(+), 13 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/{PopupStatus.swift => FilterOption.swift} (78%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/{SearchCategoryReactor.swift => CategorySelectReactor.swift} (97%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{SearchCategoryView.swift => CategorySelectView.swift} (96%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{SearchCategoryController.swift => CategorySelectViewController.swift} (90%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift similarity index 78% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift index 4c837492..884d3dee 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/PopupStatus.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift @@ -1,5 +1,18 @@ import Foundation +/// 필터 옵션 상태를 공유하기 위한 싱글톤 객체 +final class FilterOption { + static let shared = FilterOption(status: .open, sortOption: .newest) + + let status: PopupStatus + let sortOption: PopupSortOption + + private init(status: PopupStatus, sortOption: PopupSortOption) { + self.status = status + self.sortOption = sortOption + } +} + /// 팝업 상점이 현재 열려 있는지 또는 닫혀 있는지 여부를 나타냅니다 enum PopupStatus: CaseIterable { case open diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift similarity index 97% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index a9a3c3f8..414983f0 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/SearchCategoryReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -1,13 +1,12 @@ import Foundation import DomainInterface -import DesignSystem import ReactorKit import RxCocoa import RxSwift -final class SearchCategoryReactor: Reactor { +final class CategorySelectReactor: Reactor { // MARK: - Reactor enum Action { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift similarity index 96% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift index 1825f612..8dc3770b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift @@ -5,7 +5,7 @@ import DesignSystem import SnapKit import Then -final class SearchCategoryView: UIView { +final class CategorySelectView: UIView { // MARK: - Components private let titleLabel: PPLabel = { @@ -48,7 +48,7 @@ final class SearchCategoryView: UIView { } // MARK: - SetUp -private extension SearchCategoryView { +private extension CategorySelectView { func addViews() { [titleLabel, closeButton, collectionView, buttonStackView].forEach { @@ -87,7 +87,7 @@ private extension SearchCategoryView { } } -private extension SearchCategoryView { +private extension CategorySelectView { func makeLayout() -> UICollectionViewLayout { return UICollectionViewCompositionalLayout { sectionIndex, environment in diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift similarity index 90% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift index b1830c9a..46d47d5c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchCategoryController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift @@ -8,18 +8,18 @@ import RxCocoa import RxSwift import SnapKit -final class SearchCategoryController: BaseViewController, View { +final class CategorySelectViewController: BaseViewController, View { - typealias Reactor = SearchCategoryReactor + typealias Reactor = CategorySelectReactor // MARK: - Properties var disposeBag = DisposeBag() - private var mainView = SearchCategoryView() + private var mainView = CategorySelectView() } // MARK: - Life Cycle -extension SearchCategoryController { +extension CategorySelectViewController { override func loadView() { self.view = mainView } @@ -30,7 +30,7 @@ extension SearchCategoryController { } // MARK: - Methods -extension SearchCategoryController { +extension CategorySelectViewController { func bind(reactor: Reactor) { rx.viewWillAppear .map { Reactor.Action.viewWillAppear } @@ -90,7 +90,7 @@ extension SearchCategoryController { } // MARK: - PanModalPresentable -extension SearchCategoryController: PanModalPresentable { +extension CategorySelectViewController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 25baa49a..b12e0fa4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -40,11 +40,11 @@ extension PopupSearchViewController { .subscribe(onNext: { [weak self, reactor] _ in guard let self = self else { return } let sharedCategory = reactor.sourceOfTruthCategory - let categoryReactor = SearchCategoryReactor( + let categoryReactor = CategorySelectReactor( originCategory: sharedCategory, signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) ) - let viewController = SearchCategoryController() + let viewController = CategorySelectViewController() viewController.reactor = categoryReactor self.presentPanModal(viewController) }) From 7a2b4655e55d5bda684209af3a96b54e1dc994d3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 17:17:19 +0900 Subject: [PATCH 256/393] =?UTF-8?q?feat/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=ED=99=94=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Model/FilterOption.swift | 15 ++- .../Reactor/FilterOptionSelectReactor.swift | 71 ++++++++++++++ .../Source/View/FilterOptionSelectView.swift | 87 +++++++++++++++++ .../FilterOptionSelectViewController.swift | 95 +++++++++++++++++++ 4 files changed, 265 insertions(+), 3 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift index 884d3dee..e5630839 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift @@ -1,11 +1,20 @@ import Foundation /// 필터 옵션 상태를 공유하기 위한 싱글톤 객체 -final class FilterOption { +final class FilterOption: NSCopying, Equatable { + func copy(with zone: NSZone? = nil) -> Any { + return FilterOption( + status: self.status, + sortOption: self.sortOption + ) + } + + static func == (lhs: FilterOption, rhs: FilterOption) -> Bool { return lhs === rhs } + static let shared = FilterOption(status: .open, sortOption: .newest) - let status: PopupStatus - let sortOption: PopupSortOption + var status: PopupStatus + var sortOption: PopupSortOption private init(status: PopupStatus, sortOption: PopupSortOption) { self.status = status diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift new file mode 100644 index 00000000..02c2d661 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift @@ -0,0 +1,71 @@ +import Foundation + +import ReactorKit +import RxCocoa +import RxSwift + +final class FilterOptionSelectReactor: Reactor { + + // MARK: - Reactor + enum Action { + case changeStatus(status: PopupStatus) + case changeSortOption(sortOption: PopupSortOption) + case saveButtonTapped + } + + enum Mutation { + case changeStatus(status: PopupStatus) + case changeSortOption(sortOption: PopupSortOption) + case save + } + + struct State { + var selectedFilterOption: FilterOption + var saveButtonIsEnable: Bool = false + } + + // MARK: - properties + + var initialState: State + var disposeBag = DisposeBag() + private var originFilterOption: FilterOption + + // MARK: - init + init() { + self.initialState = State(selectedFilterOption: FilterOption.shared.copy() as! FilterOption) + } + + // MARK: - Reactor Methods + func mutate(action: Action) -> Observable { + switch action { + case .changeStatus(let status): + return Observable.just(.changeStatus(status: status)) + + case .changeSortOption(let filter): + return Observable.just(.changeSortOption(sortOption: filter)) + + case .saveButtonTapped: + return Observable.just(.save) + } + } + + func reduce(state: State, mutation: Mutation) -> State { + var newState = state + + switch mutation { + case .changeStatus(let status): + newState.selectedFilterOption.status = status + newState.saveButtonIsEnable = (newState.selectedFilterOption != FilterOption.shared) + + case .changeSortOption(let sortOption): + newState.selectedFilterOption.sortOption = sortOption + newState.saveButtonIsEnable = (newState.selectedFilterOption != FilterOption.shared) + + case .save: + FilterOption.shared.status = newState.selectedFilterOption.status + FilterOption.shared.sortOption = newState.selectedFilterOption.sortOption + } + + return newState + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift new file mode 100644 index 00000000..1b27697d --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift @@ -0,0 +1,87 @@ +import UIKit + +import DesignSystem + +import SnapKit + +final class FilterOptionSelectView: UIView { + + // MARK: - Components + private let titleLabel = PPLabel(style: .bold, fontSize: 18, text: "노출 순서를 선택해주세요") + + let closeButton = UIButton().then { + $0.setImage(UIImage(named: "icon_xmark"), for: .normal) + } + + private let statusLabel = PPLabel(style: .regular, fontSize: 13, text: "노출 조건") + + let statusSegmentControl = PPSegmentedControl(type: .base, segments: ["오픈", "종료"], selectedSegmentIndex: 0) + + private let sortOptionLabel = PPLabel(style: .regular, fontSize: 13, text: "팝업순서") + + let sortOptionSegmentControl = PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) + + let saveButton = PPButton(style: .primary, text: "저장", disabledText: "저장") + + // MARK: - init + init() { + super.init(frame: .zero) + + self.addViews() + self.setupConstraints() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension FilterOptionSelectView { + func addViews() { + [titleLabel, closeButton, statusLabel, statusSegmentControl, + sortOptionLabel, sortOptionSegmentControl, saveButton].forEach { + self.addSubview($0) + } + } + + func setupConstraints() { + titleLabel.snp.makeConstraints { make in + make.leading.equalToSuperview().inset(20) + make.top.equalToSuperview().inset(32) + } + + closeButton.snp.makeConstraints { make in + make.size.equalTo(24) + make.trailing.equalToSuperview().inset(20) + make.centerY.equalTo(titleLabel) + } + + statusLabel.snp.makeConstraints { make in + make.top.equalTo(titleLabel.snp.bottom).offset(36) + make.leading.equalToSuperview().inset(20) + } + + statusSegmentControl.snp.makeConstraints { make in + make.top.equalTo(statusLabel.snp.bottom).offset(8) + make.leading.trailing.equalToSuperview().inset(20) + } + + sortOptionLabel.snp.makeConstraints { make in + make.top.equalTo(statusSegmentControl.snp.bottom).offset(20) + make.leading.equalToSuperview().inset(20) + } + + sortOptionSegmentControl.snp.makeConstraints { make in + make.top.equalTo(sortOptionLabel.snp.bottom).offset(8) + make.horizontalEdges.equalToSuperview().inset(20) + } + + saveButton.snp.makeConstraints { make in + make.top.equalTo(sortOptionSegmentControl.snp.bottom).offset(32) + make.horizontalEdges.equalToSuperview().inset(20) + make.bottom.equalTo(self.safeAreaLayoutGuide) + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift new file mode 100644 index 00000000..31f2b813 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift @@ -0,0 +1,95 @@ +import UIKit + +import DesignSystem + +import PanModal +import ReactorKit +import RxCocoa +import RxSwift +import SnapKit + +final class FilterOptionSelectViewController: BaseViewController, View { + + typealias Reactor = FilterOptionSelectReactor + + // MARK: - Properties + var disposeBag = DisposeBag() + + private var mainView = FilterOptionSelectView() +} + +// MARK: - Life Cycle +extension FilterOptionSelectViewController { + override func loadView() { + self.view = mainView + } + + override func viewDidLoad() { + super.viewDidLoad() + } +} + +// MARK: - Methods +extension FilterOptionSelectViewController { + func bind(reactor: Reactor) { + mainView.closeButton.rx.tap + .withUnretained(self) + .subscribe(onNext: { (owner, _) in owner.dismiss(animated: true) }) + .disposed(by: disposeBag) + + mainView.statusSegmentControl.rx.controlEvent(.valueChanged) + .withUnretained(self) + .map { (owner, _) in + if owner.mainView.statusSegmentControl.selectedSegmentIndex == 0 { + Reactor.Action.changeStatus(status: .open) + } else { Reactor.Action.changeStatus(status: .closed) } + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.sortOptionSegmentControl.rx.controlEvent(.valueChanged) + .withUnretained(self) + .map { (owner, _) in + if owner.mainView.sortOptionSegmentControl.selectedSegmentIndex == 0 { + Reactor.Action.changeSortOption(sortOption: .newest) + } else { Reactor.Action.changeSortOption(sortOption: .popularity) } + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.saveButton.rx.tap + .withUnretained(self) + .do { (owner, _) in owner.dismiss(animated: true) } + .map { (owner, _) in Reactor.Action.saveButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + reactor.state + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.statusSegmentControl.selectedSegmentIndex = state.selectedFilterOption.status.index + owner.mainView.sortOptionSegmentControl.selectedSegmentIndex = state.selectedFilterOption.sortOption.index + owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable + } + .disposed(by: disposeBag) + } +} + +// MARK: - PanModalPresentable +extension FilterOptionSelectViewController: PanModalPresentable { + var panScrollable: UIScrollView? { + return nil + } + var longFormHeight: PanModalHeight { + return .intrinsicHeight + } + var shortFormHeight: PanModalHeight { + return .intrinsicHeight + } + var showDragIndicator: Bool { + return false + } + var cornerRadius: CGFloat { + return 20 + } +} From 85752c824ba41d2cfaf5146fc904aab35841ca44 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 18:37:43 +0900 Subject: [PATCH 257/393] =?UTF-8?q?feat/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EC=84=A0=ED=83=9D=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EB=B0=94=EC=9D=B8=EB=94=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/PopupSearchView.swift | 8 ++- .../View/PopupSearchViewController.swift | 49 +++++++++++++++---- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 3775df37..33deec30 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -113,8 +113,10 @@ private extension PopupSearchView { private func makeLayout() -> UICollectionViewLayout { return UICollectionViewCompositionalLayout { [weak self] sectionIndex, environment in guard let self else { return nil } - let sections = self.dataSource?.snapshot().sectionIdentifiers ?? [] + + let sections = getSectionsFromDataSource() guard sectionIndex < sections.count else { return nil } + switch sections[sectionIndex] { case .recentSearch: return makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) @@ -326,4 +328,8 @@ extension PopupSearchView { dataSource?.apply(snapshot, animatingDifferences: true) } + + func getSectionsFromDataSource() -> [Section] { + return dataSource?.snapshot().sectionIdentifiers ?? [] + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index b12e0fa4..e5780b0c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -6,6 +6,7 @@ import Infrastructure import ReactorKit import RxSwift +import RxCocoa public final class PopupSearchViewController: BaseViewController, View { @@ -37,16 +38,44 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.collectionView.rx.itemSelected - .subscribe(onNext: { [weak self, reactor] _ in - guard let self = self else { return } - let sharedCategory = reactor.sourceOfTruthCategory - let categoryReactor = CategorySelectReactor( - originCategory: sharedCategory, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) - ) - let viewController = CategorySelectViewController() - viewController.reactor = categoryReactor - self.presentPanModal(viewController) + .withUnretained(self) + .subscribe(onNext: { (owner, indexPath) in + + let sections = owner.mainView.getSectionsFromDataSource() + guard indexPath.section < sections.count else { return } + + switch sections[indexPath.section] { + case .recentSearch: return + case .category: + let sharedCategory = reactor.sourceOfTruthCategory + let categoryReactor = CategorySelectReactor( + originCategory: sharedCategory, + signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + ) + let viewController = CategorySelectViewController() + viewController.reactor = categoryReactor + self.presentPanModal(viewController) + case .searchResult: + // MARK: 디테일 화면으로 이동하기 + print("SECTION DEBUG:", sections[indexPath.section]) + } + }) + .disposed(by: disposeBag) + + /// CollectionView에 등록된 Header중 searchResult의 헤더를 찾아서 내부에 있는 button에 접근하기 위한 Rx 바인딩 + mainView.collectionView.rx + .willDisplaySupplementaryView + .filter { $0.elementKind == PopupSearchView.SectionHeaderKind.searchResult.rawValue } + .compactMap { $0.supplementaryView as? PopupGridCollectionHeaderView } + .withUnretained(self) + .subscribe(onNext: { (owner, headerView) in + headerView.filterOptionButton.rx.tap + .subscribe { _ in + let viewController = FilterOptionSelectViewController() + viewController.reactor = FilterOptionSelectReactor() + owner.presentPanModal(viewController) + } + .disposed(by: owner.disposeBag) }) .disposed(by: disposeBag) From 3b698fe6777bdff1ebb12b68ee6a14075d126669 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 19:17:02 +0900 Subject: [PATCH 258/393] =?UTF-8?q?feat/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=98=B5=EC=85=98=20=EA=B8=B0=EB=B0=98=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Model/FilterOption.swift | 2 ++ .../Reactor/FilterOptionSelectReactor.swift | 9 ++--- .../Source/Reactor/PopupSearchReactor.swift | 35 ++++++++++++++++++- .../View/PopupGridCollectionHeaderView.swift | 13 ++++--- .../Source/View/PopupSearchView.swift | 8 ++--- .../View/PopupSearchViewController.swift | 7 ++++ .../Source/View/TagCollectionHeaderView.swift | 2 -- 7 files changed, 59 insertions(+), 17 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift index e5630839..aeac407a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift @@ -20,6 +20,8 @@ final class FilterOption: NSCopying, Equatable { self.status = status self.sortOption = sortOption } + + var title: String { [status.title, sortOption.title].joined(separator: "・") } } /// 팝업 상점이 현재 열려 있는지 또는 닫혀 있는지 여부를 나타냅니다 diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift index 02c2d661..619f8f3b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift @@ -16,19 +16,19 @@ final class FilterOptionSelectReactor: Reactor { enum Mutation { case changeStatus(status: PopupStatus) case changeSortOption(sortOption: PopupSortOption) - case save + case saveCurrentFilterOption } struct State { var selectedFilterOption: FilterOption var saveButtonIsEnable: Bool = false + var isSaveButtonTapped: Bool = false } // MARK: - properties var initialState: State var disposeBag = DisposeBag() - private var originFilterOption: FilterOption // MARK: - init init() { @@ -45,7 +45,7 @@ final class FilterOptionSelectReactor: Reactor { return Observable.just(.changeSortOption(sortOption: filter)) case .saveButtonTapped: - return Observable.just(.save) + return Observable.just(.saveCurrentFilterOption) } } @@ -61,9 +61,10 @@ final class FilterOptionSelectReactor: Reactor { newState.selectedFilterOption.sortOption = sortOption newState.saveButtonIsEnable = (newState.selectedFilterOption != FilterOption.shared) - case .save: + case .saveCurrentFilterOption: FilterOption.shared.status = newState.selectedFilterOption.status FilterOption.shared.sortOption = newState.selectedFilterOption.sortOption + newState.isSaveButtonTapped = true } return newState diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 330fea29..cc14f117 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -12,6 +12,7 @@ public final class PopupSearchReactor: Reactor { // MARK: - Reactor public enum Action { case viewDidLoad + case filterOptionChanged } public enum Mutation { @@ -20,6 +21,12 @@ public final class PopupSearchReactor: Reactor { categoryItems: [TagCollectionViewCell.Input], results: [PPPopupGridCollectionViewCell.Input] ) + + case updateResult( + recentSearch: [TagCollectionViewCell.Input], + categoryItems: [TagCollectionViewCell.Input], + results: [PPPopupGridCollectionViewCell.Input] + ) } public struct State { @@ -67,17 +74,43 @@ public final class PopupSearchReactor: Reactor { results: owner.convertResponseToSearchResultInput(response: response) ) } + + case .filterOptionChanged: + return useCase.getSearchBottomPopUpList( + isOpen: FilterOption.shared.status.requestValue, + categories: [], + page: 0, + size: 10, + sort: FilterOption.shared.sortOption.requestValue + ) + .withUnretained(self) + .map { (owner, response) in + return .updateResult( + recentSearch: owner.getRecentSearchKeywords(), + categoryItems: owner.sourceOfTruthCategory.items, + results: owner.convertResponseToSearchResultInput(response: response) + ) + + } } } public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case let .setInitialState(recentSearchItems, categoryItems, searchResultItems): + case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems): + newState.recentSearchItems = recentSearchItems + newState.categoryItems = categoryItems + newState.searchResultItems = searchResultItems + + case .updateResult(let recentSearchItems, let categoryItems, let searchResultItems): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems + newState.openTitle = FilterOption.shared.status.title + newState.sortOptionTitle = FilterOption.shared.sortOption.title } + return newState } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift index 924e9229..916d3af1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift @@ -36,8 +36,6 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { self.addViews() self.setupConstraints() - - self.backgroundColor = .blue } @available(*, unavailable) @@ -92,12 +90,17 @@ private extension PopupGridCollectionHeaderView { extension PopupGridCollectionHeaderView: Inputable { struct Input { - var count: Int + var count: Int? var sortedTitle: String? } func injection(with input: Input) { - cellCountLabel.text = "총 \(input.count)개" - filterOptionLabel.text = input.sortedTitle + if let count = input.count { + cellCountLabel.text = "총 \(count)개" + } + + if let sortedTitle = input.sortedTitle { + filterOptionLabel.text = sortedTitle + } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 33deec30..acc99ea5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -53,7 +53,6 @@ final class PopupSearchView: UIView { forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue ) - $0.backgroundColor = UIColor.green // UICollectionView 최 상단 빈 영역 $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 0, right: 0) @@ -61,7 +60,7 @@ final class PopupSearchView: UIView { } private var dataSource: UICollectionViewDiffableDataSource? - private var popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? + var popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? // MARK: - init init() { @@ -100,9 +99,6 @@ private extension PopupSearchView { } func configureUI() { - self.searchBar.backgroundColor = .yellow - self.collectionView.backgroundColor = UIColor.green - self.configurationDataSourceItem() self.configureDataSourceHeader() } @@ -120,8 +116,10 @@ private extension PopupSearchView { switch sections[sectionIndex] { case .recentSearch: return makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) + case .category: return makeTagSectionLayout(SectionHeaderKind.category.rawValue) + case .searchResult: return makeSearchResultSectionLayout(SectionHeaderKind.searchResult.rawValue) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index e5780b0c..bce54ff9 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -73,6 +73,13 @@ extension PopupSearchViewController { .subscribe { _ in let viewController = FilterOptionSelectViewController() viewController.reactor = FilterOptionSelectReactor() + + viewController.reactor?.state + .filter { $0.isSaveButtonTapped == true } + .map { _ in Reactor.Action.filterOptionChanged } + .bind(to: reactor.action) + .disposed(by: owner.disposeBag) + owner.presentPanModal(viewController) } .disposed(by: owner.disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index 1777e985..083b2463 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -30,8 +30,6 @@ final class TagCollectionHeaderView: UICollectionReusableView { self.addViews() self.setupConstraints() - - self.backgroundColor = .red } required init?(coder: NSCoder) { From d9a51d20bb44e163e015b49228998a314f7c17bf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 20:22:18 +0900 Subject: [PATCH 259/393] =?UTF-8?q?feat/#131:=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20API=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 4 +++- .../API/CategoryAPI/CategoryAPIEndpoint.swift | 16 ++++++++++++++ .../GetCategoryListResponseDTO.swift | 2 +- .../CategoryRepositoryImpl.swift | 21 +++++++++++++++++++ .../FetchCategoryListUseCaseImpl.swift | 18 ++++++++++++++++ .../AuthResponse/CategoryResponse.swift | 6 +++--- .../Repository/CategoryRepository.swift | 7 +++++++ .../UseCase/FetchCategoryListUseCase.swift | 7 +++++++ .../SearchFeature/Source/Model/Category.swift | 4 ++-- .../Reactor/CategorySelectReactor.swift | 19 ++++++++++------- .../View/PopupSearchViewController.swift | 2 +- .../Source/View/TagCollectionViewCell.swift | 2 +- .../SearchFeatureDemo/App/AppDelegate.swift | 3 +++ 13 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/CategoryAPIEndpoint.swift rename Poppool/DataLayer/Data/Data/Network/API/{SignUpAPI => CategoryAPI}/ResponseDTO/GetCategoryListResponseDTO.swift (94%) create mode 100644 Poppool/DataLayer/Data/Data/RepositoryImpl/CategoryRepositoryImpl.swift create mode 100644 Poppool/DomainLayer/Domain/Domain/UseCaseImpl/FetchCategoryListUseCaseImpl.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Repository/CategoryRepository.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/UseCase/FetchCategoryListUseCase.swift diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index dfaf69fc..1a8e09f1 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -31,6 +31,8 @@ Network/API/AuthAPI/AuthAPIEndPoint.swift, Network/API/AuthAPI/ResponseDTO/LoginResponseDTO.swift, Network/API/AuthAPI/ResponseDTO/PostTokenReissueResponseDTO.swift, + Network/API/CategoryAPI/CategoryAPIEndpoint.swift, + Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift, Network/API/CommentAPI/CommentAPIEndPoint.swift, Network/API/CommentAPI/RequestDTO/DeleteCommentRequestDTO.swift, Network/API/CommentAPI/RequestDTO/PostCommentRequestDTO.swift, @@ -59,7 +61,6 @@ Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift, Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift, Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift, - Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift, Network/API/SignUpAPI/SignUpAPIEndpoint.swift, Network/API/UserAPI/RequesetDTO/CommentLikeRequestDTO.swift, Network/API/UserAPI/RequesetDTO/GetMyCommentRequestDTO.swift, @@ -96,6 +97,7 @@ RepositoryImpl/AdminRepositoryImpl.swift, RepositoryImpl/AppleLoginRepositoryImpl.swift, RepositoryImpl/AuthAPIRepositoryImpl.swift, + RepositoryImpl/CategoryRepositoryImpl.swift, RepositoryImpl/CommentAPIRepositoryImpl.swift, RepositoryImpl/HomeAPIRepositoryImpl.swift, RepositoryImpl/KakaoLoginRepositoryImpl.swift, diff --git a/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/CategoryAPIEndpoint.swift b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/CategoryAPIEndpoint.swift new file mode 100644 index 00000000..5ad42b7d --- /dev/null +++ b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/CategoryAPIEndpoint.swift @@ -0,0 +1,16 @@ +import Foundation + +import Infrastructure + +struct CategoryAPIEndpoint { + + /// 관심사 목록을 가져옵니다. + /// - Returns: Endpoint + static func getCategoryList() -> Endpoint { + return Endpoint( + baseURL: Secrets.popPoolBaseURL, + path: "/categories", + method: .get + ) + } +} diff --git a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift similarity index 94% rename from Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift rename to Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift index f964e045..424d9cff 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/ResponseDTO/GetCategoryListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift @@ -9,7 +9,7 @@ struct GetCategoryListResponseDTO: Codable { // MARK: - InterestResponse struct CategoryResponseDTO: Codable { - let categoryId: Int64 + let categoryId: Int32 let categoryName: String } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/CategoryRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/CategoryRepositoryImpl.swift new file mode 100644 index 00000000..5616be38 --- /dev/null +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/CategoryRepositoryImpl.swift @@ -0,0 +1,21 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public final class CategoryRepositoryImpl: CategoryRepository { + + private let provider: Provider + + public init(provider: Provider) { + self.provider = provider + } + + public func fetchCategoryList() -> Observable<[CategoryResponse]> { + let endPoint = CategoryAPIEndpoint.getCategoryList() + return provider.requestData(with: endPoint, interceptor: TokenInterceptor()).map { responseDTO in + return responseDTO.categoryResponseList.map({ $0.toDomain() }) + } + } +} diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/FetchCategoryListUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/FetchCategoryListUseCaseImpl.swift new file mode 100644 index 00000000..aab8b580 --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/FetchCategoryListUseCaseImpl.swift @@ -0,0 +1,18 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public final class FetchCategoryListUseCaseImpl: FetchCategoryListUseCase { + + private let repository: CategoryRepository + + public init(repository: CategoryRepository) { + self.repository = repository + } + + public func execute() -> Observable<[CategoryResponse]> { + return repository.fetchCategoryList() + } +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index 23d1c6e0..d45f60d2 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -1,11 +1,11 @@ import Foundation public struct CategoryResponse { - public init(categoryId: Int64, category: String) { - self.categoryId = categoryId + public init(categoryId: Int32, category: String) { + self.categoryId = Int(categoryId) self.category = category } - public let categoryId: Int64 + public let categoryId: Int public let category: String } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/CategoryRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CategoryRepository.swift new file mode 100644 index 00000000..1df92582 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/CategoryRepository.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol CategoryRepository { + func fetchCategoryList() -> Observable<[CategoryResponse]> +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/FetchCategoryListUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/FetchCategoryListUseCase.swift new file mode 100644 index 00000000..14e35064 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/FetchCategoryListUseCase.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol FetchCategoryListUseCase { + func execute() -> Observable<[CategoryResponse]> +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index 37b399d9..b5fb8ae4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -9,7 +9,7 @@ public final class Category: NSCopying, Equatable { self.items = items } - func contains(id: Int64) -> Bool { + func contains(id: Int) -> Bool { items.contains { $0.id == id } } @@ -17,7 +17,7 @@ public final class Category: NSCopying, Equatable { return Category(items: self.items) } - func toggleItemSelection(by categoryID: Int64) { + func toggleItemSelection(by categoryID: Int) { guard let index = items.firstIndex(where: { $0.id == categoryID }) else { return } items[index].isSelected.toggle() } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index 414983f0..dc31e8f5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -13,14 +13,14 @@ final class CategorySelectReactor: Reactor { case viewWillAppear case resetButtonTapped case saveButtonTapped - case cellTapped(categoryID: Int64) + case cellTapped(categoryID: Int) } enum Mutation { case setupCategotyTag(items: [TagCollectionViewCell.Input]) case resetCategory case saveCategory - case toggleTappedCell(categoryID: Int64) + case toggleTappedCell(categoryID: Int) } struct State { @@ -34,28 +34,33 @@ final class CategorySelectReactor: Reactor { var disposeBag = DisposeBag() let originCategory: Category - private let signUpAPIUseCase: SignUpAPIUseCase + private let fetchCategoryListUseCase: FetchCategoryListUseCase // MARK: - init init( originCategory: Category, - signUpAPIUseCase: SignUpAPIUseCase + fetchCategoryListUseCase: FetchCategoryListUseCase ) { self.initialState = State(category: originCategory.copy() as! Category) self.originCategory = originCategory - self.signUpAPIUseCase = signUpAPIUseCase + self.fetchCategoryListUseCase = fetchCategoryListUseCase } // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { case .viewWillAppear: - return signUpAPIUseCase.fetchCategoryList() + return fetchCategoryListUseCase.execute() .withUnretained(self) .map { (owner, response) in let items = response.map { let isSelected = owner.originCategory.contains(id: $0.categoryId) - return TagCollectionViewCell.Input(title: $0.category, id: $0.categoryId, isSelected: isSelected) + return TagCollectionViewCell.Input( + title: $0.category, + id: $0.categoryId, + isSelected: isSelected, + isCancelable: false + ) } return .setupCategotyTag(items: items) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index bce54ff9..afb26437 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -50,7 +50,7 @@ extension PopupSearchViewController { let sharedCategory = reactor.sourceOfTruthCategory let categoryReactor = CategorySelectReactor( originCategory: sharedCategory, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) + fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) ) let viewController = CategorySelectViewController() viewController.reactor = categoryReactor diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift index b134317f..f4a8f4f2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -78,7 +78,7 @@ private extension TagCollectionViewCell { extension TagCollectionViewCell: Inputable { public struct Input: Hashable { var title: String? - var id: Int64? = nil + var id: Int? = nil var isSelected: Bool = false var isCancelable: Bool = true } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index 8211412f..ad3e3fda 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -46,6 +46,7 @@ extension AppDelegate { DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } + DIContainer.register(CategoryRepository.self) { return CategoryRepositoryImpl(provider: provider) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -59,6 +60,7 @@ extension AppDelegate { @Dependency var preSignedRepository: PreSignedRepository @Dependency var kakaoLoginRepository: KakaoLoginRepository @Dependency var appleLoginRepository: AppleLoginRepository + @Dependency var categoryRepository: CategoryRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -72,6 +74,7 @@ extension AppDelegate { DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } + DIContainer.register(FetchCategoryListUseCase.self) { return FetchCategoryListUseCaseImpl(repository: categoryRepository) } } } From 2519e09d2e13f0fd67d03ad5f2afe584972fc236 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 21:31:40 +0900 Subject: [PATCH 260/393] =?UTF-8?q?feat/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=84=A0=ED=83=9D=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=84=A0=ED=83=9D=ED=95=9C=20=EC=B9=B4?= =?UTF-8?q?=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EB=B0=98=EC=98=81=EB=90=98?= =?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 --- .../SearchFeature/Source/Model/Category.swift | 35 +++++++++++++++---- .../Reactor/CategorySelectReactor.swift | 33 ++++++++++------- .../Source/Reactor/PopupSearchReactor.swift | 34 ++++++++++++------ .../View/CategorySelectViewController.swift | 4 +-- .../View/PopupSearchViewController.swift | 11 ++++-- .../Source/View/TagCollectionViewCell.swift | 5 +++ 6 files changed, 88 insertions(+), 34 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index b5fb8ae4..b8cf3602 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -1,20 +1,33 @@ import Foundation public final class Category: NSCopying, Equatable { + public func copy(with zone: NSZone? = nil) -> Any { + return Category(items: self.items) + } + public static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } - var items: [TagCollectionViewCell.Input] + static let shared = Category() - init(items: [TagCollectionViewCell.Input] = []) { - self.items = items + /// 선택된 아이템들만 들어가는 인스턴스 + private var _items: [TagCollectionViewCell.Input] + public var items: [TagCollectionViewCell.Input] { + get { _items } + set { _items = newValue.isEmpty ? [Category.defaultItem] : newValue } } - func contains(id: Int) -> Bool { - items.contains { $0.id == id } + private static let defaultItem = TagCollectionViewCell.Input(title: "카테고리", isSelected: false, isCancelable: false) + + private init(items: [TagCollectionViewCell.Input] = [Category.defaultItem]) { + self._items = items.isEmpty ? [Category.defaultItem] : items } +} - public func copy(with zone: NSZone? = nil) -> Any { - return Category(items: self.items) +// MARK: - Functions +extension Category { + + func contains(id: Int) -> Bool { + items.contains { $0.id == id } } func toggleItemSelection(by categoryID: Int) { @@ -25,4 +38,12 @@ public final class Category: NSCopying, Equatable { func turnOffAllItemSelection() { for index in items.indices { items[index].isSelected = false } } + + func resetItems() { + items = [Category.defaultItem] + } + + func getSelectedCategoryIDs() -> [Int64] { + return items.filter { $0.isSelected == true }.compactMap { $0.id }.map { Int64($0) } + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index dc31e8f5..c365240d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -24,25 +24,24 @@ final class CategorySelectReactor: Reactor { } struct State { - var category: Category + var categoryItems: [TagCollectionViewCell.Input] = [] var saveButtonIsEnable: Bool = false + var isSaveOrResetButtonTapped: Bool = false } // MARK: - properties var initialState: State + private var originCategoryItems: [TagCollectionViewCell.Input] = [] var disposeBag = DisposeBag() - let originCategory: Category private let fetchCategoryListUseCase: FetchCategoryListUseCase // MARK: - init init( - originCategory: Category, fetchCategoryListUseCase: FetchCategoryListUseCase ) { - self.initialState = State(category: originCategory.copy() as! Category) - self.originCategory = originCategory + self.initialState = State() self.fetchCategoryListUseCase = fetchCategoryListUseCase } @@ -54,11 +53,9 @@ final class CategorySelectReactor: Reactor { .withUnretained(self) .map { (owner, response) in let items = response.map { - let isSelected = owner.originCategory.contains(id: $0.categoryId) return TagCollectionViewCell.Input( title: $0.category, id: $0.categoryId, - isSelected: isSelected, isCancelable: false ) } @@ -81,19 +78,31 @@ final class CategorySelectReactor: Reactor { switch mutation { case .setupCategotyTag(let items): - newState.category.items = items + let fetchedItems = items.map { + if let id = $0.id, Category.shared.contains(id: id) { + return $0.selectionToggledItem() + } else { return $0 } + } + + originCategoryItems = fetchedItems + newState.categoryItems = fetchedItems case .resetCategory: - newState.category.turnOffAllItemSelection() + Category.shared.resetItems() + newState.isSaveOrResetButtonTapped = true case .saveCategory: - self.originCategory.items = newState.category.items + Category.shared.items = newState.categoryItems.filter { $0.isSelected == true } + newState.isSaveOrResetButtonTapped = true case .toggleTappedCell(let categoryID): - newState.category.toggleItemSelection(by: categoryID) + newState.categoryItems = state.categoryItems.map { + if $0.id == categoryID { return $0.selectionToggledItem() } + else { return $0 } + } + newState.saveButtonIsEnable = (originCategoryItems != newState.categoryItems) } - newState.saveButtonIsEnable = (originCategory != newState.category) return newState } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index cc14f117..225e82b4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -12,7 +12,8 @@ public final class PopupSearchReactor: Reactor { // MARK: - Reactor public enum Action { case viewDidLoad - case filterOptionChanged + case filterOptionSaveButtonTapped + case categorySaveButtonTapped } public enum Mutation { @@ -31,7 +32,7 @@ public final class PopupSearchReactor: Reactor { public struct State { var recentSearchItems: [TagCollectionViewCell.Input] = [] - var categoryItems: [TagCollectionViewCell.Input] = [] + var categoryItems: [TagCollectionViewCell.Input] = Category.shared.items var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] var openTitle: String = PopupStatus.open.title var sortOptionTitle: String = PopupSortOption.newest.title @@ -45,14 +46,10 @@ public final class PopupSearchReactor: Reactor { private let userDefaultService = UserDefaultService() private let useCase: PopUpAPIUseCase - public let sourceOfTruthCategory: Category = Category( - items: [TagCollectionViewCell.Input(title: "카테고리", isSelected: false, isCancelable: false)] - ) - // MARK: - init public init(useCase: PopUpAPIUseCase) { self.useCase = useCase - self.initialState = State(categoryItems: self.sourceOfTruthCategory.items) + self.initialState = State() } // MARK: - Reactor Methods @@ -70,12 +67,12 @@ public final class PopupSearchReactor: Reactor { .map { owner, response in return .setInitialState( recentSearch: owner.getRecentSearchKeywords(), - categoryItems: owner.sourceOfTruthCategory.items, + categoryItems: Category.shared.items, results: owner.convertResponseToSearchResultInput(response: response) ) } - case .filterOptionChanged: + case .filterOptionSaveButtonTapped: return useCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: [], @@ -87,11 +84,28 @@ public final class PopupSearchReactor: Reactor { .map { (owner, response) in return .updateResult( recentSearch: owner.getRecentSearchKeywords(), - categoryItems: owner.sourceOfTruthCategory.items, + categoryItems: Category.shared.items, results: owner.convertResponseToSearchResultInput(response: response) ) } + + case .categorySaveButtonTapped: + return useCase.getSearchBottomPopUpList( + isOpen: FilterOption.shared.status.requestValue, + categories: Category.shared.getSelectedCategoryIDs(), + page: 0, + size: 10, + sort: FilterOption.shared.sortOption.requestValue + ) + .withUnretained(self) + .map { (owner, response) in + return .updateResult( + recentSearch: owner.getRecentSearchKeywords(), + categoryItems: Category.shared.items, + results: owner.convertResponseToSearchResultInput(response: response) + ) + } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift index 46d47d5c..896ab0ca 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift @@ -38,7 +38,7 @@ extension CategorySelectViewController { .disposed(by: disposeBag) reactor.state - .map { $0.category.items } + .map { $0.categoryItems } .distinctUntilChanged() .bind(to: mainView.collectionView.rx.items( cellIdentifier: TagCollectionViewCell.identifiers, @@ -50,7 +50,7 @@ extension CategorySelectViewController { mainView.collectionView.rx.itemSelected .withLatestFrom( - reactor.state.map { Array($0.category.items) }, + reactor.state.map { Array($0.categoryItems) }, resultSelector: { indexPath, items in items[indexPath.item] } ) .compactMap { $0.id } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index afb26437..5aed91e1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -47,13 +47,18 @@ extension PopupSearchViewController { switch sections[indexPath.section] { case .recentSearch: return case .category: - let sharedCategory = reactor.sourceOfTruthCategory let categoryReactor = CategorySelectReactor( - originCategory: sharedCategory, fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) ) let viewController = CategorySelectViewController() viewController.reactor = categoryReactor + + viewController.reactor?.state + .filter { $0.isSaveOrResetButtonTapped == true } + .map { _ in Reactor.Action.categorySaveButtonTapped } + .bind(to: reactor.action) + .disposed(by: self.disposeBag) + self.presentPanModal(viewController) case .searchResult: // MARK: 디테일 화면으로 이동하기 @@ -76,7 +81,7 @@ extension PopupSearchViewController { viewController.reactor?.state .filter { $0.isSaveButtonTapped == true } - .map { _ in Reactor.Action.filterOptionChanged } + .map { _ in Reactor.Action.filterOptionSaveButtonTapped } .bind(to: reactor.action) .disposed(by: owner.disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift index f4a8f4f2..184e4337 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -81,6 +81,11 @@ extension TagCollectionViewCell: Inputable { var id: Int? = nil var isSelected: Bool = false var isCancelable: Bool = true + + func selectionToggledItem() -> Input { + let toggledSelection = !isSelected + return Input(title: self.title, id: self.id, isSelected: toggledSelection, isCancelable: self.isCancelable) + } } public func injection(with input: Input) { From 2eae6f184f729f81b9ed796921d251af8068274e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 21:36:02 +0900 Subject: [PATCH 261/393] =?UTF-8?q?fix/#131:=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=82=AC=EC=9D=B4=EC=82=AC=EC=9D=B4=20=EA=B0=84=EA=B2=A9=20?= =?UTF-8?q?=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index acc99ea5..744699da 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -4,18 +4,21 @@ import SnapKit import Then final class PopupSearchView: UIView { + /// View를 구성하는 section을 정의 enum Section: CaseIterable, Hashable { case recentSearch case category case searchResult } + /// Section에 들어갈 Item을 정의한 변수 enum SectionItem: Hashable { case recentSearchItem(TagCollectionViewCell.Input) case categoryItem(TagCollectionViewCell.Input) case searchResultItem(PPPopupGridCollectionViewCell.Input) } + /// Section의 헤더를 구분하기 위한 변수 enum SectionHeaderKind: String { case recentSearch = "recentSearchElementKind" case category = "categoryElementKind" @@ -144,6 +147,8 @@ private extension PopupSearchView { subitems: [item] ) + group.interItemSpacing = .fixed(6) + // Section let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous @@ -154,8 +159,6 @@ private extension PopupSearchView { section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 0) } - section.interGroupSpacing = 6 - section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] return section From 10641e71a223a907aa6d534de42fcbc753b2bea0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 21:46:20 +0900 Subject: [PATCH 262/393] =?UTF-8?q?fix/#131:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EA=B0=80=20=EB=B3=80=EA=B2=BD=EC=9D=B4=20=EC=97=86?= =?UTF-8?q?=EB=8B=A4=EB=A9=B4=20fetch=EB=A5=BC=20=EC=8B=A4=ED=96=89?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=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 --- .../SearchFeature/Source/Reactor/CategorySelectReactor.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index c365240d..530547e1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -88,6 +88,7 @@ final class CategorySelectReactor: Reactor { newState.categoryItems = fetchedItems case .resetCategory: + if originCategoryItems == newState.categoryItems { break } Category.shared.resetItems() newState.isSaveOrResetButtonTapped = true From 24ff9bb97190cd7896da585495a652ca40971a44 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 21:46:20 +0900 Subject: [PATCH 263/393] =?UTF-8?q?fix/#131:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=EA=B0=80=20=EB=B3=80=EA=B2=BD=EC=9D=B4=20=EC=97=86?= =?UTF-8?q?=EB=8B=A4=EB=A9=B4=20fetch=EB=A5=BC=20=EC=8B=A4=ED=96=89?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=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 --- .../SearchFeature/Source/Reactor/CategorySelectReactor.swift | 1 + .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 4 ++-- .../SearchFeature/Source/View/PopupSearchViewController.swift | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index c365240d..530547e1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -88,6 +88,7 @@ final class CategorySelectReactor: Reactor { newState.categoryItems = fetchedItems case .resetCategory: + if originCategoryItems == newState.categoryItems { break } Category.shared.resetItems() newState.isSaveOrResetButtonTapped = true diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 225e82b4..e0a5ea5b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -13,7 +13,7 @@ public final class PopupSearchReactor: Reactor { public enum Action { case viewDidLoad case filterOptionSaveButtonTapped - case categorySaveButtonTapped + case categorySaveOrResetButtonTapped } public enum Mutation { @@ -90,7 +90,7 @@ public final class PopupSearchReactor: Reactor { } - case .categorySaveButtonTapped: + case .categorySaveOrResetButtonTapped: return useCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 5aed91e1..28d0b1d6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -55,7 +55,7 @@ extension PopupSearchViewController { viewController.reactor?.state .filter { $0.isSaveOrResetButtonTapped == true } - .map { _ in Reactor.Action.categorySaveButtonTapped } + .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } .bind(to: reactor.action) .disposed(by: self.disposeBag) From 64fe835f3fb9dcca70cbbeefab7a6228c2dfbc13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Sat, 3 May 2025 02:57:45 +0900 Subject: [PATCH 264/393] =?UTF-8?q?refactor/#130:=20Then=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 --- .../FillterSheetView/BalloonChipCell.swift | 101 +++++++++--------- .../Scene/Map/MapView/MapViewController.swift | 2 - 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index d29dc6db..711f1fd3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -1,6 +1,7 @@ import Infrastructure import SnapKit import UIKit +import Then final class BalloonChipCell: UICollectionViewCell { static let identifier = "BalloonChipCell" @@ -15,17 +16,15 @@ final class BalloonChipCell: UICollectionViewCell { static let fontSize: CGFloat = 11 } - private let button: PPButton = { - let button = PPButton( - style: .secondary, - text: "", - font: .korFont(style: .medium, size: Constant.fontSize), - cornerRadius: 15 - ) - button.titleLabel?.lineBreakMode = .byTruncatingTail - button.titleLabel?.adjustsFontSizeToFitWidth = false - return button - }() + private let button = PPButton( + style: .secondary, + text: "", + font: .korFont(style: .medium, size: Constant.fontSize), + cornerRadius: 15 + ).then { + $0.titleLabel?.lineBreakMode = .byTruncatingTail + $0.titleLabel?.adjustsFontSizeToFitWidth = false + } private var currentAction: UIAction? @@ -46,34 +45,31 @@ final class BalloonChipCell: UICollectionViewCell { } func configure(with title: String, isSelected: Bool) { - let attributedTitle = NSMutableAttributedString(string: title) - attributedTitle.addAttribute( - .baselineOffset, - value: Constant.baselineOffset, - range: NSRange(location: .zero, length: attributedTitle.length) - ) + let attributedTitle = NSMutableAttributedString(string: title).then { + $0.addAttribute( + .baselineOffset, + value: Constant.baselineOffset, + range: NSRange(location: .zero, length: $0.length) + ) + } if isSelected { - let checkImage = UIImage(named: "icon_check_white")? - .withRenderingMode(.alwaysOriginal) - .resize(to: Constant.checkIconSize) - self.button.setImage(checkImage, for: .normal) - self.button.semanticContentAttribute = .forceRightToLeft - self.button.imageEdgeInsets = .init( - top: .zero, - left: 1, - bottom: .zero, - right: .zero - ) - self.button.contentEdgeInsets = .init( - top: Constant.verticalInset, - left: Constant.selectedLeftInset, - bottom: Constant.verticalInset, - right: Constant.rightInset - ) - self.button.setBackgroundColor(.blu500, for: .normal) - self.button.setTitleColor(.white, for: .normal) - self.button.layer.borderWidth = .zero + let checkImage = UIImage(named: "icon_check_white")?.withRenderingMode(.alwaysOriginal).resize(to: Constant.checkIconSize) + + button.then { + $0.setImage(checkImage, for: .normal) + $0.semanticContentAttribute = .forceRightToLeft + $0.imageEdgeInsets = .init(top: .zero, left: 1, bottom: .zero, right: .zero) + $0.contentEdgeInsets = .init( + top: Constant.verticalInset, + left: Constant.selectedLeftInset, + bottom: Constant.verticalInset, + right: Constant.rightInset + ) + $0.setBackgroundColor(.blu500, for: .normal) + $0.setTitleColor(.white, for: .normal) + $0.layer.borderWidth = .zero + } attributedTitle.addAttribute( .font, @@ -81,19 +77,21 @@ final class BalloonChipCell: UICollectionViewCell { range: NSRange(location: .zero, length: attributedTitle.length) ) } else { - self.button.setImage(nil, for: .normal) - self.button.semanticContentAttribute = .unspecified - self.button.imageEdgeInsets = .zero - self.button.contentEdgeInsets = .init( - top: Constant.verticalInset, - left: Constant.normalLeftInset, - bottom: Constant.verticalInset, - right: Constant.rightInset - ) - self.button.setBackgroundColor(.white, for: .normal) - self.button.setTitleColor(.g400, for: .normal) - self.button.layer.borderWidth = 1 - self.button.layer.borderColor = UIColor.g200.cgColor + button.then { + $0.setImage(nil, for: .normal) + $0.semanticContentAttribute = .unspecified + $0.imageEdgeInsets = .zero + $0.contentEdgeInsets = .init( + top: Constant.verticalInset, + left: Constant.normalLeftInset, + bottom: Constant.verticalInset, + right: Constant.rightInset + ) + $0.setBackgroundColor(.white, for: .normal) + $0.setTitleColor(.g400, for: .normal) + $0.layer.borderWidth = 1 + $0.layer.borderColor = UIColor.g200.cgColor + } attributedTitle.addAttribute( .font, @@ -110,15 +108,18 @@ final class BalloonChipCell: UICollectionViewCell { if let oldAction = currentAction { self.button.removeAction(oldAction, for: .touchUpInside) } + let action = UIAction { [weak self] _ in guard let self = self else { return } self.buttonAction?() } + self.button.addAction(action, for: .touchUpInside) self.currentAction = action } } } + extension UIImage { func resize(to size: CGSize) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, 0.0) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 6e6923b8..88ce0efa 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -161,12 +161,10 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM self.configureTooltip(for: markerToFocus, stores: storeArray) } - // 툴팁에서 선택된 스토어 업데이트 if let tooltipIndex = storeArray.firstIndex(where: { $0.id == store.id }) { (self.currentTooltipView as? MarkerTooltipView)?.selectStore(at: tooltipIndex) } } else { - // 단일 마커면 기존 툴팁 제거 self.currentTooltipView?.removeFromSuperview() self.currentTooltipView = nil } From 94c86185d27aa4ee7188a785c6da59d5167f5c30 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 22:49:54 +0900 Subject: [PATCH 265/393] =?UTF-8?q?feat/#131:=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 68 ++++++++++++------- .../View/PopupSearchViewController.swift | 34 +++++++++- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index e0a5ea5b..4875a3ec 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -14,20 +14,25 @@ public final class PopupSearchReactor: Reactor { case viewDidLoad case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped + case viewAllVisibleItems } public enum Mutation { case setInitialState( - recentSearch: [TagCollectionViewCell.Input], + recentSearchItems: [TagCollectionViewCell.Input], categoryItems: [TagCollectionViewCell.Input], - results: [PPPopupGridCollectionViewCell.Input] + searchResultsItems: [PPPopupGridCollectionViewCell.Input], + totalPages: Int32 ) - case updateResult( - recentSearch: [TagCollectionViewCell.Input], + case updateSearchResult( + recentSearchItems: [TagCollectionViewCell.Input], categoryItems: [TagCollectionViewCell.Input], - results: [PPPopupGridCollectionViewCell.Input] + searchResultsItems: [PPPopupGridCollectionViewCell.Input], + totalPage: Int32 ) + + case fetchNextPage(searchResultsItems: [PPPopupGridCollectionViewCell.Input]) } public struct State { @@ -36,6 +41,11 @@ public final class PopupSearchReactor: Reactor { var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] var openTitle: String = PopupStatus.open.title var sortOptionTitle: String = PopupSortOption.newest.title + + fileprivate var currentPage: Int32 = 0 + fileprivate let paginationSize: Int32 = 10 + fileprivate var totalPages: Int32 = 0 + var hasNextPage: Bool { get { currentPage < (totalPages - 1) } } } // MARK: - properties @@ -60,51 +70,51 @@ public final class PopupSearchReactor: Reactor { isOpen: PopupStatus.open.requestValue, categories: [], page: 0, - size: 10, + size: currentState.paginationSize, sort: PopupSortOption.newest.requestValue ) .withUnretained(self) .map { owner, response in return .setInitialState( - recentSearch: owner.getRecentSearchKeywords(), + recentSearchItems: owner.getRecentSearchKeywords(), categoryItems: Category.shared.items, - results: owner.convertResponseToSearchResultInput(response: response) + searchResultsItems: owner.convertResponseToSearchResultInput(response: response), + totalPages: response.totalPages ) } - case .filterOptionSaveButtonTapped: + case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: return useCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, - categories: [], + categories: Category.shared.getSelectedCategoryIDs(), page: 0, - size: 10, + size: currentState.paginationSize, sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) .map { (owner, response) in - return .updateResult( - recentSearch: owner.getRecentSearchKeywords(), + return .updateSearchResult( + recentSearchItems: owner.getRecentSearchKeywords(), categoryItems: Category.shared.items, - results: owner.convertResponseToSearchResultInput(response: response) + searchResultsItems: owner.convertResponseToSearchResultInput(response: response), + totalPage: response.totalPages ) } - case .categorySaveOrResetButtonTapped: + case .viewAllVisibleItems: + guard currentState.hasNextPage else { return .empty() } + return useCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), - page: 0, - size: 10, + page: currentState.currentPage + 1, + size: currentState.paginationSize, sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) .map { (owner, response) in - return .updateResult( - recentSearch: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.items, - results: owner.convertResponseToSearchResultInput(response: response) - ) + return .fetchNextPage(searchResultsItems: owner.convertResponseToSearchResultInput(response: response)) } } } @@ -112,19 +122,27 @@ public final class PopupSearchReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems): + case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems, let totalPages): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems + newState.totalPages = totalPages + - case .updateResult(let recentSearchItems, let categoryItems, let searchResultItems): + case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPages): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems newState.openTitle = FilterOption.shared.status.title newState.sortOptionTitle = FilterOption.shared.sortOption.title + newState.currentPage = 0 + newState.totalPages = totalPages + + case .fetchNextPage(let searchResultItems): + newState.searchResultItems += searchResultItems + newState.currentPage += 1 } - + return newState } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 28d0b1d6..2ab2ba96 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -68,8 +68,7 @@ extension PopupSearchViewController { .disposed(by: disposeBag) /// CollectionView에 등록된 Header중 searchResult의 헤더를 찾아서 내부에 있는 button에 접근하기 위한 Rx 바인딩 - mainView.collectionView.rx - .willDisplaySupplementaryView + mainView.collectionView.rx.willDisplaySupplementaryView .filter { $0.elementKind == PopupSearchView.SectionHeaderKind.searchResult.rawValue } .compactMap { $0.supplementaryView as? PopupGridCollectionHeaderView } .withUnretained(self) @@ -91,6 +90,37 @@ extension PopupSearchViewController { }) .disposed(by: disposeBag) + + mainView.collectionView.rx.prefetchItems + .throttle(.seconds(1), latest: false , scheduler: MainScheduler.instance) + .withUnretained(self) + .subscribe { (owner, indexPaths) in + let sections = owner.mainView.getSectionsFromDataSource() + + guard let searchResultSectionIndex = sections.firstIndex(where: { section in + switch section { + case .searchResult: return true + default: return false + } + }) else { return } + + /// prefetch를 하기까지 남은 아이템의 갯수 + let prefetchCount = 2 + + let itemCount = owner.mainView.collectionView.numberOfItems(inSection: searchResultSectionIndex) + + guard itemCount > prefetchCount else { return } + + /// 보여줄 아이템이 prefetchCount만큼 남았을때 가까운 상태라고 확인 + let isNearBottom = indexPaths.contains { + $0.section == searchResultSectionIndex && + $0.item >= owner.mainView.collectionView.numberOfItems(inSection: $0.section) - prefetchCount + } + + if isNearBottom { owner.reactor?.action.onNext(.viewAllVisibleItems) } + } + .disposed(by: disposeBag) + reactor.state .withUnretained(self) .subscribe { (owner, state) in From 89a06270b569ea7a28f5774fb9b0a67986c77d63 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 4 May 2025 23:36:52 +0900 Subject: [PATCH 266/393] =?UTF-8?q?fix/#131:=20=EC=86=8C=EC=86=8C=ED=95=9C?= =?UTF-8?q?=20=EB=B2=84=EA=B7=B8=EB=93=A4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 카테고리가 초기화 안되는 문제 수정 - 총 아이템 갯수가 다르게 뜨는 문제 수정 - 카테고리 태그 옆에 지우기 버튼 추가 --- .../SearchFeature/Source/Model/Category.swift | 5 +++ .../Reactor/CategorySelectReactor.swift | 2 +- .../Source/Reactor/PopupSearchReactor.swift | 45 +++++++++++-------- .../View/PopupSearchViewController.swift | 3 +- .../Source/View/TagCollectionViewCell.swift | 4 ++ .../icon_xmark_gray.imageset/Contents.json | 12 +++++ .../icon_xmark_gray.imageset/line.svg | 4 ++ .../icon_xmark_white.imageset/Contents.json | 12 +++++ .../icon_xmark_white.imageset/line.svg | 4 ++ 9 files changed, 70 insertions(+), 21 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/line.svg create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/Contents.json create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/line.svg diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index b8cf3602..9607654d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -46,4 +46,9 @@ extension Category { func getSelectedCategoryIDs() -> [Int64] { return items.filter { $0.isSelected == true }.compactMap { $0.id }.map { Int64($0) } } + + func getCancelableCategoryItems() -> [TagCollectionViewCell.Input] { + if items == [Category.defaultItem] { return items } + else { return items.filter { $0.isSelected == true }.map { $0.cancelableItem() } } + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index 530547e1..840467f1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -88,7 +88,7 @@ final class CategorySelectReactor: Reactor { newState.categoryItems = fetchedItems case .resetCategory: - if originCategoryItems == newState.categoryItems { break } + if originCategoryItems.isEmpty { break } Category.shared.resetItems() newState.isSaveOrResetButtonTapped = true diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 4875a3ec..d5d79223 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -22,14 +22,16 @@ public final class PopupSearchReactor: Reactor { recentSearchItems: [TagCollectionViewCell.Input], categoryItems: [TagCollectionViewCell.Input], searchResultsItems: [PPPopupGridCollectionViewCell.Input], - totalPages: Int32 + totalPagesCount: Int32, + totalElementCount: Int64 ) case updateSearchResult( recentSearchItems: [TagCollectionViewCell.Input], categoryItems: [TagCollectionViewCell.Input], searchResultsItems: [PPPopupGridCollectionViewCell.Input], - totalPage: Int32 + totalPagesCount: Int32, + totalElementCount: Int64 ) case fetchNextPage(searchResultsItems: [PPPopupGridCollectionViewCell.Input]) @@ -37,15 +39,16 @@ public final class PopupSearchReactor: Reactor { public struct State { var recentSearchItems: [TagCollectionViewCell.Input] = [] - var categoryItems: [TagCollectionViewCell.Input] = Category.shared.items + var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] var openTitle: String = PopupStatus.open.title var sortOptionTitle: String = PopupSortOption.newest.title - fileprivate var currentPage: Int32 = 0 - fileprivate let paginationSize: Int32 = 10 - fileprivate var totalPages: Int32 = 0 - var hasNextPage: Bool { get { currentPage < (totalPages - 1) } } + fileprivate var currentPage: Int = 0 + fileprivate let paginationSize: Int = 10 + fileprivate var totalPagesCount: Int = 0 + var hasNextPage: Bool { get { currentPage < (totalPagesCount - 1) } } + var totalElementsCount: Int = 0 } // MARK: - properties @@ -70,16 +73,17 @@ public final class PopupSearchReactor: Reactor { isOpen: PopupStatus.open.requestValue, categories: [], page: 0, - size: currentState.paginationSize, + size: Int32(currentState.paginationSize), sort: PopupSortOption.newest.requestValue ) .withUnretained(self) .map { owner, response in return .setInitialState( recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.items, + categoryItems: Category.shared.getCancelableCategoryItems(), searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPages: response.totalPages + totalPagesCount: response.totalPages, + totalElementCount: response.totalElements ) } @@ -88,16 +92,17 @@ public final class PopupSearchReactor: Reactor { isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), page: 0, - size: currentState.paginationSize, + size: Int32(currentState.paginationSize), sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) .map { (owner, response) in return .updateSearchResult( recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.items, + categoryItems: Category.shared.getCancelableCategoryItems(), searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPage: response.totalPages + totalPagesCount: response.totalPages, + totalElementCount: response.totalElements ) } @@ -108,8 +113,8 @@ public final class PopupSearchReactor: Reactor { return useCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), - page: currentState.currentPage + 1, - size: currentState.paginationSize, + page: Int32(currentState.currentPage + 1), + size: Int32(currentState.paginationSize), sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) @@ -122,21 +127,23 @@ public final class PopupSearchReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems, let totalPages): + case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems - newState.totalPages = totalPages + newState.totalPagesCount = Int(totalPagesCount) + newState.totalElementsCount = Int(totalElementsCount) - case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPages): + case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems newState.openTitle = FilterOption.shared.status.title newState.sortOptionTitle = FilterOption.shared.sortOption.title newState.currentPage = 0 - newState.totalPages = totalPages + newState.totalPagesCount = Int(totalPagesCount) + newState.totalElementsCount = Int(totalElementsCount) case .fetchNextPage(let searchResultItems): newState.searchResultItems += searchResultItems diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 2ab2ba96..cb916b7a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -124,6 +124,7 @@ extension PopupSearchViewController { reactor.state .withUnretained(self) .subscribe { (owner, state) in + owner.mainView.updateSnapshot( recentSearchItems: state.recentSearchItems .map(PopupSearchView.SectionItem.recentSearchItem), @@ -132,7 +133,7 @@ extension PopupSearchViewController { searchResultItems: state.searchResultItems .map(PopupSearchView.SectionItem.searchResultItem), headerInput: PopupGridCollectionHeaderView.Input( - count: state.searchResultItems.count, + count: state.totalElementsCount, sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") ) ) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift index 184e4337..09c8f62f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -86,6 +86,10 @@ extension TagCollectionViewCell: Inputable { let toggledSelection = !isSelected return Input(title: self.title, id: self.id, isSelected: toggledSelection, isCancelable: self.isCancelable) } + + func cancelableItem() -> Input { + return Input(title: self.title, id: self.id, isSelected: self.isSelected, isCancelable: true) + } } public func injection(with input: Input) { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/Contents.json new file mode 100644 index 00000000..d86eb988 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "line.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/line.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/line.svg new file mode 100644 index 00000000..6a318745 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_gray.imageset/line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/Contents.json b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/Contents.json new file mode 100644 index 00000000..d86eb988 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "line.svg", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/line.svg b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/line.svg new file mode 100644 index 00000000..b55257a1 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Assets.xcassets/icon_xmark_white.imageset/line.svg @@ -0,0 +1,4 @@ + + + + From 4e001e5870c8776c7c13868a9596c4dfa3205b13 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 00:41:13 +0900 Subject: [PATCH 267/393] =?UTF-8?q?feat/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 8 +++++++ .../SearchFeature/Source/Model/Category.swift | 4 ++++ .../Source/Reactor/PopupSearchReactor.swift | 24 ++++++++++++++++++- .../Source/View/PopupSearchView.swift | 11 +++++++++ .../View/PopupSearchViewController.swift | 8 ++++++- 5 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 45f84f65..0dffd886 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -26,6 +26,7 @@ 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; }; 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC28E32DC696E000C761A5 /* PanModal */; }; + 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC2AE82DC7C07400C761A5 /* RxRelay */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -109,6 +110,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */, 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */, 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, @@ -205,6 +207,7 @@ 05EC234A2DC49AB400C761A5 /* Then */, 05EC234D2DC49AC100C761A5 /* SnapKit */, 05EC28E32DC696E000C761A5 /* PanModal */, + 05EC2AE82DC7C07400C761A5 /* RxRelay */, ); productName = SearchFeature; productReference = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; @@ -709,6 +712,11 @@ package = 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */; productName = PanModal; }; + 05EC2AE82DC7C07400C761A5 /* RxRelay */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxRelay; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 051633642DC457A900A6C0D1 /* Project object */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index 9607654d..775c4c40 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -51,4 +51,8 @@ extension Category { if items == [Category.defaultItem] { return items } else { return items.filter { $0.isSelected == true }.map { $0.cancelableItem() } } } + + func removeItem(by categoryID: Int) { + items.removeAll { $0.id == categoryID } + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index d5d79223..9d1911d9 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -15,6 +15,7 @@ public final class PopupSearchReactor: Reactor { case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped case viewAllVisibleItems + case categoryCancelButtonTapped(categoryID: Int) } public enum Mutation { @@ -104,7 +105,6 @@ public final class PopupSearchReactor: Reactor { totalPagesCount: response.totalPages, totalElementCount: response.totalElements ) - } case .viewAllVisibleItems: @@ -121,6 +121,28 @@ public final class PopupSearchReactor: Reactor { .map { (owner, response) in return .fetchNextPage(searchResultsItems: owner.convertResponseToSearchResultInput(response: response)) } + + case .categoryCancelButtonTapped(let categoryID): + Category.shared.removeItem(by: categoryID) + + return useCase.getSearchBottomPopUpList( + isOpen: FilterOption.shared.status.requestValue, + categories: Category.shared.getSelectedCategoryIDs(), + page: 0, + size: Int32(currentState.paginationSize), + sort: FilterOption.shared.sortOption.requestValue + ) + .withUnretained(self) + .map { (owner, response) in + return .updateSearchResult( + recentSearchItems: owner.getRecentSearchKeywords(), + categoryItems: Category.shared.getCancelableCategoryItems(), + searchResultsItems: owner.convertResponseToSearchResultInput(response: response), + totalPagesCount: response.totalPages, + totalElementCount: response.totalElements + ) + + } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 744699da..338c569d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -2,6 +2,9 @@ import UIKit import SnapKit import Then +import RxSwift +import RxCocoa +import RxRelay final class PopupSearchView: UIView { /// View를 구성하는 section을 정의 @@ -26,6 +29,8 @@ final class PopupSearchView: UIView { } // MARK: - Properties + let canceledCategoryID = PublishRelay() + let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { $0.setCollectionViewLayout(self.makeLayout(), animated: false) @@ -245,6 +250,12 @@ extension PopupSearchView { for: indexPath ) as! TagCollectionViewCell cell.injection(with: categoryItem) + + cell.cancelButton.rx.tap + .compactMap { categoryItem.id } + .bind(to: self.canceledCategoryID) + .disposed(by: cell.disposeBag) + return cell case .searchResultItem(let searchResultItem): diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index cb916b7a..d2fd8bac 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -40,13 +40,14 @@ extension PopupSearchViewController { mainView.collectionView.rx.itemSelected .withUnretained(self) .subscribe(onNext: { (owner, indexPath) in - let sections = owner.mainView.getSectionsFromDataSource() guard indexPath.section < sections.count else { return } switch sections[indexPath.section] { case .recentSearch: return case .category: + + let categoryReactor = CategorySelectReactor( fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) ) @@ -121,6 +122,11 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) + mainView.canceledCategoryID + .map { Reactor.Action.categoryCancelButtonTapped(categoryID: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + reactor.state .withUnretained(self) .subscribe { (owner, state) in From d37cd9286e6d5fdc16fd610360b528701f966ed1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 00:49:56 +0900 Subject: [PATCH 268/393] =?UTF-8?q?refactor/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=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=EB=8B=A8=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/View/PopupSearchView.swift | 6 ++++ .../View/PopupSearchViewController.swift | 32 +++++++------------ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 338c569d..68292e65 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -30,6 +30,7 @@ final class PopupSearchView: UIView { // MARK: - Properties let canceledCategoryID = PublishRelay() + let filterOptionButtonTapped = PublishRelay() let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { @@ -301,10 +302,15 @@ extension PopupSearchView { withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue, for: indexPath ) as? PopupGridCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } + if let input = self.popupGridCollectionHeaderInput { header.injection(with: input) } else { header.injection(with: PopupGridCollectionHeaderView.Input(count: 0, sortedTitle: "nil")) } + header.filterOptionButton.rx.tap + .bind(to: self.filterOptionButtonTapped) + .disposed(by: header.disposeBag) + return header } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index d2fd8bac..3d362af7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -68,29 +68,21 @@ extension PopupSearchViewController { }) .disposed(by: disposeBag) - /// CollectionView에 등록된 Header중 searchResult의 헤더를 찾아서 내부에 있는 button에 접근하기 위한 Rx 바인딩 - mainView.collectionView.rx.willDisplaySupplementaryView - .filter { $0.elementKind == PopupSearchView.SectionHeaderKind.searchResult.rawValue } - .compactMap { $0.supplementaryView as? PopupGridCollectionHeaderView } + mainView.filterOptionButtonTapped .withUnretained(self) - .subscribe(onNext: { (owner, headerView) in - headerView.filterOptionButton.rx.tap - .subscribe { _ in - let viewController = FilterOptionSelectViewController() - viewController.reactor = FilterOptionSelectReactor() - - viewController.reactor?.state - .filter { $0.isSaveButtonTapped == true } - .map { _ in Reactor.Action.filterOptionSaveButtonTapped } - .bind(to: reactor.action) - .disposed(by: owner.disposeBag) - - owner.presentPanModal(viewController) - } + .subscribe { (owner, _) in + let viewController = FilterOptionSelectViewController() + viewController.reactor = FilterOptionSelectReactor() + + viewController.reactor?.state + .filter { $0.isSaveButtonTapped == true } + .map { _ in Reactor.Action.filterOptionSaveButtonTapped } + .bind(to: reactor.action) .disposed(by: owner.disposeBag) - }) - .disposed(by: disposeBag) + owner.presentPanModal(viewController) + } + .disposed(by: disposeBag) mainView.collectionView.rx.prefetchItems .throttle(.seconds(1), latest: false , scheduler: MainScheduler.instance) From 1d0005cb07328c1bf097c7191f47b03c1d593ea8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 01:00:01 +0900 Subject: [PATCH 269/393] =?UTF-8?q?fix/#131:=20=EC=B9=B4=ED=85=8C=EA=B3=A0?= =?UTF-8?q?=EB=A6=AC=20=ED=83=9C=EA=B7=B8=20=EB=AA=A9=EB=A1=9D=EC=9D=98=20?= =?UTF-8?q?=EA=B0=84=EA=B2=A9=EC=9D=B4=20=EC=9D=B4=EC=83=81=ED=95=98?= =?UTF-8?q?=EB=8D=98=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 6 +++--- .../Source/View/PopupSearchViewController.swift | 2 -- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 68292e65..317c1d0d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -145,7 +145,7 @@ private extension PopupSearchView { // Group let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), + widthDimension: .estimated(100), heightDimension: .estimated(31) ) let group = NSCollectionLayoutGroup.horizontal( @@ -160,9 +160,9 @@ private extension PopupSearchView { section.orthogonalScrollingBehavior = .continuous if headerKind == SectionHeaderKind.recentSearch.rawValue { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 0) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) } else { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 0) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) } section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 3d362af7..0e4de2a7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -46,8 +46,6 @@ extension PopupSearchViewController { switch sections[indexPath.section] { case .recentSearch: return case .category: - - let categoryReactor = CategorySelectReactor( fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) ) From 61a77c1ac8cfaf4ef073afe04d5ac835c18896c1 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 01:10:03 +0900 Subject: [PATCH 270/393] =?UTF-8?q?fix/#131:=20=EC=86=8C=EC=86=8C=ED=95=9C?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 하단 빈 영역이 안잡혀있는거 잡아줌 - TODO 주석 추가 --- .../SearchFeature/Source/View/PopupSearchView.swift | 4 ++-- .../SearchFeature/Source/View/PopupSearchViewController.swift | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 317c1d0d..14125b95 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -63,8 +63,8 @@ final class PopupSearchView: UIView { withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue ) - // UICollectionView 최 상단 빈 영역 - $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 0, right: 0) + // UICollectionView 최 상/하단 빈 영역 + $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 48, right: 0) $0.contentInsetAdjustmentBehavior = .never } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 0e4de2a7..8109b9a2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -61,6 +61,7 @@ extension PopupSearchViewController { self.presentPanModal(viewController) case .searchResult: // MARK: 디테일 화면으로 이동하기 + // TODO: Presentation에 의존 역전이 되지 않도록 할 방법 고민하기 print("SECTION DEBUG:", sections[indexPath.section]) } }) From fac0c99622ccafec53598e0e8e34ee3c68149c75 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 01:36:50 +0900 Subject: [PATCH 271/393] =?UTF-8?q?chore/#131:=20=EC=A3=BC=EC=84=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 --- .../SearchFeature/Source/View/CategorySelectView.swift | 1 + .../SearchFeature/Source/View/FilterOptionSelectView.swift | 1 + 2 files changed, 2 insertions(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift index 8dc3770b..2983c77d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift @@ -60,6 +60,7 @@ private extension CategorySelectView { } } + // FIXME: 레이아웃 에러로 인한 Modal이 살짝 내려가지는 문제 발생중 func setupConstraints() { titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift index 1b27697d..ee745cb8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift @@ -46,6 +46,7 @@ private extension FilterOptionSelectView { } } + // FIXME: 레이아웃 에러로 인한 Modal이 살짝 내려가지는 문제 발생중 func setupConstraints() { titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview().inset(20) From 304ae7771926f3402ed0780f29cbe6c829fe44df Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 5 May 2025 09:39:09 +0000 Subject: [PATCH 272/393] style/#130: Apply SwiftLint autocorrect --- .../Scene/Map/FillterSheetView/BalloonChipCell.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 711f1fd3..70409a3f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -1,7 +1,7 @@ import Infrastructure import SnapKit -import UIKit import Then +import UIKit final class BalloonChipCell: UICollectionViewCell { static let identifier = "BalloonChipCell" From 284b02499052668791c0cc932614f01b069eec00 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 15:52:19 +0900 Subject: [PATCH 273/393] =?UTF-8?q?refactor/#131:=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EB=B0=8F=20=EB=84=A4=EC=9D=B4=EB=B0=8D=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategorySelectView.swift | 0 .../CategorySelectViewController.swift | 0 .../FilterOptionSelectView.swift | 0 .../FilterOptionSelectViewController.swift | 0 .../Source/View/PopupSearchView.swift | 14 +++++++------- .../Source/View/PopupSearchViewController.swift | 2 +- ...aderView.swift => SearchResultHeaderView.swift} | 7 +++---- 7 files changed, 11 insertions(+), 12 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => CategorySelectModal}/CategorySelectView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => CategorySelectModal}/CategorySelectViewController.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => FilterOptionSelectModal}/FilterOptionSelectView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => FilterOptionSelectModal}/FilterOptionSelectViewController.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{PopupGridCollectionHeaderView.swift => SearchResultHeaderView.swift} (93%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 14125b95..598ea482 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -58,9 +58,9 @@ final class PopupSearchView: UIView { withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue ) $0.register( - PopupGridCollectionHeaderView.self, + SearchResultHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, - withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue + withReuseIdentifier: SearchResultHeaderView.Identifier.searchResult.rawValue ) // UICollectionView 최 상/하단 빈 영역 @@ -69,7 +69,7 @@ final class PopupSearchView: UIView { } private var dataSource: UICollectionViewDiffableDataSource? - var popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? + var popupGridCollectionHeaderInput: SearchResultHeaderView.Input? // MARK: - init init() { @@ -299,13 +299,13 @@ extension PopupSearchView { case .searchResult: guard let header = collectionView.dequeueReusableSupplementaryView( ofKind: elementKind, - withReuseIdentifier: PopupGridCollectionHeaderView.Identifier.searchResult.rawValue, + withReuseIdentifier: SearchResultHeaderView.Identifier.searchResult.rawValue, for: indexPath - ) as? PopupGridCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } + ) as? SearchResultHeaderView else { fatalError("\(#file), \(#function) Error") } if let input = self.popupGridCollectionHeaderInput { header.injection(with: input) - } else { header.injection(with: PopupGridCollectionHeaderView.Input(count: 0, sortedTitle: "nil")) } + } else { header.injection(with: SearchResultHeaderView.Input(count: 0, sortedTitle: "nil")) } header.filterOptionButton.rx.tap .bind(to: self.filterOptionButtonTapped) @@ -320,7 +320,7 @@ extension PopupSearchView { recentSearchItems: [SectionItem], categoryItems: [SectionItem], searchResultItems: [SectionItem], - headerInput popupGridCollectionHeaderInput: PopupGridCollectionHeaderView.Input? = nil + headerInput popupGridCollectionHeaderInput: SearchResultHeaderView.Input? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 8109b9a2..ae9bb4de 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -129,7 +129,7 @@ extension PopupSearchViewController { .map(PopupSearchView.SectionItem.categoryItem), searchResultItems: state.searchResultItems .map(PopupSearchView.SectionItem.searchResultItem), - headerInput: PopupGridCollectionHeaderView.Input( + headerInput: SearchResultHeaderView.Input( count: state.totalElementsCount, sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") ) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift similarity index 93% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index 916d3af1..720470c4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupGridCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -6,7 +6,7 @@ import SnapKit import RxSwift import Then -final class PopupGridCollectionHeaderView: UICollectionReusableView { +final class SearchResultHeaderView: UICollectionReusableView { enum Identifier: String { case searchResult = "PopupGridCollectionHeaderView.searchResult" @@ -25,7 +25,6 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { private let dropDownImageView = UIImageView().then { $0.image = UIImage(named: "icon_dropdown") $0.isUserInteractionEnabled = false - } let filterOptionButton = UIButton() @@ -50,7 +49,7 @@ final class PopupGridCollectionHeaderView: UICollectionReusableView { } // MARK: - SetUp -private extension PopupGridCollectionHeaderView { +private extension SearchResultHeaderView { func addViews() { [cellCountLabel, filterOptionButton].forEach { addSubview($0) @@ -88,7 +87,7 @@ private extension PopupGridCollectionHeaderView { } } -extension PopupGridCollectionHeaderView: Inputable { +extension SearchResultHeaderView: Inputable { struct Input { var count: Int? var sortedTitle: String? From b253af92a0d696f9ff73108b20665f45dea3d396 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 16:30:32 +0900 Subject: [PATCH 274/393] =?UTF-8?q?refactor/#131:=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=86=8C=EC=8A=A4=20=EA=B4=80=EB=A6=AC=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 598ea482..a411f280 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -69,7 +69,7 @@ final class PopupSearchView: UIView { } private var dataSource: UICollectionViewDiffableDataSource? - var popupGridCollectionHeaderInput: SearchResultHeaderView.Input? + private var searchResultHeaderInput: SearchResultHeaderView.Input? // MARK: - init init() { @@ -303,7 +303,7 @@ extension PopupSearchView { for: indexPath ) as? SearchResultHeaderView else { fatalError("\(#file), \(#function) Error") } - if let input = self.popupGridCollectionHeaderInput { + if let input = self.searchResultHeaderInput { header.injection(with: input) } else { header.injection(with: SearchResultHeaderView.Input(count: 0, sortedTitle: "nil")) } @@ -320,14 +320,10 @@ extension PopupSearchView { recentSearchItems: [SectionItem], categoryItems: [SectionItem], searchResultItems: [SectionItem], - headerInput popupGridCollectionHeaderInput: SearchResultHeaderView.Input? = nil + headerInput searchResultHeaderInput: SearchResultHeaderView.Input? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() - if let input = popupGridCollectionHeaderInput { - self.popupGridCollectionHeaderInput = input - } - if !recentSearchItems.isEmpty { snapshot.appendSections([PopupSearchView.Section.recentSearch]) snapshot.appendItems(recentSearchItems, toSection: .recentSearch) @@ -339,6 +335,7 @@ extension PopupSearchView { } if !searchResultItems.isEmpty { + self.searchResultHeaderInput = searchResultHeaderInput snapshot.appendSections([PopupSearchView.Section.searchResult]) snapshot.appendItems(searchResultItems, toSection: .searchResult) snapshot.reloadSections([.searchResult]) From 3f3d6575b85721ffd274d5713982bf18e62e5f68 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 5 May 2025 17:14:13 +0900 Subject: [PATCH 275/393] =?UTF-8?q?feat/#131:=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=9D=84=20=EC=9C=84=ED=95=9C=20API=20=ED=9D=90=EB=A6=84=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 --- .../Service/UserDefaultService.swift | 60 +++++++++++++++++++ .../Data/Data.xcodeproj/project.pbxproj | 4 ++ .../GetSearchPopupStoreRequestDTO.swift | 5 ++ .../GetSearchPopupStoreResponseDTO.swift | 17 ++++++ .../API/SearchAPI/SearchAPIEndPoint.swift | 17 ++++++ .../Search/SearchAPIRepositoryImpl.swift | 40 +++++++++++++ .../FetchKeywordBasePopupStoreListImpl.swift | 18 ++++++ .../KeywordBasePopupStoreListResponse.swift | 11 ++++ .../Repository/SearchAPIRepository.swift | 7 +++ .../FetchKeywordBasePopupStoreList.swift | 8 +++ .../SearchFeatureDemo/App/AppDelegate.swift | 5 ++ .../SearchFeatureDemo/App/SceneDelegate.swift | 3 +- 12 files changed, 194 insertions(+), 1 deletion(-) create mode 100644 Poppool/DataLayer/Data/Data/Network/API/SearchAPI/RequestDTO/GetSearchPopupStoreRequestDTO.swift create mode 100644 Poppool/DataLayer/Data/Data/Network/API/SearchAPI/ResponseDTO/GetSearchPopupStoreResponseDTO.swift create mode 100644 Poppool/DataLayer/Data/Data/Network/API/SearchAPI/SearchAPIEndPoint.swift create mode 100644 Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift create mode 100644 Poppool/DomainLayer/Domain/Domain/UseCaseImpl/Search/FetchKeywordBasePopupStoreListImpl.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Entity/SearchResponse/KeywordBasePopupStoreListResponse.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/Repository/SearchAPIRepository.swift create mode 100644 Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift index d3ef879b..52211c19 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Service/UserDefaultService.swift @@ -66,3 +66,63 @@ public final class UserDefaultService { UserDefaults.standard.removeObject(forKey: key) } } + +// MARK: - Key base +extension UserDefaultService { + public enum Key: String { + case searchKeyword = "searchList" + } + + /// Userdefault 데이터 저장 메서드 + /// - Parameters: + /// - key: 저장하는 데이터의 키 값 i.e) 유저 id 등 + /// - value: 저장하는 데이터 값 i.e) access token 등 + /// - to: 로컬 데이터베이스 타입 - DatabaseType + /// - Returns: 별도 안내 없음 + public func save(keyType: Key, value: String) { + UserDefaults.standard.set(value, forKey: keyType.rawValue) + } + + /// Userdefault 데이터 저장 메서드 + /// - Parameters: + /// - key: 저장하는 데이터의 키 값 i.e) 유저 id 등 + /// - value: 저장하는 데이터 값 i.e) access token 등 + /// - to: 로컬 데이터베이스 타입 - DatabaseType + /// - Returns: 별도 안내 없음 + public func save(keyType: Key, value: [String]) { + UserDefaults.standard.set(value, forKey: keyType.rawValue) + } + + /// Userdefault 데이터 발견 메서드 + /// - Parameters: + /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 + /// - from: 로컬 데이터베이스 타입 - DatabaseType + /// - Returns: 찾은 데이터 - String 타입 + public func fetch(keyType: Key) -> String? { + if let token = UserDefaults.standard.string(forKey: keyType.rawValue) { + return token + } + return nil + } + + /// Userdefault 데이터 발견 메서드 + /// - Parameters: + /// - key: 찾는 데이터의 키 값 i.e) 유저 id 등 + /// - from: 로컬 데이터베이스 타입 - DatabaseType + /// - Returns: 찾은 데이터 - String 타입 + public func fetchArray(keyType: Key) -> [String]? { + if let token = UserDefaults.standard.array(forKey: keyType.rawValue) as? [String] { + return token + } + return nil + } + + /// Userdefault 데이터 삭제 메서드 + /// - Parameters: + /// - key: 삭제하는 데이터의 키 값 i.e) 유저 id 등 + /// - from: 로컬 데이터베이스 타입 - DatabaseType + /// - Returns: 별도 안내 없음 + public func delete(keyType: Key) { + UserDefaults.standard.removeObject(forKey: keyType.rawValue) + } +} diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 1a8e09f1..427daac0 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -59,6 +59,9 @@ Network/API/PreSignedAPI/RequestDTO/PresignedURLRequestDTO.swift, Network/API/PreSignedAPI/ResponseDTO/PreSignedURLDTO.swift, Network/API/PreSignedAPI/ResponseDTO/PreSignedURLResponseDTO.swift, + Network/API/SearchAPI/RequestDTO/GetSearchPopupStoreRequestDTO.swift, + Network/API/SearchAPI/ResponseDTO/GetSearchPopupStoreResponseDTO.swift, + Network/API/SearchAPI/SearchAPIEndPoint.swift, Network/API/SignUpAPI/RequestDTO/CheckNickNameRequestDTO.swift, Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift, Network/API/SignUpAPI/SignUpAPIEndpoint.swift, @@ -105,6 +108,7 @@ RepositoryImpl/MapRepositoryImpl.swift, RepositoryImpl/PopUpAPIRepositoryImpl.swift, RepositoryImpl/PreSignedRepositoryImpl.swift, + RepositoryImpl/Search/SearchAPIRepositoryImpl.swift, RepositoryImpl/SignUpRepositoryImpl.swift, RepositoryImpl/UserAPIRepositoryImpl.swift, ); diff --git a/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/RequestDTO/GetSearchPopupStoreRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/RequestDTO/GetSearchPopupStoreRequestDTO.swift new file mode 100644 index 00000000..679b1e63 --- /dev/null +++ b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/RequestDTO/GetSearchPopupStoreRequestDTO.swift @@ -0,0 +1,5 @@ +import Foundation + +struct GetSearchPopupStoreRequestDTO: Encodable { + let query: String +} diff --git a/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/ResponseDTO/GetSearchPopupStoreResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/ResponseDTO/GetSearchPopupStoreResponseDTO.swift new file mode 100644 index 00000000..2872bf8a --- /dev/null +++ b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/ResponseDTO/GetSearchPopupStoreResponseDTO.swift @@ -0,0 +1,17 @@ +import Foundation + +import DomainInterface + +struct GetSearchPopupStoreResponseDTO: Decodable { + var popUpStoreList: [PopUpStoreResponseDTO] + var loginYn: Bool +} + +extension GetSearchPopupStoreResponseDTO { + func toDomain() -> KeywordBasePopupStoreListResponse { + return KeywordBasePopupStoreListResponse( + popupStoreList: popUpStoreList.map { $0.toDomain() }, + loginYn: loginYn + ) + } +} diff --git a/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/SearchAPIEndPoint.swift b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/SearchAPIEndPoint.swift new file mode 100644 index 00000000..9235420d --- /dev/null +++ b/Poppool/DataLayer/Data/Data/Network/API/SearchAPI/SearchAPIEndPoint.swift @@ -0,0 +1,17 @@ +import Foundation + +import Infrastructure + +import RxSwift + +struct SearchAPIEndPoint { + + static func getSearchPopUpList(request: GetSearchPopupStoreRequestDTO) -> Endpoint { + return Endpoint( + baseURL: Secrets.popPoolBaseURL, + path: "/search/popup-stores", + method: .get, + queryParameters: request + ) + } +} diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift new file mode 100644 index 00000000..1526ed6b --- /dev/null +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift @@ -0,0 +1,40 @@ +import Foundation + +import DomainInterface +import Infrastructure + +import RxSwift + +public final class SearchAPIRepositoryImpl: SearchAPIRepository { + + private let provider: Provider + private let tokenInterceptor = TokenInterceptor() + private let userDefaultService: UserDefaultService + + public init( + provider: Provider, + userDefaultService: UserDefaultService + ) { + self.provider = provider + self.userDefaultService = userDefaultService + } + + public func fetchSearchResult(by query: String) -> Observable { + self.saveSearchKeyword(keyword: query) + + let request = GetSearchPopupStoreRequestDTO(query: query) + let endPoint = SearchAPIEndPoint.getSearchPopUpList(request: request) + return provider.requestData( + with: endPoint, + interceptor: tokenInterceptor + ).map { $0.toDomain() } + } +} + +private extension SearchAPIRepositoryImpl { + func saveSearchKeyword(keyword: String) { + let existingList = userDefaultService.fetchArray(keyType: .searchKeyword) ?? [] + let updatedList = [keyword] + existingList.filter { $0 != keyword } + userDefaultService.save(keyType: .searchKeyword, value: updatedList) + } +} diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/Search/FetchKeywordBasePopupStoreListImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/Search/FetchKeywordBasePopupStoreListImpl.swift new file mode 100644 index 00000000..4b723585 --- /dev/null +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/Search/FetchKeywordBasePopupStoreListImpl.swift @@ -0,0 +1,18 @@ +import Foundation + +import DomainInterface + +import RxSwift + +public final class FetchKeywordBasePopupListUseCaseImpl: FetchKeywordBasePopupListUseCase { + + private let repository: SearchAPIRepository + + public init(repository: SearchAPIRepository) { + self.repository = repository + } + + public func execute(keyword: String) -> Observable { + return repository.fetchSearchResult(by: keyword) + } +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/SearchResponse/KeywordBasePopupStoreListResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/SearchResponse/KeywordBasePopupStoreListResponse.swift new file mode 100644 index 00000000..b1c60718 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/SearchResponse/KeywordBasePopupStoreListResponse.swift @@ -0,0 +1,11 @@ +import Foundation + +public struct KeywordBasePopupStoreListResponse { + public init(popupStoreList: [PopUpStoreResponse], loginYn: Bool) { + self.popupStoreList = popupStoreList + self.loginYn = loginYn + } + + public var popupStoreList: [PopUpStoreResponse] + public var loginYn: Bool +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/SearchAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SearchAPIRepository.swift new file mode 100644 index 00000000..4d428540 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SearchAPIRepository.swift @@ -0,0 +1,7 @@ +import Foundation + +import RxSwift + +public protocol SearchAPIRepository { + func fetchSearchResult(by query: String) -> Observable +} diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift new file mode 100644 index 00000000..754a07a6 --- /dev/null +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift @@ -0,0 +1,8 @@ +import Foundation + +import RxSwift + +public protocol FetchKeywordBasePopupListUseCase { + func execute(keyword: String) -> Observable +} + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index ad3e3fda..b162e314 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -29,9 +29,11 @@ extension AppDelegate { // MARK: Register Service DIContainer.register(Provider.self) { return ProviderImpl() } DIContainer.register(KeyChainService.self) { return KeyChainService() } + DIContainer.register(UserDefaultService.self) { return UserDefaultService() } // MARK: Resolve service @Dependency var provider: Provider + @Dependency var userDefaultService: UserDefaultService // MARK: Register repository DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } @@ -47,6 +49,7 @@ extension AppDelegate { DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } DIContainer.register(CategoryRepository.self) { return CategoryRepositoryImpl(provider: provider) } + DIContainer.register(SearchAPIRepository.self) { return SearchAPIRepositoryImpl(provider: provider, userDefaultService: userDefaultService) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -61,6 +64,7 @@ extension AppDelegate { @Dependency var kakaoLoginRepository: KakaoLoginRepository @Dependency var appleLoginRepository: AppleLoginRepository @Dependency var categoryRepository: CategoryRepository + @Dependency var searchAPIRepository: SearchAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -75,6 +79,7 @@ extension AppDelegate { DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } DIContainer.register(FetchCategoryListUseCase.self) { return FetchCategoryListUseCaseImpl(repository: categoryRepository) } + DIContainer.register(FetchKeywordBasePopupListUseCase.self) { return FetchKeywordBasePopupListUseCaseImpl(repository: searchAPIRepository) } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index 242654ab..001bc4d8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -16,7 +16,8 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) let vc = PopupSearchViewController() vc.reactor = PopupSearchReactor( - useCase: DIContainer.resolve(PopUpAPIUseCase.self) + useCase: DIContainer.resolve(PopUpAPIUseCase.self), + fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) ) window?.rootViewController = vc window?.makeKeyAndVisible() From f49470f54592d3eef5a208b7c1e221a891c25018 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 16:33:49 +0900 Subject: [PATCH 276/393] =?UTF-8?q?refactor/#131:=20=EC=9C=A0=EC=A6=88?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=9D=B4=EB=A6=84=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 19 ++++++++++++------- .../SearchFeatureDemo/App/SceneDelegate.swift | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 9d1911d9..ab7a1c8a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -58,11 +58,16 @@ public final class PopupSearchReactor: Reactor { var disposeBag = DisposeBag() private let userDefaultService = UserDefaultService() - private let useCase: PopUpAPIUseCase + private let popupAPIUseCase: PopUpAPIUseCase + private let fetchKeywordBasePopupListUseCase: FetchKeywordBasePopupListUseCase // MARK: - init - public init(useCase: PopUpAPIUseCase) { - self.useCase = useCase + public init( + popupAPIUseCase: PopUpAPIUseCase, + fetchKeywordBasePopupListUseCase: FetchKeywordBasePopupListUseCase + ) { + self.popupAPIUseCase = popupAPIUseCase + self.fetchKeywordBasePopupListUseCase = fetchKeywordBasePopupListUseCase self.initialState = State() } @@ -70,7 +75,7 @@ public final class PopupSearchReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - return useCase.getSearchBottomPopUpList( + return popupAPIUseCase.getSearchBottomPopUpList( isOpen: PopupStatus.open.requestValue, categories: [], page: 0, @@ -89,7 +94,7 @@ public final class PopupSearchReactor: Reactor { } case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: - return useCase.getSearchBottomPopUpList( + return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), page: 0, @@ -110,7 +115,7 @@ public final class PopupSearchReactor: Reactor { case .viewAllVisibleItems: guard currentState.hasNextPage else { return .empty() } - return useCase.getSearchBottomPopUpList( + return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), page: Int32(currentState.currentPage + 1), @@ -125,7 +130,7 @@ public final class PopupSearchReactor: Reactor { case .categoryCancelButtonTapped(let categoryID): Category.shared.removeItem(by: categoryID) - return useCase.getSearchBottomPopUpList( + return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), page: 0, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index 001bc4d8..134a042d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) let vc = PopupSearchViewController() vc.reactor = PopupSearchReactor( - useCase: DIContainer.resolve(PopUpAPIUseCase.self), + popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) ) window?.rootViewController = vc From c7e566b9ae5a0684467180fddb1210362a083e9f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 16:36:34 +0900 Subject: [PATCH 277/393] =?UTF-8?q?fix/#131:=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index a411f280..616cc402 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -153,8 +153,6 @@ private extension PopupSearchView { subitems: [item] ) - group.interItemSpacing = .fixed(6) - // Section let section = NSCollectionLayoutSection(group: group) section.orthogonalScrollingBehavior = .continuous @@ -165,6 +163,8 @@ private extension PopupSearchView { section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) } + section.interGroupSpacing = 6 + section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] return section From e83fa262ced138187898026430da0bf29a746f09 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 17:49:39 +0900 Subject: [PATCH 278/393] =?UTF-8?q?refactor/#131:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 101 ++++++++++-------- .../View/PopupSearchViewController.swift | 2 +- 2 files changed, 60 insertions(+), 43 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index ab7a1c8a..b1900fd5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -12,21 +12,16 @@ public final class PopupSearchReactor: Reactor { // MARK: - Reactor public enum Action { case viewDidLoad + + case loadNextPage + + case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped - case viewAllVisibleItems case categoryCancelButtonTapped(categoryID: Int) } public enum Mutation { - case setInitialState( - recentSearchItems: [TagCollectionViewCell.Input], - categoryItems: [TagCollectionViewCell.Input], - searchResultsItems: [PPPopupGridCollectionViewCell.Input], - totalPagesCount: Int32, - totalElementCount: Int64 - ) - case updateSearchResult( recentSearchItems: [TagCollectionViewCell.Input], categoryItems: [TagCollectionViewCell.Input], @@ -35,7 +30,13 @@ public final class PopupSearchReactor: Reactor { totalElementCount: Int64 ) - case fetchNextPage(searchResultsItems: [PPPopupGridCollectionViewCell.Input]) + case setupRecentSearch(items: [TagCollectionViewCell.Input]) + case setupCategory(items: [TagCollectionViewCell.Input]) + case setupSearchResult(items: [PPPopupGridCollectionViewCell.Input]) + case setupTotalPageCount(count: Int32) + case setupTotalElementCount(count: Int64) + + case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) } public struct State { @@ -66,7 +67,7 @@ public final class PopupSearchReactor: Reactor { popupAPIUseCase: PopUpAPIUseCase, fetchKeywordBasePopupListUseCase: FetchKeywordBasePopupListUseCase ) { - self.popupAPIUseCase = popupAPIUseCase + self.popupAPIUseCase = popupAPIUseCase self.fetchKeywordBasePopupListUseCase = fetchKeywordBasePopupListUseCase self.initialState = State() } @@ -83,48 +84,55 @@ public final class PopupSearchReactor: Reactor { sort: PopupSortOption.newest.requestValue ) .withUnretained(self) - .map { owner, response in - return .setInitialState( - recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.getCancelableCategoryItems(), - searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPagesCount: response.totalPages, - totalElementCount: response.totalElements - ) + .flatMap { (owner, response) -> Observable in + let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + + return Observable.concat([ + .just(.setupRecentSearch(items: owner.getRecentSearchKeywords())), + .just(.setupCategory(items: Category.shared.items)), + .just(.setupSearchResult(items: searchResultItems)), + .just(.setupTotalPageCount(count: response.totalPages)), + .just(.setupTotalElementCount(count: response.totalElements)) + ]) } - case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: + case .loadNextPage: + guard currentState.hasNextPage else { + return .empty() } + return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), - page: 0, + page: Int32(currentState.currentPage + 1), size: Int32(currentState.paginationSize), sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) - .map { (owner, response) in - return .updateSearchResult( - recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.getCancelableCategoryItems(), - searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPagesCount: response.totalPages, - totalElementCount: response.totalElements - ) - } + .flatMap { (owner, response) -> Observable in + let searchResultItems = owner.convertResponseToSearchResultInput(response: response) - case .viewAllVisibleItems: - guard currentState.hasNextPage else { return .empty() } + return Observable.concat([ + .just(.appendSearchResult(items: searchResultItems)) + ]) + } + case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, categories: Category.shared.getSelectedCategoryIDs(), - page: Int32(currentState.currentPage + 1), + page: 0, size: Int32(currentState.paginationSize), sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) .map { (owner, response) in - return .fetchNextPage(searchResultsItems: owner.convertResponseToSearchResultInput(response: response)) + return .updateSearchResult( + recentSearchItems: owner.getRecentSearchKeywords(), + categoryItems: Category.shared.getCancelableCategoryItems(), + searchResultsItems: owner.convertResponseToSearchResultInput(response: response), + totalPagesCount: response.totalPages, + totalElementCount: response.totalElements + ) } case .categoryCancelButtonTapped(let categoryID): @@ -154,14 +162,23 @@ public final class PopupSearchReactor: Reactor { public func reduce(state: State, mutation: Mutation) -> State { var newState = state switch mutation { - case .setInitialState(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): - newState.recentSearchItems = recentSearchItems - newState.categoryItems = categoryItems - newState.searchResultItems = searchResultItems - newState.totalPagesCount = Int(totalPagesCount) - newState.totalElementsCount = Int(totalElementsCount) + case .setupRecentSearch(let items): + newState.recentSearchItems = items + + case .setupCategory(let items): + newState.categoryItems = items + + case .setupSearchResult(let items): + newState.searchResultItems = items + + case .setupTotalPageCount(let count): + newState.totalPagesCount = Int(count) + + case .setupTotalElementCount(let count): + newState.totalElementsCount = Int(count) + case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems @@ -172,8 +189,8 @@ public final class PopupSearchReactor: Reactor { newState.totalPagesCount = Int(totalPagesCount) newState.totalElementsCount = Int(totalElementsCount) - case .fetchNextPage(let searchResultItems): - newState.searchResultItems += searchResultItems + case .appendSearchResult(let items): + newState.searchResultItems += items newState.currentPage += 1 } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index ae9bb4de..1d17b2f9 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -109,7 +109,7 @@ extension PopupSearchViewController { $0.item >= owner.mainView.collectionView.numberOfItems(inSection: $0.section) - prefetchCount } - if isNearBottom { owner.reactor?.action.onNext(.viewAllVisibleItems) } + if isNearBottom { owner.reactor?.action.onNext(.loadNextPage) } } .disposed(by: disposeBag) From 219e951dc6ad2dd907418b7b35490f396d4fb2ed Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 17:55:42 +0900 Subject: [PATCH 279/393] =?UTF-8?q?recator/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20=EB=A6=AC?= =?UTF-8?q?=EC=95=A1=ED=84=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 21 ++++++++++--------- .../View/PopupSearchViewController.swift | 2 +- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index b1900fd5..f8d967fe 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -13,12 +13,13 @@ public final class PopupSearchReactor: Reactor { public enum Action { case viewDidLoad + case categoryTagRemoveButtonTapped(categoryID: Int) + case loadNextPage case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped - case categoryCancelButtonTapped(categoryID: Int) } public enum Mutation { @@ -135,7 +136,7 @@ public final class PopupSearchReactor: Reactor { ) } - case .categoryCancelButtonTapped(let categoryID): + case .categoryTagRemoveButtonTapped(let categoryID): Category.shared.removeItem(by: categoryID) return popupAPIUseCase.getSearchBottomPopUpList( @@ -146,15 +147,15 @@ public final class PopupSearchReactor: Reactor { sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) - .map { (owner, response) in - return .updateSearchResult( - recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.getCancelableCategoryItems(), - searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPagesCount: response.totalPages, - totalElementCount: response.totalElements - ) + .flatMap { (owner, response) -> Observable in + let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + return Observable.concat([ + .just(.setupCategory(items: Category.shared.items)), + .just(.setupSearchResult(items: searchResultItems)), + .just(.setupTotalPageCount(count: response.totalPages)), + .just(.setupTotalElementCount(count: response.totalElements)) + ]) } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 1d17b2f9..145fa8c4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -114,7 +114,7 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.canceledCategoryID - .map { Reactor.Action.categoryCancelButtonTapped(categoryID: $0) } + .map { Reactor.Action.categoryTagRemoveButtonTapped(categoryID: $0) } .bind(to: reactor.action) .disposed(by: disposeBag) From 9a02893921c0041b4c5bf5b9501f21ef8f52f984 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 20:03:43 +0900 Subject: [PATCH 280/393] =?UTF-8?q?refactor/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20present=20=EB=A6=AC=EC=95=A1=ED=84=B0=20?= =?UTF-8?q?=ED=9D=90=EB=A6=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 28 ++++++++- .../View/PopupSearchViewController.swift | 59 ++++++++++++------- 2 files changed, 62 insertions(+), 25 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index f8d967fe..739fea78 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -14,10 +14,15 @@ public final class PopupSearchReactor: Reactor { case viewDidLoad case categoryTagRemoveButtonTapped(categoryID: Int) + case categoryTagButtonTapped case loadNextPage + case recentSearchTagButtonTapped + case searchResultItemTapped + + case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped } @@ -38,6 +43,12 @@ public final class PopupSearchReactor: Reactor { case setupTotalElementCount(count: Int64) case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) + + case present(target: PresentTarget) + } + + public enum PresentTarget { + case categorySelector } public struct State { @@ -47,6 +58,8 @@ public final class PopupSearchReactor: Reactor { var openTitle: String = PopupStatus.open.title var sortOptionTitle: String = PopupSortOption.newest.title + @Pulse var presentTarget: PresentTarget? + fileprivate var currentPage: Int = 0 fileprivate let paginationSize: Int = 10 fileprivate var totalPagesCount: Int = 0 @@ -98,8 +111,7 @@ public final class PopupSearchReactor: Reactor { } case .loadNextPage: - guard currentState.hasNextPage else { - return .empty() } + guard currentState.hasNextPage else { return .empty() } return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, @@ -117,6 +129,12 @@ public final class PopupSearchReactor: Reactor { ]) } + case .categoryTagButtonTapped: + return .just(.present(target: .categorySelector)) + + case .recentSearchTagButtonTapped: return .empty() + case .searchResultItemTapped: return .empty() + case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: return popupAPIUseCase.getSearchBottomPopUpList( isOpen: FilterOption.shared.status.requestValue, @@ -178,8 +196,12 @@ public final class PopupSearchReactor: Reactor { case .setupTotalElementCount(let count): newState.totalElementsCount = Int(count) + case .present(let target): + switch target { + case .categorySelector: + newState.presentTarget = .categorySelector + } - case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 145fa8c4..590734c4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -39,34 +39,20 @@ extension PopupSearchViewController { mainView.collectionView.rx.itemSelected .withUnretained(self) - .subscribe(onNext: { (owner, indexPath) in + .compactMap { (owner, indexPath) in let sections = owner.mainView.getSectionsFromDataSource() - guard indexPath.section < sections.count else { return } + guard indexPath.section < sections.count else { return nil } switch sections[indexPath.section] { - case .recentSearch: return - case .category: - let categoryReactor = CategorySelectReactor( - fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) - ) - let viewController = CategorySelectViewController() - viewController.reactor = categoryReactor - - viewController.reactor?.state - .filter { $0.isSaveOrResetButtonTapped == true } - .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } - .bind(to: reactor.action) - .disposed(by: self.disposeBag) - - self.presentPanModal(viewController) - case .searchResult: - // MARK: 디테일 화면으로 이동하기 - // TODO: Presentation에 의존 역전이 되지 않도록 할 방법 고민하기 - print("SECTION DEBUG:", sections[indexPath.section]) + case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped + case .category: return Reactor.Action.categoryTagButtonTapped + case .searchResult: return Reactor.Action.searchResultItemTapped } - }) + } + .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.filterOptionButtonTapped .withUnretained(self) .subscribe { (owner, _) in @@ -118,6 +104,8 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + self.bindState(reactor: reactor) + reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -137,4 +125,31 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) } + + private func bindState(reactor: Reactor) { + reactor.pulse(\.$presentTarget) + .withUnretained(self) + .subscribe { owner, target in + switch target { + case .categorySelector: + let categoryReactor = CategorySelectReactor( + fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) + ) + let viewController = CategorySelectViewController() + viewController.reactor = categoryReactor + + // 완료 후 다시 Reactor로 Action 보내기 + categoryReactor.state + .filter { $0.isSaveOrResetButtonTapped } + .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } + .bind(to: owner.reactor!.action) + .disposed(by: owner.disposeBag) + + owner.presentPanModal(viewController) + + default: break + } + } + .disposed(by: disposeBag) + } } From 77c4291cc3cc77e3ccf8211dae1fdc20807d5be2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 20:21:49 +0900 Subject: [PATCH 281/393] =?UTF-8?q?refactor/#131:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 12 ++- .../Source/View/PopupSearchView.swift | 4 +- .../View/PopupSearchViewController.swift | 95 ++++++++++--------- 3 files changed, 63 insertions(+), 48 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 739fea78..ed3076a5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -13,14 +13,16 @@ public final class PopupSearchReactor: Reactor { public enum Action { case viewDidLoad + case recentSearchTagButtonTapped + case categoryTagRemoveButtonTapped(categoryID: Int) case categoryTagButtonTapped + case filterOptionButtonTapped + case searchResultItemTapped case loadNextPage - case recentSearchTagButtonTapped - case searchResultItemTapped case filterOptionSaveButtonTapped @@ -49,6 +51,7 @@ public final class PopupSearchReactor: Reactor { public enum PresentTarget { case categorySelector + case filterOptionSelector } public struct State { @@ -175,6 +178,9 @@ public final class PopupSearchReactor: Reactor { .just(.setupTotalElementCount(count: response.totalElements)) ]) } + + case .filterOptionButtonTapped: + return .just(.present(target: .filterOptionSelector)) } } @@ -200,6 +206,8 @@ public final class PopupSearchReactor: Reactor { switch target { case .categorySelector: newState.presentTarget = .categorySelector + case .filterOptionSelector: + newState.presentTarget = .filterOptionSelector } case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 616cc402..5a24faf7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -29,7 +29,7 @@ final class PopupSearchView: UIView { } // MARK: - Properties - let canceledCategoryID = PublishRelay() + let categoryTagRemoveButtonTapped = PublishRelay() let filterOptionButtonTapped = PublishRelay() let searchBar = PPSearchBarView() @@ -254,7 +254,7 @@ extension PopupSearchView { cell.cancelButton.rx.tap .compactMap { categoryItem.id } - .bind(to: self.canceledCategoryID) + .bind(to: self.categoryTagRemoveButtonTapped) .disposed(by: cell.disposeBag) return cell diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 590734c4..2ca9eaea 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -32,45 +32,11 @@ extension PopupSearchViewController { // MARK: - Bind extension PopupSearchViewController { public func bind(reactor: Reactor) { - rx.viewDidLoad - .map { Reactor.Action.viewDidLoad } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.collectionView.rx.itemSelected - .withUnretained(self) - .compactMap { (owner, indexPath) in - let sections = owner.mainView.getSectionsFromDataSource() - guard indexPath.section < sections.count else { return nil } - - switch sections[indexPath.section] { - case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped - case .category: return Reactor.Action.categoryTagButtonTapped - case .searchResult: return Reactor.Action.searchResultItemTapped - } - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - - mainView.filterOptionButtonTapped - .withUnretained(self) - .subscribe { (owner, _) in - let viewController = FilterOptionSelectViewController() - viewController.reactor = FilterOptionSelectReactor() - - viewController.reactor?.state - .filter { $0.isSaveButtonTapped == true } - .map { _ in Reactor.Action.filterOptionSaveButtonTapped } - .bind(to: reactor.action) - .disposed(by: owner.disposeBag) - - owner.presentPanModal(viewController) - } - .disposed(by: disposeBag) + self.bindAction(reactor: reactor) + self.bindState(reactor: reactor) mainView.collectionView.rx.prefetchItems - .throttle(.seconds(1), latest: false , scheduler: MainScheduler.instance) + .throttle(.seconds(1), latest: false , scheduler: MainScheduler.asyncInstance) .withUnretained(self) .subscribe { (owner, indexPaths) in let sections = owner.mainView.getSectionsFromDataSource() @@ -99,13 +65,6 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) - mainView.canceledCategoryID - .map { Reactor.Action.categoryTagRemoveButtonTapped(categoryID: $0) } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - self.bindState(reactor: reactor) - reactor.state .withUnretained(self) .subscribe { (owner, state) in @@ -126,6 +85,42 @@ extension PopupSearchViewController { .disposed(by: disposeBag) } + private func bindAction(reactor: Reactor) { + rx.viewDidLoad + .map { Reactor.Action.viewDidLoad } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.collectionView.rx.itemSelected + .compactMap { indexPath in + let sections = self.mainView.getSectionsFromDataSource() + guard indexPath.section < sections.count else { return nil } + + switch sections[indexPath.section] { + case .recentSearch: + return Reactor.Action.recentSearchTagButtonTapped + + case .category: + return Reactor.Action.categoryTagButtonTapped + + case .searchResult: + return Reactor.Action.searchResultItemTapped + } + } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.categoryTagRemoveButtonTapped + .map { Reactor.Action.categoryTagRemoveButtonTapped(categoryID: $0) } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.filterOptionButtonTapped + .map { Reactor.Action.filterOptionButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + } + private func bindState(reactor: Reactor) { reactor.pulse(\.$presentTarget) .withUnretained(self) @@ -147,6 +142,18 @@ extension PopupSearchViewController { owner.presentPanModal(viewController) + case .filterOptionSelector: + let viewController = FilterOptionSelectViewController() + viewController.reactor = FilterOptionSelectReactor() + + viewController.reactor?.state + .filter { $0.isSaveButtonTapped == true } + .map { _ in Reactor.Action.filterOptionSaveButtonTapped } + .bind(to: reactor.action) + .disposed(by: owner.disposeBag) + + owner.presentPanModal(viewController) + default: break } } From aedac216dfdd5edc86911075d40616e1ca8128e0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 20:33:39 +0900 Subject: [PATCH 282/393] =?UTF-8?q?fix/#131:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=EC=9D=B4=20=EC=A2=85=EC=A2=85=20?= =?UTF-8?q?=EC=95=88=EB=90=98=EB=8D=98=20=EB=AC=B8=EC=A0=9C=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 --- .../SearchFeature/Source/View/PopupSearchViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 2ca9eaea..c36c1055 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -36,7 +36,7 @@ extension PopupSearchViewController { self.bindState(reactor: reactor) mainView.collectionView.rx.prefetchItems - .throttle(.seconds(1), latest: false , scheduler: MainScheduler.asyncInstance) + .throttle(.milliseconds(100), latest: false , scheduler: MainScheduler.asyncInstance) .withUnretained(self) .subscribe { (owner, indexPaths) in let sections = owner.mainView.getSectionsFromDataSource() From 9b56f1d8c2cd0a2c9e80a1a2fa6066cb0ada9015 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 22:02:09 +0900 Subject: [PATCH 283/393] =?UTF-8?q?refactor/#131:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=ED=9D=90=EB=A6=84=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 100 ++++++++++++++---- .../View/PopupSearchViewController.swift | 90 ++++++++++------ .../Source/View/SearchResultHeaderView.swift | 8 +- 3 files changed, 139 insertions(+), 59 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index ed3076a5..2131533a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -22,6 +22,8 @@ public final class PopupSearchReactor: Reactor { case searchResultItemTapped case loadNextPage + case searchResultPrefetchItems(indexPathList: [IndexPath]) + @@ -43,10 +45,14 @@ public final class PopupSearchReactor: Reactor { case setupSearchResult(items: [PPPopupGridCollectionViewCell.Input]) case setupTotalPageCount(count: Int32) case setupTotalElementCount(count: Int64) + case setupSearchResultHeader(item: SearchResultHeaderView.Input) case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) case present(target: PresentTarget) + + case updateCurrentPage(to: Int) + case updateDataSource } public enum PresentTarget { @@ -58,10 +64,10 @@ public final class PopupSearchReactor: Reactor { var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] - var openTitle: String = PopupStatus.open.title - var sortOptionTitle: String = PopupSortOption.newest.title + var searchResultHeader: SearchResultHeaderView.Input? = nil - @Pulse var presentTarget: PresentTarget? + @Pulse var present: PresentTarget? + @Pulse var updateDataSource: Void? fileprivate var currentPage: Int = 0 fileprivate let paginationSize: Int = 10 @@ -109,7 +115,8 @@ public final class PopupSearchReactor: Reactor { .just(.setupCategory(items: Category.shared.items)), .just(.setupSearchResult(items: searchResultItems)), .just(.setupTotalPageCount(count: response.totalPages)), - .just(.setupTotalElementCount(count: response.totalElements)) + .just(.setupTotalElementCount(count: response.totalElements)), + .just(.updateDataSource) ]) } @@ -128,9 +135,45 @@ public final class PopupSearchReactor: Reactor { let searchResultItems = owner.convertResponseToSearchResultInput(response: response) return Observable.concat([ - .just(.appendSearchResult(items: searchResultItems)) + .just(.appendSearchResult(items: searchResultItems)), + .just(.updateDataSource) ]) } + case .searchResultPrefetchItems(let indexPathList): + // 마지막 섹션의 마지막 아이템 + guard let lastItemIndex = indexPathList.last?.last else { return .empty() } + + func isPrefetchable(prefetchCount: Int = 4) -> Bool { + let isScrollToEnd = lastItemIndex > currentState.paginationSize * (currentState.currentPage + 1) - prefetchCount + + let hasNextPage = currentState.currentPage < (currentState.totalPagesCount - 1) + + return isScrollToEnd && hasNextPage + } + + if isPrefetchable() { + return popupAPIUseCase.getSearchBottomPopUpList( + isOpen: FilterOption.shared.status.requestValue, + categories: Category.shared.getSelectedCategoryIDs(), + page: Int32(currentState.currentPage + 1), + size: Int32(currentState.paginationSize), + sort: FilterOption.shared.sortOption.requestValue + ) + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + + + return .concat([ + .just(.appendSearchResult(items: searchResultItems)), + .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), + .just(.updateDataSource) + ]) + } + + } + return .empty() + case .categoryTagButtonTapped: return .just(.present(target: .categorySelector)) @@ -147,14 +190,16 @@ public final class PopupSearchReactor: Reactor { sort: FilterOption.shared.sortOption.requestValue ) .withUnretained(self) - .map { (owner, response) in - return .updateSearchResult( - recentSearchItems: owner.getRecentSearchKeywords(), - categoryItems: Category.shared.getCancelableCategoryItems(), - searchResultsItems: owner.convertResponseToSearchResultInput(response: response), - totalPagesCount: response.totalPages, - totalElementCount: response.totalElements - ) + .flatMap { (owner, response) -> Observable in + return .concat([ + .just(.setupRecentSearch(items: owner.getRecentSearchKeywords())), + .just(.setupCategory(items: Category.shared.getCancelableCategoryItems())), + .just(.setupSearchResult(items: owner.convertResponseToSearchResultInput(response: response))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: Int(response.totalElements)))), + .just(.setupTotalPageCount(count: response.totalPages)), + .just(.setupTotalElementCount(count: response.totalElements)), + .just(.updateDataSource) + ]) } case .categoryTagRemoveButtonTapped(let categoryID): @@ -175,7 +220,8 @@ public final class PopupSearchReactor: Reactor { .just(.setupCategory(items: Category.shared.items)), .just(.setupSearchResult(items: searchResultItems)), .just(.setupTotalPageCount(count: response.totalPages)), - .just(.setupTotalElementCount(count: response.totalElements)) + .just(.setupTotalElementCount(count: response.totalElements)), + .just(.updateDataSource) ]) } @@ -202,27 +248,35 @@ public final class PopupSearchReactor: Reactor { case .setupTotalElementCount(let count): newState.totalElementsCount = Int(count) + case .setupSearchResultHeader(let input): + newState.searchResultHeader = input + + case .appendSearchResult(let items): + newState.searchResultItems += items + + + case .updateCurrentPage(let currentPage): + newState.currentPage = currentPage + + case .updateDataSource: + newState.updateDataSource = () + case .present(let target): switch target { case .categorySelector: - newState.presentTarget = .categorySelector + newState.present = .categorySelector case .filterOptionSelector: - newState.presentTarget = .filterOptionSelector + newState.present = .filterOptionSelector } case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): newState.recentSearchItems = recentSearchItems newState.categoryItems = categoryItems newState.searchResultItems = searchResultItems - newState.openTitle = FilterOption.shared.status.title - newState.sortOptionTitle = FilterOption.shared.sortOption.title newState.currentPage = 0 newState.totalPagesCount = Int(totalPagesCount) newState.totalElementsCount = Int(totalElementsCount) - case .appendSearchResult(let items): - newState.searchResultItems += items - newState.currentPage += 1 } return newState @@ -251,4 +305,8 @@ private extension PopupSearchReactor { ) } } + + func makeSearchResultHeaderInput(count: Int, title: String = FilterOption.shared.title) -> SearchResultHeaderView.Input { + return SearchResultHeaderView.Input(count: count, sortedTitle: title) + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index c36c1055..4d91c1f8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -36,39 +36,15 @@ extension PopupSearchViewController { self.bindState(reactor: reactor) mainView.collectionView.rx.prefetchItems - .throttle(.milliseconds(100), latest: false , scheduler: MainScheduler.asyncInstance) - .withUnretained(self) - .subscribe { (owner, indexPaths) in - let sections = owner.mainView.getSectionsFromDataSource() - - guard let searchResultSectionIndex = sections.firstIndex(where: { section in - switch section { - case .searchResult: return true - default: return false - } - }) else { return } - - /// prefetch를 하기까지 남은 아이템의 갯수 - let prefetchCount = 2 - - let itemCount = owner.mainView.collectionView.numberOfItems(inSection: searchResultSectionIndex) - - guard itemCount > prefetchCount else { return } - - /// 보여줄 아이템이 prefetchCount만큼 남았을때 가까운 상태라고 확인 - let isNearBottom = indexPaths.contains { - $0.section == searchResultSectionIndex && - $0.item >= owner.mainView.collectionView.numberOfItems(inSection: $0.section) - prefetchCount - } - - if isNearBottom { owner.reactor?.action.onNext(.loadNextPage) } - } + .throttle(.milliseconds(100), latest: false, scheduler: MainScheduler.asyncInstance) + .map(Reactor.Action.searchResultPrefetchItems) + .bind(to: reactor.action) .disposed(by: disposeBag) - reactor.state + reactor.pulse(\.$updateDataSource) + .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in - owner.mainView.updateSnapshot( recentSearchItems: state.recentSearchItems .map(PopupSearchView.SectionItem.recentSearchItem), @@ -76,13 +52,59 @@ extension PopupSearchViewController { .map(PopupSearchView.SectionItem.categoryItem), searchResultItems: state.searchResultItems .map(PopupSearchView.SectionItem.searchResultItem), - headerInput: SearchResultHeaderView.Input( - count: state.totalElementsCount, - sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") - ) + headerInput: state.searchResultHeader ) } .disposed(by: disposeBag) +// +// +// mainView.collectionView.rx.prefetchItems +// .throttle(.milliseconds(100), latest: false , scheduler: MainScheduler.asyncInstance) +// .withUnretained(self) +// .subscribe { (owner, indexPaths) in +// let sections = owner.mainView.getSectionsFromDataSource() +// +// guard let searchResultSectionIndex = sections.firstIndex(where: { section in +// switch section { +// case .searchResult: return true +// default: return false +// } +// }) else { return } +// +// /// prefetch를 하기까지 남은 아이템의 갯수 +// let prefetchCount = 2 +// +// let itemCount = owner.mainView.collectionView.numberOfItems(inSection: searchResultSectionIndex) +// +// guard itemCount > prefetchCount else { return } +// +// /// 보여줄 아이템이 prefetchCount만큼 남았을때 가까운 상태라고 확인 +// let isNearBottom = indexPaths.contains { +// $0.section == searchResultSectionIndex && +// $0.item >= owner.mainView.collectionView.numberOfItems(inSection: $0.section) - prefetchCount +// } +// +// if isNearBottom { owner.reactor?.action.onNext(.loadNextPage) } +// } +// .disposed(by: disposeBag) +// +// reactor.state +// .withUnretained(self) +// .subscribe { (owner, state) in +// owner.mainView.updateSnapshot( +// recentSearchItems: state.recentSearchItems +// .map(PopupSearchView.SectionItem.recentSearchItem), +// categoryItems: state.categoryItems +// .map(PopupSearchView.SectionItem.categoryItem), +// searchResultItems: state.searchResultItems +// .map(PopupSearchView.SectionItem.searchResultItem), +// headerInput: SearchResultHeaderView.Input( +// count: state.totalElementsCount, +// sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") +// ) +// ) +// } +// .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { @@ -122,7 +144,7 @@ extension PopupSearchViewController { } private func bindState(reactor: Reactor) { - reactor.pulse(\.$presentTarget) + reactor.pulse(\.$present) .withUnretained(self) .subscribe { owner, target in switch target { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index 720470c4..eb554be6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -6,7 +6,7 @@ import SnapKit import RxSwift import Then -final class SearchResultHeaderView: UICollectionReusableView { +public final class SearchResultHeaderView: UICollectionReusableView { enum Identifier: String { case searchResult = "PopupGridCollectionHeaderView.searchResult" @@ -42,7 +42,7 @@ final class SearchResultHeaderView: UICollectionReusableView { fatalError("\(#file), \(#function) Error") } - override func prepareForReuse() { + public override func prepareForReuse() { super.prepareForReuse() disposeBag = DisposeBag() } @@ -88,12 +88,12 @@ private extension SearchResultHeaderView { } extension SearchResultHeaderView: Inputable { - struct Input { + public struct Input { var count: Int? var sortedTitle: String? } - func injection(with input: Input) { + public func injection(with input: Input) { if let count = input.count { cellCountLabel.text = "총 \(count)개" } From d722253ec19d7b8aee4a68e1943ee530af9cd5f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 22:08:15 +0900 Subject: [PATCH 284/393] =?UTF-8?q?refactor/#131:=20=ED=9D=90=EB=A6=84=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0=20=EA=B1=B0=EC=9D=98=20=EB=A7=88=EB=AC=B4?= =?UTF-8?q?=EB=A6=AC...=3F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 41 +++----- .../View/PopupSearchViewController.swift | 95 +++++-------------- 2 files changed, 37 insertions(+), 99 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 2131533a..6774c5a7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -32,14 +32,6 @@ public final class PopupSearchReactor: Reactor { } public enum Mutation { - case updateSearchResult( - recentSearchItems: [TagCollectionViewCell.Input], - categoryItems: [TagCollectionViewCell.Input], - searchResultsItems: [PPPopupGridCollectionViewCell.Input], - totalPagesCount: Int32, - totalElementCount: Int64 - ) - case setupRecentSearch(items: [TagCollectionViewCell.Input]) case setupCategory(items: [TagCollectionViewCell.Input]) case setupSearchResult(items: [PPPopupGridCollectionViewCell.Input]) @@ -108,10 +100,10 @@ public final class PopupSearchReactor: Reactor { ) .withUnretained(self) .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + let searchResultItems = owner.makeSearchResultInputs(response: response) return Observable.concat([ - .just(.setupRecentSearch(items: owner.getRecentSearchKeywords())), + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), .just(.setupCategory(items: Category.shared.items)), .just(.setupSearchResult(items: searchResultItems)), .just(.setupTotalPageCount(count: response.totalPages)), @@ -132,7 +124,7 @@ public final class PopupSearchReactor: Reactor { ) .withUnretained(self) .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + let searchResultItems = owner.makeSearchResultInputs(response: response) return Observable.concat([ .just(.appendSearchResult(items: searchResultItems)), @@ -161,7 +153,7 @@ public final class PopupSearchReactor: Reactor { ) .withUnretained(self) .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + let searchResultItems = owner.makeSearchResultInputs(response: response) return .concat([ @@ -192,9 +184,9 @@ public final class PopupSearchReactor: Reactor { .withUnretained(self) .flatMap { (owner, response) -> Observable in return .concat([ - .just(.setupRecentSearch(items: owner.getRecentSearchKeywords())), - .just(.setupCategory(items: Category.shared.getCancelableCategoryItems())), - .just(.setupSearchResult(items: owner.convertResponseToSearchResultInput(response: response))), + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: Int(response.totalElements)))), .just(.setupTotalPageCount(count: response.totalPages)), .just(.setupTotalElementCount(count: response.totalElements)), @@ -214,7 +206,7 @@ public final class PopupSearchReactor: Reactor { ) .withUnretained(self) .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.convertResponseToSearchResultInput(response: response) + let searchResultItems = owner.makeSearchResultInputs(response: response) return Observable.concat([ .just(.setupCategory(items: Category.shared.items)), @@ -268,15 +260,6 @@ public final class PopupSearchReactor: Reactor { case .filterOptionSelector: newState.present = .filterOptionSelector } - - case .updateSearchResult(let recentSearchItems, let categoryItems, let searchResultItems, let totalPagesCount, let totalElementsCount): - newState.recentSearchItems = recentSearchItems - newState.categoryItems = categoryItems - newState.searchResultItems = searchResultItems - newState.currentPage = 0 - newState.totalPagesCount = Int(totalPagesCount) - newState.totalElementsCount = Int(totalElementsCount) - } return newState @@ -285,12 +268,16 @@ public final class PopupSearchReactor: Reactor { // MARK: - Functions private extension PopupSearchReactor { - func getRecentSearchKeywords() -> [TagCollectionViewCell.Input] { + func makeRecentSearchItems() -> [TagCollectionViewCell.Input] { let searchKeywords = userDefaultService.fetchArray(key: "searchList") ?? [] return searchKeywords.map { TagCollectionViewCell.Input(title: $0) } } - func convertResponseToSearchResultInput(response: GetSearchBottomPopUpListResponse) -> [PPPopupGridCollectionViewCell.Input] { + func makeCategoryItems() -> [TagCollectionViewCell.Input] { + return Category.shared.getCancelableCategoryItems() + } + + func makeSearchResultInputs(response: GetSearchBottomPopUpListResponse) -> [PPPopupGridCollectionViewCell.Input] { return response.popUpStoreList.map { PPPopupGridCollectionViewCell.Input( imagePath: $0.mainImageUrl, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 4d91c1f8..1982b703 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -34,77 +34,6 @@ extension PopupSearchViewController { public func bind(reactor: Reactor) { self.bindAction(reactor: reactor) self.bindState(reactor: reactor) - - mainView.collectionView.rx.prefetchItems - .throttle(.milliseconds(100), latest: false, scheduler: MainScheduler.asyncInstance) - .map(Reactor.Action.searchResultPrefetchItems) - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.pulse(\.$updateDataSource) - .withLatestFrom(reactor.state) - .withUnretained(self) - .subscribe { (owner, state) in - owner.mainView.updateSnapshot( - recentSearchItems: state.recentSearchItems - .map(PopupSearchView.SectionItem.recentSearchItem), - categoryItems: state.categoryItems - .map(PopupSearchView.SectionItem.categoryItem), - searchResultItems: state.searchResultItems - .map(PopupSearchView.SectionItem.searchResultItem), - headerInput: state.searchResultHeader - ) - } - .disposed(by: disposeBag) -// -// -// mainView.collectionView.rx.prefetchItems -// .throttle(.milliseconds(100), latest: false , scheduler: MainScheduler.asyncInstance) -// .withUnretained(self) -// .subscribe { (owner, indexPaths) in -// let sections = owner.mainView.getSectionsFromDataSource() -// -// guard let searchResultSectionIndex = sections.firstIndex(where: { section in -// switch section { -// case .searchResult: return true -// default: return false -// } -// }) else { return } -// -// /// prefetch를 하기까지 남은 아이템의 갯수 -// let prefetchCount = 2 -// -// let itemCount = owner.mainView.collectionView.numberOfItems(inSection: searchResultSectionIndex) -// -// guard itemCount > prefetchCount else { return } -// -// /// 보여줄 아이템이 prefetchCount만큼 남았을때 가까운 상태라고 확인 -// let isNearBottom = indexPaths.contains { -// $0.section == searchResultSectionIndex && -// $0.item >= owner.mainView.collectionView.numberOfItems(inSection: $0.section) - prefetchCount -// } -// -// if isNearBottom { owner.reactor?.action.onNext(.loadNextPage) } -// } -// .disposed(by: disposeBag) -// -// reactor.state -// .withUnretained(self) -// .subscribe { (owner, state) in -// owner.mainView.updateSnapshot( -// recentSearchItems: state.recentSearchItems -// .map(PopupSearchView.SectionItem.recentSearchItem), -// categoryItems: state.categoryItems -// .map(PopupSearchView.SectionItem.categoryItem), -// searchResultItems: state.searchResultItems -// .map(PopupSearchView.SectionItem.searchResultItem), -// headerInput: SearchResultHeaderView.Input( -// count: state.totalElementsCount, -// sortedTitle: [state.openTitle, state.sortOptionTitle].joined(separator: "・") -// ) -// ) -// } -// .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { @@ -141,9 +70,31 @@ extension PopupSearchViewController { .map { Reactor.Action.filterOptionButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + mainView.collectionView.rx.prefetchItems + .throttle(.milliseconds(100), latest: false, scheduler: MainScheduler.asyncInstance) + .map(Reactor.Action.searchResultPrefetchItems) + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindState(reactor: Reactor) { + reactor.pulse(\.$updateDataSource) + .withLatestFrom(reactor.state) + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.updateSnapshot( + recentSearchItems: state.recentSearchItems + .map(PopupSearchView.SectionItem.recentSearchItem), + categoryItems: state.categoryItems + .map(PopupSearchView.SectionItem.categoryItem), + searchResultItems: state.searchResultItems + .map(PopupSearchView.SectionItem.searchResultItem), + headerInput: state.searchResultHeader + ) + } + .disposed(by: disposeBag) + reactor.pulse(\.$present) .withUnretained(self) .subscribe { owner, target in @@ -155,7 +106,7 @@ extension PopupSearchViewController { let viewController = CategorySelectViewController() viewController.reactor = categoryReactor - // 완료 후 다시 Reactor로 Action 보내기 + #warning("pulse에서 bind하는 구조...? 개선 가능하려나") categoryReactor.state .filter { $0.isSaveOrResetButtonTapped } .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } From b20665174016f9c8971e643189ea6296e9f962fb Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 22:49:40 +0900 Subject: [PATCH 285/393] =?UTF-8?q?refactor/#131:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=ED=9D=90=EB=A6=84=20=EA=B0=9C=EC=84=A0=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Model/FilterOption.swift | 4 +- .../Source/Reactor/PopupSearchReactor.swift | 216 +++++++----------- .../View/PopupSearchViewController.swift | 2 +- 3 files changed, 90 insertions(+), 132 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift index aeac407a..5b238bd3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift @@ -13,8 +13,8 @@ final class FilterOption: NSCopying, Equatable { static let shared = FilterOption(status: .open, sortOption: .newest) - var status: PopupStatus - var sortOption: PopupSortOption + var status: PopupStatus = .open + var sortOption: PopupSortOption = .newest private init(status: PopupStatus, sortOption: PopupSortOption) { self.status = status diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 6774c5a7..a573699c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -18,15 +18,11 @@ public final class PopupSearchReactor: Reactor { case categoryTagRemoveButtonTapped(categoryID: Int) case categoryTagButtonTapped - case filterOptionButtonTapped + case searchResultFilterButtonTapped case searchResultItemTapped - case loadNextPage - case searchResultPrefetchItems(indexPathList: [IndexPath]) - - case filterOptionSaveButtonTapped case categorySaveOrResetButtonTapped } @@ -35,15 +31,14 @@ public final class PopupSearchReactor: Reactor { case setupRecentSearch(items: [TagCollectionViewCell.Input]) case setupCategory(items: [TagCollectionViewCell.Input]) case setupSearchResult(items: [PPPopupGridCollectionViewCell.Input]) - case setupTotalPageCount(count: Int32) - case setupTotalElementCount(count: Int64) case setupSearchResultHeader(item: SearchResultHeaderView.Input) + case setupSearchResultTotalPageCount(count: Int32) case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) case present(target: PresentTarget) - case updateCurrentPage(to: Int) + case updateCurrentPage(to: Int32) case updateDataSource } @@ -61,11 +56,9 @@ public final class PopupSearchReactor: Reactor { @Pulse var present: PresentTarget? @Pulse var updateDataSource: Void? - fileprivate var currentPage: Int = 0 - fileprivate let paginationSize: Int = 10 - fileprivate var totalPagesCount: Int = 0 - var hasNextPage: Bool { get { currentPage < (totalPagesCount - 1) } } - var totalElementsCount: Int = 0 + fileprivate var currentPage: Int32 = 0 + fileprivate let paginationSize: Int32 = 10 + fileprivate var totalPagesCount: Int32 = 0 } // MARK: - properties @@ -91,133 +84,70 @@ public final class PopupSearchReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: PopupStatus.open.requestValue, - categories: [], - page: 0, - size: Int32(currentState.paginationSize), - sort: PopupSortOption.newest.requestValue - ) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.makeSearchResultInputs(response: response) - - return Observable.concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: Category.shared.items)), - .just(.setupSearchResult(items: searchResultItems)), - .just(.setupTotalPageCount(count: response.totalPages)), - .just(.setupTotalElementCount(count: response.totalElements)), - .just(.updateDataSource) - ]) - } - - case .loadNextPage: - guard currentState.hasNextPage else { return .empty() } + return fetchSearchResult() + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateDataSource) + ]) + } - return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: FilterOption.shared.status.requestValue, - categories: Category.shared.getSelectedCategoryIDs(), - page: Int32(currentState.currentPage + 1), - size: Int32(currentState.paginationSize), - sort: FilterOption.shared.sortOption.requestValue - ) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.makeSearchResultInputs(response: response) - - return Observable.concat([ - .just(.appendSearchResult(items: searchResultItems)), - .just(.updateDataSource) - ]) - } case .searchResultPrefetchItems(let indexPathList): - // 마지막 섹션의 마지막 아이템 - guard let lastItemIndex = indexPathList.last?.last else { return .empty() } - - func isPrefetchable(prefetchCount: Int = 4) -> Bool { - let isScrollToEnd = lastItemIndex > currentState.paginationSize * (currentState.currentPage + 1) - prefetchCount - - let hasNextPage = currentState.currentPage < (currentState.totalPagesCount - 1) - - return isScrollToEnd && hasNextPage - } - - if isPrefetchable() { - return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: FilterOption.shared.status.requestValue, - categories: Category.shared.getSelectedCategoryIDs(), - page: Int32(currentState.currentPage + 1), - size: Int32(currentState.paginationSize), - sort: FilterOption.shared.sortOption.requestValue - ) + guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } + return fetchSearchResult(page: currentState.currentPage + 1) .withUnretained(self) .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.makeSearchResultInputs(response: response) - - return .concat([ - .just(.appendSearchResult(items: searchResultItems)), + .just(.appendSearchResult(items: owner.makeSearchResultInputs(response: response))), .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), .just(.updateDataSource) ]) } - } - return .empty() - - case .categoryTagButtonTapped: return .just(.present(target: .categorySelector)) - case .recentSearchTagButtonTapped: return .empty() - case .searchResultItemTapped: return .empty() + case .recentSearchTagButtonTapped: + return .empty() + + case .searchResultItemTapped: + return .empty() case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: - return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: FilterOption.shared.status.requestValue, - categories: Category.shared.getSelectedCategoryIDs(), - page: 0, - size: Int32(currentState.paginationSize), - sort: FilterOption.shared.sortOption.requestValue - ) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return .concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: Int(response.totalElements)))), - .just(.setupTotalPageCount(count: response.totalPages)), - .just(.setupTotalElementCount(count: response.totalElements)), - .just(.updateDataSource) - ]) + return fetchSearchResult() + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return .concat([ + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateDataSource) + ]) } case .categoryTagRemoveButtonTapped(let categoryID): Category.shared.removeItem(by: categoryID) + return fetchSearchResult() + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), + .just(.updateDataSource) + ]) + } - return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: FilterOption.shared.status.requestValue, - categories: Category.shared.getSelectedCategoryIDs(), - page: 0, - size: Int32(currentState.paginationSize), - sort: FilterOption.shared.sortOption.requestValue - ) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - let searchResultItems = owner.makeSearchResultInputs(response: response) - - return Observable.concat([ - .just(.setupCategory(items: Category.shared.items)), - .just(.setupSearchResult(items: searchResultItems)), - .just(.setupTotalPageCount(count: response.totalPages)), - .just(.setupTotalElementCount(count: response.totalElements)), - .just(.updateDataSource) - ]) - } - - case .filterOptionButtonTapped: + case .searchResultFilterButtonTapped: return .just(.present(target: .filterOptionSelector)) } } @@ -234,11 +164,8 @@ public final class PopupSearchReactor: Reactor { case .setupSearchResult(let items): newState.searchResultItems = items - case .setupTotalPageCount(let count): - newState.totalPagesCount = Int(count) - - case .setupTotalElementCount(let count): - newState.totalElementsCount = Int(count) + case .setupSearchResultTotalPageCount(let count): + newState.totalPagesCount = count case .setupSearchResultHeader(let input): newState.searchResultHeader = input @@ -246,7 +173,6 @@ public final class PopupSearchReactor: Reactor { case .appendSearchResult(let items): newState.searchResultItems += items - case .updateCurrentPage(let currentPage): newState.currentPage = currentPage @@ -266,7 +192,27 @@ public final class PopupSearchReactor: Reactor { } } -// MARK: - Functions +// MARK: Captulation Mutate +private extension PopupSearchReactor { + + func fetchSearchResult( + isOpen: Bool = FilterOption.shared.status.requestValue, + categoried: [Int64] = Category.shared.getSelectedCategoryIDs(), + page: Int32 = 0, + size: Int32 = 10, + sort: String = FilterOption.shared.sortOption.requestValue + ) -> Observable { + return popupAPIUseCase.getSearchBottomPopUpList( + isOpen: FilterOption.shared.status.requestValue, + categories: Category.shared.getSelectedCategoryIDs(), + page: 0, + size: currentState.paginationSize, + sort: FilterOption.shared.sortOption.requestValue + ) + } +} + +// MARK: - Make Functions private extension PopupSearchReactor { func makeRecentSearchItems() -> [TagCollectionViewCell.Input] { let searchKeywords = userDefaultService.fetchArray(key: "searchList") ?? [] @@ -293,7 +239,19 @@ private extension PopupSearchReactor { } } - func makeSearchResultHeaderInput(count: Int, title: String = FilterOption.shared.title) -> SearchResultHeaderView.Input { - return SearchResultHeaderView.Input(count: count, sortedTitle: title) + func makeSearchResultHeaderInput(count: Int64, title: String = FilterOption.shared.title) -> SearchResultHeaderView.Input { + return SearchResultHeaderView.Input(count: Int(count), sortedTitle: title) + } +} + +// MARK: - Checking Method +private extension PopupSearchReactor { + func isPrefetchable(prefetchCount: Int = 4, indexPathList: [IndexPath]) -> Bool { + guard let lastItemIndex = indexPathList.last?.last else { return false } + + let isScrollToEnd = lastItemIndex > Int(currentState.paginationSize) * Int(currentState.currentPage + 1) - prefetchCount + let hasNextPage = currentState.currentPage < (currentState.totalPagesCount - 1) + + return isScrollToEnd && hasNextPage } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 1982b703..bf2e6bea 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -67,7 +67,7 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.filterOptionButtonTapped - .map { Reactor.Action.filterOptionButtonTapped } + .map { Reactor.Action.searchResultFilterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) From a30e02b70aca78ee1ab4fe6508acf14af4ae1bca Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 6 May 2025 23:00:11 +0900 Subject: [PATCH 286/393] =?UTF-8?q?refactor/#131:=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=84=A4=EC=9D=B4=EC=85=98=20=EC=98=A4=EB=A5=98=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 --- .../Source/Reactor/PopupSearchReactor.swift | 12 ++++++------ .../SearchFeature/Source/View/PopupSearchView.swift | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index a573699c..e53e6443 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -197,17 +197,17 @@ private extension PopupSearchReactor { func fetchSearchResult( isOpen: Bool = FilterOption.shared.status.requestValue, - categoried: [Int64] = Category.shared.getSelectedCategoryIDs(), + categories: [Int64] = Category.shared.getSelectedCategoryIDs(), page: Int32 = 0, size: Int32 = 10, sort: String = FilterOption.shared.sortOption.requestValue ) -> Observable { return popupAPIUseCase.getSearchBottomPopUpList( - isOpen: FilterOption.shared.status.requestValue, - categories: Category.shared.getSelectedCategoryIDs(), - page: 0, - size: currentState.paginationSize, - sort: FilterOption.shared.sortOption.requestValue + isOpen: isOpen, + categories: categories, + page: page, + size: size, + sort: sort ) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 5a24faf7..d8c244bd 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -337,7 +337,7 @@ extension PopupSearchView { if !searchResultItems.isEmpty { self.searchResultHeaderInput = searchResultHeaderInput snapshot.appendSections([PopupSearchView.Section.searchResult]) - snapshot.appendItems(searchResultItems, toSection: .searchResult) + snapshot.appendItems(searchResultItems, toSection: .searchResult) snapshot.reloadSections([.searchResult]) } From 25c19124220d6f1ced56c0d2c503ebc2652ccf86 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 00:06:40 +0900 Subject: [PATCH 287/393] =?UTF-8?q?feat/#131:=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=96=B4=20=EB=AA=A8=EB=91=90=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 15 +++++++++++++++ .../Source/View/PopupSearchView.swift | 6 ++++++ .../Source/View/PopupSearchViewController.swift | 5 +++++ .../Source/View/TagCollectionHeaderView.swift | 10 +++++----- 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index e53e6443..613f011b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -14,6 +14,7 @@ public final class PopupSearchReactor: Reactor { case viewDidLoad case recentSearchTagButtonTapped + case recentSearchTagRemoveAllButtonTapped case categoryTagRemoveButtonTapped(categoryID: Int) case categoryTagButtonTapped @@ -97,6 +98,14 @@ public final class PopupSearchReactor: Reactor { ]) } + case .recentSearchTagRemoveAllButtonTapped: + self.removeAllRecentSearchItems() + return .concat([ + .just(.setupRecentSearch(items: self.makeRecentSearchItems())), + .just(.updateDataSource) + ]) + + case .searchResultPrefetchItems(let indexPathList): guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } return fetchSearchResult(page: currentState.currentPage + 1) @@ -244,6 +253,12 @@ private extension PopupSearchReactor { } } +private extension PopupSearchReactor { + func removeAllRecentSearchItems() { + userDefaultService.delete(keyType: .searchKeyword) + } +} + // MARK: - Checking Method private extension PopupSearchReactor { func isPrefetchable(prefetchCount: Int = 4, indexPathList: [IndexPath]) -> Bool { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index d8c244bd..0ed17496 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -29,6 +29,7 @@ final class PopupSearchView: UIView { } // MARK: - Properties + let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() let filterOptionButtonTapped = PublishRelay() @@ -243,6 +244,7 @@ extension PopupSearchView { for: indexPath ) as! TagCollectionViewCell cell.injection(with: recentRearchItem) + return cell case .categoryItem(let categoryItem): @@ -284,6 +286,10 @@ extension PopupSearchView { ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } header.setupHeader(title: "최근 검색어", buttonTitle: "모두삭제") + header.removeAllButton.rx.tap + .bind(to: self.recentSearchTagRemoveAllButtonTapped) + .disposed(by: header.disposeBag) + return header case .category: diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index bf2e6bea..676e9e39 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -42,6 +42,11 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.recentSearchTagRemoveAllButtonTapped + .map { Reactor.Action.recentSearchTagRemoveAllButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.collectionView.rx.itemSelected .compactMap { indexPath in let sections = self.mainView.getSectionsFromDataSource() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index 083b2463..892c9faa 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -19,7 +19,7 @@ final class TagCollectionHeaderView: UICollectionReusableView { $0.font = .korFont(style: .bold, size: 16) } - let titleButton = UIButton().then { + let removeAllButton = UIButton().then { $0.isHidden = true } @@ -45,7 +45,7 @@ final class TagCollectionHeaderView: UICollectionReusableView { // MARK: - SetUp private extension TagCollectionHeaderView { func addViews() { - [sectionTitleLabel, titleButton].forEach { + [sectionTitleLabel, removeAllButton].forEach { self.addSubview($0) } } @@ -57,7 +57,7 @@ private extension TagCollectionHeaderView { make.height.equalTo(22) } - titleButton.snp.makeConstraints { make in + removeAllButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.centerY.equalTo(sectionTitleLabel) make.height.equalTo(20) @@ -69,13 +69,13 @@ extension TagCollectionHeaderView { func setupHeader(title: String, buttonTitle: String? = nil) { sectionTitleLabel.text = title if let buttonTitle = buttonTitle { - titleButton.isHidden = false + removeAllButton.isHidden = false let attributes: [NSAttributedString.Key: Any] = [ .underlineStyle: NSUnderlineStyle.single.rawValue, .font: UIFont.korFont(style: .regular, size: 13) ] let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) - titleButton.setAttributedTitle(attributedTitle, for: .normal) + removeAllButton.setAttributedTitle(attributedTitle, for: .normal) } } } From 6b457d2fb320571ad72bab6842ae5561a29e8b06 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 00:12:48 +0900 Subject: [PATCH 288/393] =?UTF-8?q?refactor/#131:=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EC=BA=A1=EC=8A=90=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 613f011b..9d75491b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -142,7 +142,7 @@ public final class PopupSearchReactor: Reactor { } case .categoryTagRemoveButtonTapped(let categoryID): - Category.shared.removeItem(by: categoryID) + self.removeCategoryItem(by: categoryID) return fetchSearchResult() .withUnretained(self) .flatMap { (owner, response) -> Observable in @@ -253,10 +253,15 @@ private extension PopupSearchReactor { } } +// MARK: - Remove Funtions private extension PopupSearchReactor { func removeAllRecentSearchItems() { userDefaultService.delete(keyType: .searchKeyword) } + + func removeCategoryItem(by categoryID: Int) { + Category.shared.removeItem(by: categoryID) + } } // MARK: - Checking Method From 04fd431c884756426271e6e4ed69b9781f898df4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 00:13:01 +0900 Subject: [PATCH 289/393] =?UTF-8?q?fix/#131:=20=ED=8E=98=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=EC=85=98=EC=9D=B4=20=ED=95=9C=EB=B2=88?= =?UTF-8?q?=EB=A7=8C=20=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=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 --- .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 9d75491b..d2859917 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -137,6 +137,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), .just(.updateDataSource) ]) } From bc3e11b3b91bdab4b68bf78a7d7e5d8dc9024db8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 00:26:55 +0900 Subject: [PATCH 290/393] =?UTF-8?q?refactor/#131:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{FilterOption.swift => Filter.swift} | 20 +++++------ ...eactor.swift => FilterSelectReactor.swift} | 34 +++++++++---------- .../Source/Reactor/PopupSearchReactor.swift | 22 ++++++------ .../FilterSelectView.swift} | 18 +++++----- .../FilterSelectViewController.swift} | 24 ++++++------- .../Source/View/PopupSearchView.swift | 8 ++--- .../View/PopupSearchViewController.swift | 10 +++--- .../Source/View/SearchResultHeaderView.swift | 22 ++++++------ 8 files changed, 80 insertions(+), 78 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/{FilterOption.swift => Filter.swift} (74%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/{FilterOptionSelectReactor.swift => FilterSelectReactor.swift} (54%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{FilterOptionSelectModal/FilterOptionSelectView.swift => FilterSelectModal/FilterSelectView.swift} (77%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{FilterOptionSelectModal/FilterOptionSelectViewController.swift => FilterSelectModal/FilterSelectViewController.swift} (72%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift similarity index 74% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift index 5b238bd3..33cad9b7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/FilterOption.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift @@ -1,27 +1,27 @@ import Foundation /// 필터 옵션 상태를 공유하기 위한 싱글톤 객체 -final class FilterOption: NSCopying, Equatable { +final class Filter: NSCopying, Equatable { func copy(with zone: NSZone? = nil) -> Any { - return FilterOption( + return Filter( status: self.status, - sortOption: self.sortOption + sort: self.sort ) } - static func == (lhs: FilterOption, rhs: FilterOption) -> Bool { return lhs === rhs } + static func == (lhs: Filter, rhs: Filter) -> Bool { return lhs === rhs } - static let shared = FilterOption(status: .open, sortOption: .newest) + static let shared = Filter(status: .open, sort: .newest) var status: PopupStatus = .open - var sortOption: PopupSortOption = .newest + var sort: PopupSort = .newest - private init(status: PopupStatus, sortOption: PopupSortOption) { + private init(status: PopupStatus, sort: PopupSort) { self.status = status - self.sortOption = sortOption + self.sort = sort } - var title: String { [status.title, sortOption.title].joined(separator: "・") } + var title: String { [status.title, sort.title].joined(separator: "・") } } /// 팝업 상점이 현재 열려 있는지 또는 닫혀 있는지 여부를 나타냅니다 @@ -52,7 +52,7 @@ enum PopupStatus: CaseIterable { } /// 팝업 검색 결과를위한 정렬 옵션을 나타냅니다 -enum PopupSortOption: CaseIterable { +enum PopupSort: CaseIterable { case newest case popularity diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift similarity index 54% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift index 619f8f3b..433736ff 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterOptionSelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift @@ -4,23 +4,23 @@ import ReactorKit import RxCocoa import RxSwift -final class FilterOptionSelectReactor: Reactor { +final class FilterSelectReactor: Reactor { // MARK: - Reactor enum Action { case changeStatus(status: PopupStatus) - case changeSortOption(sortOption: PopupSortOption) + case changeSort(sort: PopupSort) case saveButtonTapped } enum Mutation { case changeStatus(status: PopupStatus) - case changeSortOption(sortOption: PopupSortOption) - case saveCurrentFilterOption + case changeSort(sort: PopupSort) + case saveCurrentFilter } struct State { - var selectedFilterOption: FilterOption + var selectedFilter: Filter var saveButtonIsEnable: Bool = false var isSaveButtonTapped: Bool = false } @@ -32,7 +32,7 @@ final class FilterOptionSelectReactor: Reactor { // MARK: - init init() { - self.initialState = State(selectedFilterOption: FilterOption.shared.copy() as! FilterOption) + self.initialState = State(selectedFilter: Filter.shared.copy() as! Filter) } // MARK: - Reactor Methods @@ -41,11 +41,11 @@ final class FilterOptionSelectReactor: Reactor { case .changeStatus(let status): return Observable.just(.changeStatus(status: status)) - case .changeSortOption(let filter): - return Observable.just(.changeSortOption(sortOption: filter)) + case .changeSort(let sort): + return Observable.just(.changeSort(sort: sort)) case .saveButtonTapped: - return Observable.just(.saveCurrentFilterOption) + return Observable.just(.saveCurrentFilter) } } @@ -54,16 +54,16 @@ final class FilterOptionSelectReactor: Reactor { switch mutation { case .changeStatus(let status): - newState.selectedFilterOption.status = status - newState.saveButtonIsEnable = (newState.selectedFilterOption != FilterOption.shared) + newState.selectedFilter.status = status + newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) - case .changeSortOption(let sortOption): - newState.selectedFilterOption.sortOption = sortOption - newState.saveButtonIsEnable = (newState.selectedFilterOption != FilterOption.shared) + case .changeSort(let sort): + newState.selectedFilter.sort = sort + newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) - case .saveCurrentFilterOption: - FilterOption.shared.status = newState.selectedFilterOption.status - FilterOption.shared.sortOption = newState.selectedFilterOption.sortOption + case .saveCurrentFilter: + Filter.shared.status = newState.selectedFilter.status + Filter.shared.sort = newState.selectedFilter.sort newState.isSaveButtonTapped = true } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index d2859917..0c8035f3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -24,8 +24,10 @@ public final class PopupSearchReactor: Reactor { case searchResultPrefetchItems(indexPathList: [IndexPath]) - case filterOptionSaveButtonTapped + case filterSaveButtonTapped case categorySaveOrResetButtonTapped + + } public enum Mutation { @@ -45,7 +47,7 @@ public final class PopupSearchReactor: Reactor { public enum PresentTarget { case categorySelector - case filterOptionSelector + case filterSelector } public struct State { @@ -127,7 +129,7 @@ public final class PopupSearchReactor: Reactor { case .searchResultItemTapped: return .empty() - case .filterOptionSaveButtonTapped, .categorySaveOrResetButtonTapped: + case .filterSaveButtonTapped, .categorySaveOrResetButtonTapped: return fetchSearchResult() .withUnretained(self) .flatMap { (owner, response) -> Observable in @@ -158,7 +160,7 @@ public final class PopupSearchReactor: Reactor { } case .searchResultFilterButtonTapped: - return .just(.present(target: .filterOptionSelector)) + return .just(.present(target: .filterSelector)) } } @@ -193,8 +195,8 @@ public final class PopupSearchReactor: Reactor { switch target { case .categorySelector: newState.present = .categorySelector - case .filterOptionSelector: - newState.present = .filterOptionSelector + case .filterSelector: + newState.present = .filterSelector } } @@ -206,11 +208,11 @@ public final class PopupSearchReactor: Reactor { private extension PopupSearchReactor { func fetchSearchResult( - isOpen: Bool = FilterOption.shared.status.requestValue, + isOpen: Bool = Filter.shared.status.requestValue, categories: [Int64] = Category.shared.getSelectedCategoryIDs(), page: Int32 = 0, size: Int32 = 10, - sort: String = FilterOption.shared.sortOption.requestValue + sort: String = Filter.shared.sort.requestValue ) -> Observable { return popupAPIUseCase.getSearchBottomPopUpList( isOpen: isOpen, @@ -249,8 +251,8 @@ private extension PopupSearchReactor { } } - func makeSearchResultHeaderInput(count: Int64, title: String = FilterOption.shared.title) -> SearchResultHeaderView.Input { - return SearchResultHeaderView.Input(count: Int(count), sortedTitle: title) + func makeSearchResultHeaderInput(count: Int64, title: String = Filter.shared.title) -> SearchResultHeaderView.Input { + return SearchResultHeaderView.Input(count: Int(count), filterStatusTitle: title) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift similarity index 77% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift index ee745cb8..22ce4713 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift @@ -4,7 +4,7 @@ import DesignSystem import SnapKit -final class FilterOptionSelectView: UIView { +final class FilterSelectView: UIView { // MARK: - Components private let titleLabel = PPLabel(style: .bold, fontSize: 18, text: "노출 순서를 선택해주세요") @@ -17,9 +17,9 @@ final class FilterOptionSelectView: UIView { let statusSegmentControl = PPSegmentedControl(type: .base, segments: ["오픈", "종료"], selectedSegmentIndex: 0) - private let sortOptionLabel = PPLabel(style: .regular, fontSize: 13, text: "팝업순서") + private let sortLabel = PPLabel(style: .regular, fontSize: 13, text: "팝업순서") - let sortOptionSegmentControl = PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) + let sortSegmentControl = PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) let saveButton = PPButton(style: .primary, text: "저장", disabledText: "저장") @@ -38,10 +38,10 @@ final class FilterOptionSelectView: UIView { } // MARK: - SetUp -private extension FilterOptionSelectView { +private extension FilterSelectView { func addViews() { [titleLabel, closeButton, statusLabel, statusSegmentControl, - sortOptionLabel, sortOptionSegmentControl, saveButton].forEach { + sortLabel, sortSegmentControl, saveButton].forEach { self.addSubview($0) } } @@ -69,18 +69,18 @@ private extension FilterOptionSelectView { make.leading.trailing.equalToSuperview().inset(20) } - sortOptionLabel.snp.makeConstraints { make in + sortLabel.snp.makeConstraints { make in make.top.equalTo(statusSegmentControl.snp.bottom).offset(20) make.leading.equalToSuperview().inset(20) } - sortOptionSegmentControl.snp.makeConstraints { make in - make.top.equalTo(sortOptionLabel.snp.bottom).offset(8) + sortSegmentControl.snp.makeConstraints { make in + make.top.equalTo(sortLabel.snp.bottom).offset(8) make.horizontalEdges.equalToSuperview().inset(20) } saveButton.snp.makeConstraints { make in - make.top.equalTo(sortOptionSegmentControl.snp.bottom).offset(32) + make.top.equalTo(sortSegmentControl.snp.bottom).offset(32) make.horizontalEdges.equalToSuperview().inset(20) make.bottom.equalTo(self.safeAreaLayoutGuide) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift similarity index 72% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index 31f2b813..2315100b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterOptionSelectModal/FilterOptionSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -8,18 +8,18 @@ import RxCocoa import RxSwift import SnapKit -final class FilterOptionSelectViewController: BaseViewController, View { +final class FilterSelectViewController: BaseViewController, View { - typealias Reactor = FilterOptionSelectReactor + typealias Reactor = FilterSelectReactor // MARK: - Properties var disposeBag = DisposeBag() - private var mainView = FilterOptionSelectView() + private var mainView = FilterSelectView() } // MARK: - Life Cycle -extension FilterOptionSelectViewController { +extension FilterSelectViewController { override func loadView() { self.view = mainView } @@ -30,7 +30,7 @@ extension FilterOptionSelectViewController { } // MARK: - Methods -extension FilterOptionSelectViewController { +extension FilterSelectViewController { func bind(reactor: Reactor) { mainView.closeButton.rx.tap .withUnretained(self) @@ -47,12 +47,12 @@ extension FilterOptionSelectViewController { .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.sortOptionSegmentControl.rx.controlEvent(.valueChanged) + mainView.sortSegmentControl.rx.controlEvent(.valueChanged) .withUnretained(self) .map { (owner, _) in - if owner.mainView.sortOptionSegmentControl.selectedSegmentIndex == 0 { - Reactor.Action.changeSortOption(sortOption: .newest) - } else { Reactor.Action.changeSortOption(sortOption: .popularity) } + if owner.mainView.sortSegmentControl.selectedSegmentIndex == 0 { + Reactor.Action.changeSort(sort: .newest) + } else { Reactor.Action.changeSort(sort: .popularity) } } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -67,8 +67,8 @@ extension FilterOptionSelectViewController { reactor.state .withUnretained(self) .subscribe { (owner, state) in - owner.mainView.statusSegmentControl.selectedSegmentIndex = state.selectedFilterOption.status.index - owner.mainView.sortOptionSegmentControl.selectedSegmentIndex = state.selectedFilterOption.sortOption.index + owner.mainView.statusSegmentControl.selectedSegmentIndex = state.selectedFilter.status.index + owner.mainView.sortSegmentControl.selectedSegmentIndex = state.selectedFilter.sort.index owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } .disposed(by: disposeBag) @@ -76,7 +76,7 @@ extension FilterOptionSelectViewController { } // MARK: - PanModalPresentable -extension FilterOptionSelectViewController: PanModalPresentable { +extension FilterSelectViewController: PanModalPresentable { var panScrollable: UIScrollView? { return nil } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 0ed17496..8543f486 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -31,7 +31,7 @@ final class PopupSearchView: UIView { // MARK: - Properties let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() - let filterOptionButtonTapped = PublishRelay() + let filterStatusButtonTapped = PublishRelay() let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { @@ -311,10 +311,10 @@ extension PopupSearchView { if let input = self.searchResultHeaderInput { header.injection(with: input) - } else { header.injection(with: SearchResultHeaderView.Input(count: 0, sortedTitle: "nil")) } + } else { header.injection(with: SearchResultHeaderView.Input(count: 0, filterStatusTitle: "nil")) } - header.filterOptionButton.rx.tap - .bind(to: self.filterOptionButtonTapped) + header.filterStatusButton.rx.tap + .bind(to: self.filterStatusButtonTapped) .disposed(by: header.disposeBag) return header diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 676e9e39..58e8d2e3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -71,7 +71,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.filterOptionButtonTapped + mainView.filterStatusButtonTapped .map { Reactor.Action.searchResultFilterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -120,13 +120,13 @@ extension PopupSearchViewController { owner.presentPanModal(viewController) - case .filterOptionSelector: - let viewController = FilterOptionSelectViewController() - viewController.reactor = FilterOptionSelectReactor() + case .filterSelector: + let viewController = FilterSelectViewController() + viewController.reactor = FilterSelectReactor() viewController.reactor?.state .filter { $0.isSaveButtonTapped == true } - .map { _ in Reactor.Action.filterOptionSaveButtonTapped } + .map { _ in Reactor.Action.filterSaveButtonTapped } .bind(to: reactor.action) .disposed(by: owner.disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index eb554be6..8e3802d3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -20,14 +20,14 @@ public final class SearchResultHeaderView: UICollectionReusableView { $0.textColor = .g400 } - private let filterOptionLabel = PPLabel(style: .regular, fontSize: 13) + private let filterStatusLabel = PPLabel(style: .regular, fontSize: 13) private let dropDownImageView = UIImageView().then { $0.image = UIImage(named: "icon_dropdown") $0.isUserInteractionEnabled = false } - let filterOptionButton = UIButton() + let filterStatusButton = UIButton() // MARK: - init override init(frame: CGRect) { @@ -51,12 +51,12 @@ public final class SearchResultHeaderView: UICollectionReusableView { // MARK: - SetUp private extension SearchResultHeaderView { func addViews() { - [cellCountLabel, filterOptionButton].forEach { + [cellCountLabel, filterStatusButton].forEach { addSubview($0) } - [filterOptionLabel, dropDownImageView].forEach { - filterOptionButton.addSubview($0) + [filterStatusLabel, dropDownImageView].forEach { + filterStatusButton.addSubview($0) } } @@ -67,13 +67,13 @@ private extension SearchResultHeaderView { make.centerY.equalToSuperview() } - filterOptionButton.snp.makeConstraints { make in + filterStatusButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.height.equalTo(22) make.centerY.equalToSuperview() } - filterOptionLabel.snp.makeConstraints { make in + filterStatusLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() } @@ -81,7 +81,7 @@ private extension SearchResultHeaderView { dropDownImageView.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.width.equalTo(dropDownImageView.snp.height) - make.leading.equalTo(filterOptionLabel.snp.trailing).offset(6) + make.leading.equalTo(filterStatusLabel.snp.trailing).offset(6) make.trailing.equalToSuperview() } } @@ -90,7 +90,7 @@ private extension SearchResultHeaderView { extension SearchResultHeaderView: Inputable { public struct Input { var count: Int? - var sortedTitle: String? + var filterStatusTitle: String? } public func injection(with input: Input) { @@ -98,8 +98,8 @@ extension SearchResultHeaderView: Inputable { cellCountLabel.text = "총 \(count)개" } - if let sortedTitle = input.sortedTitle { - filterOptionLabel.text = sortedTitle + if let filterStatusTitle = input.filterStatusTitle { + filterStatusLabel.text = filterStatusTitle } } } From 03565ed3d58b05fe3152d5946c6464219dda200c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 00:48:34 +0900 Subject: [PATCH 291/393] =?UTF-8?q?refactor/#131:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 키워드 검색 API와 통합해서 사용할 수 있도록 개선 --- .../Source/Reactor/PopupSearchReactor.swift | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 0c8035f3..c4aec898 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -26,8 +26,6 @@ public final class PopupSearchReactor: Reactor { case filterSaveButtonTapped case categorySaveOrResetButtonTapped - - } public enum Mutation { @@ -93,7 +91,7 @@ public final class PopupSearchReactor: Reactor { return Observable.concat([ .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateDataSource) @@ -114,7 +112,7 @@ public final class PopupSearchReactor: Reactor { .withUnretained(self) .flatMap { (owner, response) -> Observable in return .concat([ - .just(.appendSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), .just(.updateDataSource) ]) @@ -136,7 +134,7 @@ public final class PopupSearchReactor: Reactor { return .concat([ .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), @@ -151,7 +149,7 @@ public final class PopupSearchReactor: Reactor { .flatMap { (owner, response) -> Observable in return Observable.concat([ .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultInputs(response: response))), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), @@ -235,8 +233,8 @@ private extension PopupSearchReactor { return Category.shared.getCancelableCategoryItems() } - func makeSearchResultInputs(response: GetSearchBottomPopUpListResponse) -> [PPPopupGridCollectionViewCell.Input] { - return response.popUpStoreList.map { + func makeSearchResultItems(_ popupStoreList: [PopUpStoreResponse], _ loginYn: Bool) -> [PPPopupGridCollectionViewCell.Input] { + return popupStoreList.map { PPPopupGridCollectionViewCell.Input( imagePath: $0.mainImageUrl, id: $0.id, @@ -246,7 +244,7 @@ private extension PopupSearchReactor { startDate: $0.startDate, endDate: $0.endDate, isBookmark: $0.bookmarkYn, - isLogin: response.loginYn + isLogin: loginYn ) } } From 34c79e59081faa6e29aa05d268579698fb7aa3ff Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 01:33:04 +0900 Subject: [PATCH 292/393] =?UTF-8?q?feat/#131:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 59 ++++++++++++++++--- .../Source/View/PPSearchBarView.swift | 23 ++++++-- .../View/PopupSearchViewController.swift | 28 +++++++++ 3 files changed, 98 insertions(+), 12 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index c4aec898..b6fa65be 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -13,6 +13,9 @@ public final class PopupSearchReactor: Reactor { public enum Action { case viewDidLoad + case textFieldEditing(text: String) + case textFieldExitEditing(text: String) + case recentSearchTagButtonTapped case recentSearchTagRemoveAllButtonTapped @@ -23,7 +26,6 @@ public final class PopupSearchReactor: Reactor { case searchResultItemTapped case searchResultPrefetchItems(indexPathList: [IndexPath]) - case filterSaveButtonTapped case categorySaveOrResetButtonTapped } @@ -38,6 +40,8 @@ public final class PopupSearchReactor: Reactor { case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) case present(target: PresentTarget) + case clearButton(state: ClearButtonState) + case endEditing case updateCurrentPage(to: Int32) case updateDataSource @@ -48,6 +52,18 @@ public final class PopupSearchReactor: Reactor { case filterSelector } + public enum ClearButtonState { + var value: Bool { + switch self { + case .visible: return false + case .hidden: return true + } + } + + case visible + case hidden + } + public struct State { var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] @@ -55,6 +71,8 @@ public final class PopupSearchReactor: Reactor { var searchResultHeader: SearchResultHeaderView.Input? = nil @Pulse var present: PresentTarget? + @Pulse var clearButton: ClearButtonState? + @Pulse var endEditing: Void? @Pulse var updateDataSource: Void? fileprivate var currentPage: Int32 = 0 @@ -94,10 +112,32 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), + .just(.updateDataSource) + ]) + } + + case .textFieldEditing(let text): + return .just(.clearButton(state: text.isEmpty ? .hidden : .visible)) + + case .textFieldExitEditing(let text): + return fetchSearchResult(keyword: text) + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupRecentSearch(items: [])), + .just(.setupCategory(items: [])), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: 0))), // FIXME: API에 해당 결과값이 아직 없음 + .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 + .just(.updateCurrentPage(to: 0)), + .just(.clearButton(state: .hidden)), + .just(.endEditing), .just(.updateDataSource) ]) } + case .recentSearchTagRemoveAllButtonTapped: self.removeAllRecentSearchItems() return .concat([ @@ -190,12 +230,13 @@ public final class PopupSearchReactor: Reactor { newState.updateDataSource = () case .present(let target): - switch target { - case .categorySelector: - newState.present = .categorySelector - case .filterSelector: - newState.present = .filterSelector - } + newState.present = target + + case .clearButton(let state): + newState.clearButton = state + + case .endEditing: + newState.endEditing = () } return newState @@ -220,6 +261,10 @@ private extension PopupSearchReactor { sort: sort ) } + + func fetchSearchResult(keyword: String) -> Observable { + fetchKeywordBasePopupListUseCase.execute(keyword: keyword) + } } // MARK: - Make Functions diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift index 7223572e..967fd74c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift @@ -14,15 +14,18 @@ public class PPSearchBarView: UIView { } let searchBar = UISearchBar().then { - $0.placeholder = "팝업스토어명을 입력해보세요" + $0.searchTextField.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .korFont(style: .regular, size: 14)) $0.tintColor = .g400 $0.backgroundColor = .g50 $0.setImage(UIImage(named: "icon_search_gray"), for: .search, state: .normal) - $0.setImage(UIImage(named: "icon_clear_button"), for: .clear, state: .normal) $0.searchBarStyle = .minimal - if let searchBarTextFieldBackgroundView = $0.searchTextField.subviews.first { - searchBarTextFieldBackgroundView.isHidden = true - } + $0.searchTextField.clearButtonMode = .never + $0.searchTextField.subviews.first?.isHidden = true + } + + let clearButton = UIButton(type: .custom).then { + $0.setImage(UIImage(named: "icon_clear_button"), for: .normal) + $0.isHidden = true } let cancelButton = UIButton(type: .system).then { @@ -53,6 +56,10 @@ private extension PPSearchBarView { [searchBar, cancelButton].forEach { self.stackView.addArrangedSubview($0) } + + [clearButton].forEach { + self.searchBar.addSubview($0) + } } func setupConstraints() { @@ -67,6 +74,12 @@ private extension PPSearchBarView { make.height.equalToSuperview() } + clearButton.snp.makeConstraints { make in + make.trailing.equalToSuperview().inset(12) + make.centerY.equalToSuperview() + make.size.equalTo(20) + } + cancelButton.snp.makeConstraints { make in make.height.equalToSuperview() } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 58e8d2e3..92f400a8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -34,6 +34,34 @@ extension PopupSearchViewController { public func bind(reactor: Reactor) { self.bindAction(reactor: reactor) self.bindState(reactor: reactor) + + + + mainView.searchBar.searchBar.searchTextField.rx.controlEvent(.editingDidEndOnExit) + .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) + .map(Reactor.Action.textFieldExitEditing) + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.searchBar.searchBar.searchTextField.rx.controlEvent([.editingDidBegin, .editingChanged]) + .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) + .map(Reactor.Action.textFieldEditing) + .bind(to: reactor.action) + .disposed(by: disposeBag) + + reactor.pulse(\.$clearButton) + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.searchBar.clearButton.isHidden = state?.value ?? true + } + .disposed(by: disposeBag) + + reactor.pulse(\.$endEditing) + .withUnretained(self) + .subscribe { (owner, _) in + owner.mainView.endEditing(true) + } + .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { From 32abac6a4b5e3c57c14fe232c2013265b1fd6243 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 01:42:11 +0900 Subject: [PATCH 293/393] =?UTF-8?q?feat/#131:=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=ED=95=84=EB=93=9C=20=EC=A7=80=EC=9A=B0=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EB=8F=99=EC=9E=91=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 12 ++++++++++++ .../Source/View/PopupSearchViewController.swift | 11 +++++++++++ 2 files changed, 23 insertions(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index b6fa65be..e52020f2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -26,6 +26,7 @@ public final class PopupSearchReactor: Reactor { case searchResultItemTapped case searchResultPrefetchItems(indexPathList: [IndexPath]) + case clearButtonTapped case filterSaveButtonTapped case categorySaveOrResetButtonTapped } @@ -41,6 +42,7 @@ public final class PopupSearchReactor: Reactor { case present(target: PresentTarget) case clearButton(state: ClearButtonState) + case clearTextField case endEditing case updateCurrentPage(to: Int32) @@ -72,6 +74,7 @@ public final class PopupSearchReactor: Reactor { @Pulse var present: PresentTarget? @Pulse var clearButton: ClearButtonState? + @Pulse var clearButtonTapped: Void? @Pulse var endEditing: Void? @Pulse var updateDataSource: Void? @@ -182,6 +185,12 @@ public final class PopupSearchReactor: Reactor { ]) } + case .clearButtonTapped: + return Observable.concat([ + .just(.clearButton(state: .hidden)), + .just(.clearTextField) + ]) + case .categoryTagRemoveButtonTapped(let categoryID): self.removeCategoryItem(by: categoryID) return fetchSearchResult() @@ -235,6 +244,9 @@ public final class PopupSearchReactor: Reactor { case .clearButton(let state): newState.clearButton = state + case .clearTextField: + newState.clearButtonTapped = () + case .endEditing: newState.endEditing = () } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 92f400a8..9df80a3f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -49,6 +49,12 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.searchBar.clearButton.rx.tap + .debug("DEBUG: Clear Button Tapped") + .map { Reactor.Action.clearButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + reactor.pulse(\.$clearButton) .withUnretained(self) .subscribe { (owner, state) in @@ -62,6 +68,11 @@ extension PopupSearchViewController { owner.mainView.endEditing(true) } .disposed(by: disposeBag) + + reactor.pulse(\.$clearButtonTapped) + .withUnretained(self) + .subscribe { (owner, _) in owner.mainView.searchBar.searchBar.searchTextField.text = nil } + .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { From 3110b6bfc1fc5310a7fb06380b90131b7fa28f56 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 01:50:11 +0900 Subject: [PATCH 294/393] =?UTF-8?q?feat/#131:=20=EC=99=B8=EB=B6=80=20?= =?UTF-8?q?=EC=98=81=EC=97=AD=20=ED=84=B0=EC=B9=98=EC=8B=9C=20=EB=8F=99?= =?UTF-8?q?=EC=9E=91=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 7 +++++++ .../SearchFeature/Source/View/PopupSearchView.swift | 3 +++ .../Source/View/PopupSearchViewController.swift | 6 +++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index e52020f2..c9b0be54 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -15,6 +15,7 @@ public final class PopupSearchReactor: Reactor { case textFieldEditing(text: String) case textFieldExitEditing(text: String) + case textFieldEndEditing case recentSearchTagButtonTapped case recentSearchTagRemoveAllButtonTapped @@ -140,6 +141,12 @@ public final class PopupSearchReactor: Reactor { ]) } + case .textFieldEndEditing: + return .concat([ + .just(.clearButton(state: .hidden)), + .just(.endEditing) + ]) + case .recentSearchTagRemoveAllButtonTapped: self.removeAllRecentSearchItems() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 8543f486..780a7d40 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -32,6 +32,7 @@ final class PopupSearchView: UIView { let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() let filterStatusButtonTapped = PublishRelay() + let tapGestureRecognizer = UITapGestureRecognizer() let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { @@ -67,6 +68,8 @@ final class PopupSearchView: UIView { // UICollectionView 최 상/하단 빈 영역 $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 48, right: 0) $0.contentInsetAdjustmentBehavior = .never + + $0.addGestureRecognizer(tapGestureRecognizer) } private var dataSource: UICollectionViewDiffableDataSource? diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 9df80a3f..7ede2af8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -50,11 +50,15 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.searchBar.clearButton.rx.tap - .debug("DEBUG: Clear Button Tapped") .map { Reactor.Action.clearButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.tapGestureRecognizer.rx.event + .map { _ in Reactor.Action.textFieldEndEditing } + .bind(to: reactor.action) + .disposed(by: disposeBag) + reactor.pulse(\.$clearButton) .withUnretained(self) .subscribe { (owner, state) in From fa5ca67a99b6fcd0a1fbce1522bc45cca1811fcd Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 01:55:33 +0900 Subject: [PATCH 295/393] =?UTF-8?q?refactor/#131:=20ViewController=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PopupSearchViewController.swift | 95 ++++++++----------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 7ede2af8..9105ab1c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -34,18 +34,16 @@ extension PopupSearchViewController { public func bind(reactor: Reactor) { self.bindAction(reactor: reactor) self.bindState(reactor: reactor) + } - - - mainView.searchBar.searchBar.searchTextField.rx.controlEvent(.editingDidEndOnExit) - .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) - .map(Reactor.Action.textFieldExitEditing) + private func bindAction(reactor: Reactor) { + rx.viewDidLoad + .map { Reactor.Action.viewDidLoad } .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.searchBar.searchBar.searchTextField.rx.controlEvent([.editingDidBegin, .editingChanged]) - .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) - .map(Reactor.Action.textFieldEditing) + mainView.tapGestureRecognizer.rx.event + .map { _ in Reactor.Action.textFieldEndEditing } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -54,34 +52,15 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.tapGestureRecognizer.rx.event - .map { _ in Reactor.Action.textFieldEndEditing } + mainView.searchBar.searchBar.searchTextField.rx.controlEvent([.editingDidBegin, .editingChanged]) + .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) + .map(Reactor.Action.textFieldEditing) .bind(to: reactor.action) .disposed(by: disposeBag) - reactor.pulse(\.$clearButton) - .withUnretained(self) - .subscribe { (owner, state) in - owner.mainView.searchBar.clearButton.isHidden = state?.value ?? true - } - .disposed(by: disposeBag) - - reactor.pulse(\.$endEditing) - .withUnretained(self) - .subscribe { (owner, _) in - owner.mainView.endEditing(true) - } - .disposed(by: disposeBag) - - reactor.pulse(\.$clearButtonTapped) - .withUnretained(self) - .subscribe { (owner, _) in owner.mainView.searchBar.searchBar.searchTextField.text = nil } - .disposed(by: disposeBag) - } - - private func bindAction(reactor: Reactor) { - rx.viewDidLoad - .map { Reactor.Action.viewDidLoad } + mainView.searchBar.searchBar.searchTextField.rx.controlEvent(.editingDidEndOnExit) + .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) + .map(Reactor.Action.textFieldExitEditing) .bind(to: reactor.action) .disposed(by: disposeBag) @@ -96,14 +75,9 @@ extension PopupSearchViewController { guard indexPath.section < sections.count else { return nil } switch sections[indexPath.section] { - case .recentSearch: - return Reactor.Action.recentSearchTagButtonTapped - - case .category: - return Reactor.Action.categoryTagButtonTapped - - case .searchResult: - return Reactor.Action.searchResultItemTapped + case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped + case .category: return Reactor.Action.categoryTagButtonTapped + case .searchResult: return Reactor.Action.searchResultItemTapped } } .bind(to: reactor.action) @@ -127,20 +101,19 @@ extension PopupSearchViewController { } private func bindState(reactor: Reactor) { - reactor.pulse(\.$updateDataSource) - .withLatestFrom(reactor.state) + reactor.pulse(\.$endEditing) .withUnretained(self) - .subscribe { (owner, state) in - owner.mainView.updateSnapshot( - recentSearchItems: state.recentSearchItems - .map(PopupSearchView.SectionItem.recentSearchItem), - categoryItems: state.categoryItems - .map(PopupSearchView.SectionItem.categoryItem), - searchResultItems: state.searchResultItems - .map(PopupSearchView.SectionItem.searchResultItem), - headerInput: state.searchResultHeader - ) - } + .subscribe { (owner, _) in owner.mainView.endEditing(true) } + .disposed(by: disposeBag) + + reactor.pulse(\.$clearButton) + .withUnretained(self) + .subscribe { (owner, state) in owner.mainView.searchBar.clearButton.isHidden = state?.value ?? true } + .disposed(by: disposeBag) + + reactor.pulse(\.$clearButtonTapped) + .withUnretained(self) + .subscribe { (owner, _) in owner.mainView.searchBar.searchBar.searchTextField.text = nil } .disposed(by: disposeBag) reactor.pulse(\.$present) @@ -179,5 +152,19 @@ extension PopupSearchViewController { } } .disposed(by: disposeBag) + + + reactor.pulse(\.$updateDataSource) + .withLatestFrom(reactor.state) + .withUnretained(self) + .subscribe { (owner, state) in + owner.mainView.updateSnapshot( + recentSearchItems: state.recentSearchItems.map(PopupSearchView.SectionItem.recentSearchItem), + categoryItems: state.categoryItems.map(PopupSearchView.SectionItem.categoryItem), + searchResultItems: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), + headerInput: state.searchResultHeader + ) + } + .disposed(by: disposeBag) } } From 0000bee8f6968f998de1f3ebdde9e1746dd5091f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 02:00:32 +0900 Subject: [PATCH 296/393] =?UTF-8?q?refactor/#131:=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 17 +++++++++-------- .../Source/View/PopupSearchViewController.swift | 8 ++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index c9b0be54..e8937934 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -13,9 +13,11 @@ public final class PopupSearchReactor: Reactor { public enum Action { case viewDidLoad - case textFieldEditing(text: String) - case textFieldExitEditing(text: String) - case textFieldEndEditing + case searchBarEditing(text: String) + case searchBarExitEditing(text: String) + case searchBarEndEditing + case searchBarClearButtonTapped + case searchBarCancelButtonTapped case recentSearchTagButtonTapped case recentSearchTagRemoveAllButtonTapped @@ -27,7 +29,6 @@ public final class PopupSearchReactor: Reactor { case searchResultItemTapped case searchResultPrefetchItems(indexPathList: [IndexPath]) - case clearButtonTapped case filterSaveButtonTapped case categorySaveOrResetButtonTapped } @@ -121,10 +122,10 @@ public final class PopupSearchReactor: Reactor { ]) } - case .textFieldEditing(let text): + case .searchBarEditing(let text): return .just(.clearButton(state: text.isEmpty ? .hidden : .visible)) - case .textFieldExitEditing(let text): + case .searchBarExitEditing(let text): return fetchSearchResult(keyword: text) .withUnretained(self) .flatMap { (owner, response) -> Observable in @@ -141,7 +142,7 @@ public final class PopupSearchReactor: Reactor { ]) } - case .textFieldEndEditing: + case .searchBarEndEditing: return .concat([ .just(.clearButton(state: .hidden)), .just(.endEditing) @@ -192,7 +193,7 @@ public final class PopupSearchReactor: Reactor { ]) } - case .clearButtonTapped: + case .searchBarClearButtonTapped: return Observable.concat([ .just(.clearButton(state: .hidden)), .just(.clearTextField) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 9105ab1c..90199862 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -43,24 +43,24 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.tapGestureRecognizer.rx.event - .map { _ in Reactor.Action.textFieldEndEditing } + .map { _ in Reactor.Action.searchBarEndEditing } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.searchBar.clearButton.rx.tap - .map { Reactor.Action.clearButtonTapped } + .map { Reactor.Action.searchBarClearButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.searchBar.searchBar.searchTextField.rx.controlEvent([.editingDidBegin, .editingChanged]) .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) - .map(Reactor.Action.textFieldEditing) + .map(Reactor.Action.searchBarEditing) .bind(to: reactor.action) .disposed(by: disposeBag) mainView.searchBar.searchBar.searchTextField.rx.controlEvent(.editingDidEndOnExit) .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) - .map(Reactor.Action.textFieldExitEditing) + .map(Reactor.Action.searchBarExitEditing) .bind(to: reactor.action) .disposed(by: disposeBag) From 30d89c002a50e62c765a17a26294d27a765e6e5b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 02:14:43 +0900 Subject: [PATCH 297/393] =?UTF-8?q?refactor/#131:=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=ED=95=9C=20=EC=83=81=ED=83=9C=EC=97=90=EC=84=9C=20=EC=9D=B4?= =?UTF-8?q?=EC=A0=84=20=ED=99=94=EB=A9=B4=EC=9C=BC=EB=A1=9C=20=EB=8F=8C?= =?UTF-8?q?=EC=95=84=EA=B0=80=EA=B8=B0=20=EA=B8=B0=EB=8A=A5=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 --- .../Source/Reactor/PopupSearchReactor.swift | 26 +++++++++++++++++++ .../View/PopupSearchViewController.swift | 5 ++++ 2 files changed, 31 insertions(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index e8937934..0621bca3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -48,6 +48,7 @@ public final class PopupSearchReactor: Reactor { case endEditing case updateCurrentPage(to: Int32) + case updateSearching(to: Bool) case updateDataSource } @@ -80,6 +81,7 @@ public final class PopupSearchReactor: Reactor { @Pulse var endEditing: Void? @Pulse var updateDataSource: Void? + fileprivate var isSearching: Bool = false fileprivate var currentPage: Int32 = 0 fileprivate let paginationSize: Int32 = 10 fileprivate var totalPagesCount: Int32 = 0 @@ -136,6 +138,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: 0))), // FIXME: API에 해당 결과값이 아직 없음 .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), + .just(.updateSearching(to: true)), .just(.clearButton(state: .hidden)), .just(.endEditing), .just(.updateDataSource) @@ -148,6 +151,26 @@ public final class PopupSearchReactor: Reactor { .just(.endEditing) ]) + case .searchBarCancelButtonTapped: + if currentState.isSearching { + return fetchSearchResult() + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), + .just(.updateSearching(to: false)), + .just(.clearTextField), + .just(.updateDataSource) + ]) + } + } + else { return .empty() } // TODO: 이전 화면으로 보내기 + case .recentSearchTagRemoveAllButtonTapped: self.removeAllRecentSearchItems() @@ -243,6 +266,9 @@ public final class PopupSearchReactor: Reactor { case .updateCurrentPage(let currentPage): newState.currentPage = currentPage + case .updateSearching(let isSearching): + newState.isSearching = isSearching + case .updateDataSource: newState.updateDataSource = () diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 90199862..d87f8879 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -34,6 +34,11 @@ extension PopupSearchViewController { public func bind(reactor: Reactor) { self.bindAction(reactor: reactor) self.bindState(reactor: reactor) + + mainView.searchBar.cancelButton.rx.tap + .map { _ in Reactor.Action.searchBarCancelButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { From 0e7a53288b4fda7366309a19f78eaaaa8c9537dc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 02:46:35 +0900 Subject: [PATCH 298/393] =?UTF-8?q?feat/#131:=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=96=B4=20=ED=83=9C=EA=B7=B8=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 14 +++++++++++++- .../Source/View/PopupSearchView.swift | 6 ++++++ .../Source/View/PopupSearchViewController.swift | 5 +++++ .../Source/View/TagCollectionViewCell.swift | 2 +- 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 0621bca3..145777a5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -20,6 +20,7 @@ public final class PopupSearchReactor: Reactor { case searchBarCancelButtonTapped case recentSearchTagButtonTapped + case recentSearchTagRemoveButtonTapped(text: String) case recentSearchTagRemoveAllButtonTapped case categoryTagRemoveButtonTapped(categoryID: Int) @@ -171,6 +172,12 @@ public final class PopupSearchReactor: Reactor { } else { return .empty() } // TODO: 이전 화면으로 보내기 + case .recentSearchTagRemoveButtonTapped(let text): + self.removeRecentSearchItem(text: text) + return Observable.concat([ + .just(.setupRecentSearch(items: self.makeRecentSearchItems())), + .just(.updateDataSource) + ]) case .recentSearchTagRemoveAllButtonTapped: self.removeAllRecentSearchItems() @@ -316,7 +323,7 @@ private extension PopupSearchReactor { // MARK: - Make Functions private extension PopupSearchReactor { func makeRecentSearchItems() -> [TagCollectionViewCell.Input] { - let searchKeywords = userDefaultService.fetchArray(key: "searchList") ?? [] + let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword) ?? [] return searchKeywords.map { TagCollectionViewCell.Input(title: $0) } } @@ -347,6 +354,11 @@ private extension PopupSearchReactor { // MARK: - Remove Funtions private extension PopupSearchReactor { + func removeRecentSearchItem(text: String) { + guard let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword) else { return } + userDefaultService.save(keyType: .searchKeyword, value: searchKeywords.filter { $0 != text }) + } + func removeAllRecentSearchItems() { userDefaultService.delete(keyType: .searchKeyword) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 780a7d40..fd401a0c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -29,6 +29,7 @@ final class PopupSearchView: UIView { } // MARK: - Properties + let recentSearchTagRemoveButtonTapped = PublishRelay() let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() let filterStatusButtonTapped = PublishRelay() @@ -248,6 +249,11 @@ extension PopupSearchView { ) as! TagCollectionViewCell cell.injection(with: recentRearchItem) + cell.cancelButton.rx.tap + .compactMap { cell.titleLabel.text } + .bind(to: self.recentSearchTagRemoveButtonTapped) + .disposed(by: cell.disposeBag) + return cell case .categoryItem(let categoryItem): diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index d87f8879..403bde90 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -39,6 +39,11 @@ extension PopupSearchViewController { .map { _ in Reactor.Action.searchBarCancelButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + + mainView.recentSearchTagRemoveButtonTapped + .map(Reactor.Action.recentSearchTagRemoveButtonTapped) + .bind(to: reactor.action) + .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift index 09c8f62f..1073a110 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift @@ -11,7 +11,7 @@ public final class TagCollectionViewCell: UICollectionViewCell { var disposeBag = DisposeBag() - private let titleLabel = PPLabel(style: .medium, fontSize: 11) + let titleLabel = PPLabel(style: .medium, fontSize: 11) let cancelButton = UIButton() From 63c427db804377df3a51e246516d21af43c5f367 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 03:07:03 +0900 Subject: [PATCH 299/393] =?UTF-8?q?feat/#131:=20=EC=B5=9C=EA=B7=BC=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=96=B4=20=ED=83=9C=EA=B7=B8=20=ED=84=B0?= =?UTF-8?q?=EC=B9=98=EC=8B=9C=20=EC=95=A1=EC=85=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 39 ++++++++++++++++--- .../View/PopupSearchViewController.swift | 9 ++++- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 145777a5..f8f01b32 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -19,7 +19,7 @@ public final class PopupSearchReactor: Reactor { case searchBarClearButtonTapped case searchBarCancelButtonTapped - case recentSearchTagButtonTapped + case recentSearchTagButtonTapped(indexPath: IndexPath) case recentSearchTagRemoveButtonTapped(text: String) case recentSearchTagRemoveAllButtonTapped @@ -48,6 +48,7 @@ public final class PopupSearchReactor: Reactor { case clearTextField case endEditing + case updateSearchBar(to: String?) case updateCurrentPage(to: Int32) case updateSearching(to: Bool) case updateDataSource @@ -71,6 +72,7 @@ public final class PopupSearchReactor: Reactor { } public struct State { + var searchBarText: String? = nil var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] @@ -166,6 +168,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateCurrentPage(to: 0)), .just(.updateSearching(to: false)), .just(.clearTextField), + .just(.endEditing), .just(.updateDataSource) ]) } @@ -202,8 +205,24 @@ public final class PopupSearchReactor: Reactor { case .categoryTagButtonTapped: return .just(.present(target: .categorySelector)) - case .recentSearchTagButtonTapped: - return .empty() + case .recentSearchTagButtonTapped(let indexPath): + return fetchSearchResult(keyword: self.makeRecentSearchItem(at: indexPath)) + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupRecentSearch(items: [])), + .just(.setupCategory(items: [])), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: 0))), // FIXME: API에 해당 결과값이 아직 없음 + .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 + .just(.updateCurrentPage(to: 0)), + .just(.updateSearchBar(to: self.makeRecentSearchItem(at: indexPath))), + .just(.updateSearching(to: true)), + .just(.clearButton(state: .hidden)), + .just(.endEditing), + .just(.updateDataSource) + ]) + } case .searchResultItemTapped: return .empty() @@ -270,6 +289,9 @@ public final class PopupSearchReactor: Reactor { case .appendSearchResult(let items): newState.searchResultItems += items + case .updateSearchBar(let text): + newState.searchBarText = text + case .updateCurrentPage(let currentPage): newState.currentPage = currentPage @@ -315,13 +337,20 @@ private extension PopupSearchReactor { ) } - func fetchSearchResult(keyword: String) -> Observable { - fetchKeywordBasePopupListUseCase.execute(keyword: keyword) + func fetchSearchResult(keyword: String?) -> Observable { + guard let keyword else { return .empty() } + return fetchKeywordBasePopupListUseCase.execute(keyword: keyword) } } // MARK: - Make Functions private extension PopupSearchReactor { + func makeRecentSearchItem(at indexPath: IndexPath) -> String? { + guard let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword), + searchKeywords.indices.contains(indexPath.item) else { return nil } + return searchKeywords[indexPath.item] + } + func makeRecentSearchItems() -> [TagCollectionViewCell.Input] { let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword) ?? [] return searchKeywords.map { TagCollectionViewCell.Input(title: $0) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 403bde90..24691ac5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -85,7 +85,8 @@ extension PopupSearchViewController { guard indexPath.section < sections.count else { return nil } switch sections[indexPath.section] { - case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped + case .recentSearch: + return Reactor.Action.recentSearchTagButtonTapped(indexPath: indexPath) case .category: return Reactor.Action.categoryTagButtonTapped case .searchResult: return Reactor.Action.searchResultItemTapped } @@ -163,6 +164,12 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) + reactor.state.distinctUntilChanged(\.searchBarText) + .map { $0.searchBarText } + .withUnretained(self) + .subscribe { (owner, text) in owner.mainView.searchBar.searchBar.text = text } + .disposed(by: disposeBag) + reactor.pulse(\.$updateDataSource) .withLatestFrom(reactor.state) From 2d2901a6b7778d7e233cc7113c897bdfe0958c10 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 03:13:15 +0900 Subject: [PATCH 300/393] =?UTF-8?q?fix/#131:=20tapGestureRecog=EA=B0=80=20?= =?UTF-8?q?cell=EC=9D=98=20=ED=84=B0=EC=B9=98=EB=A5=BC=20=EA=B0=80?= =?UTF-8?q?=EB=A1=9C=EC=B1=84=EA=B0=80=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index fd401a0c..6b6ca100 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -33,9 +33,13 @@ final class PopupSearchView: UIView { let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() let filterStatusButtonTapped = PublishRelay() - let tapGestureRecognizer = UITapGestureRecognizer() + + let tapGestureRecognizer = UITapGestureRecognizer().then { + $0.cancelsTouchesInView = false + } let searchBar = PPSearchBarView() + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { $0.setCollectionViewLayout(self.makeLayout(), animated: false) @@ -69,8 +73,6 @@ final class PopupSearchView: UIView { // UICollectionView 최 상/하단 빈 영역 $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 48, right: 0) $0.contentInsetAdjustmentBehavior = .never - - $0.addGestureRecognizer(tapGestureRecognizer) } private var dataSource: UICollectionViewDiffableDataSource? @@ -96,6 +98,10 @@ private extension PopupSearchView { [searchBar, collectionView].forEach { self.addSubview($0) } + + [tapGestureRecognizer].forEach { + self.addGestureRecognizer($0) + } } func setupConstraints() { From 7dd1de69a7833f29f33b73d101c6e3ab63a1f97f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 10:38:24 +0900 Subject: [PATCH 301/393] =?UTF-8?q?refactor/#131:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=9C=EC=84=9C=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PopupSearchViewController.swift | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 24691ac5..fa011616 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -34,16 +34,6 @@ extension PopupSearchViewController { public func bind(reactor: Reactor) { self.bindAction(reactor: reactor) self.bindState(reactor: reactor) - - mainView.searchBar.cancelButton.rx.tap - .map { _ in Reactor.Action.searchBarCancelButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.recentSearchTagRemoveButtonTapped - .map(Reactor.Action.recentSearchTagRemoveButtonTapped) - .bind(to: reactor.action) - .disposed(by: disposeBag) } private func bindAction(reactor: Reactor) { @@ -53,15 +43,11 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.tapGestureRecognizer.rx.event + .debug("DEBUG: Tap Gesture Recognizer") .map { _ in Reactor.Action.searchBarEndEditing } .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.searchBar.clearButton.rx.tap - .map { Reactor.Action.searchBarClearButtonTapped } - .bind(to: reactor.action) - .disposed(by: disposeBag) - mainView.searchBar.searchBar.searchTextField.rx.controlEvent([.editingDidBegin, .editingChanged]) .withLatestFrom(mainView.searchBar.searchBar.searchTextField.rx.text.orEmpty) .map(Reactor.Action.searchBarEditing) @@ -74,12 +60,28 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.searchBar.clearButton.rx.tap + .map { Reactor.Action.searchBarClearButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.searchBar.cancelButton.rx.tap + .map { _ in Reactor.Action.searchBarCancelButtonTapped } + .bind(to: reactor.action) + .disposed(by: disposeBag) + + mainView.recentSearchTagRemoveButtonTapped + .map(Reactor.Action.recentSearchTagRemoveButtonTapped) + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.recentSearchTagRemoveAllButtonTapped .map { Reactor.Action.recentSearchTagRemoveAllButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.collectionView.rx.itemSelected + .debug("DEBUG: collectionView.rx.itemSelected") .compactMap { indexPath in let sections = self.mainView.getSectionsFromDataSource() guard indexPath.section < sections.count else { return nil } From 6a836b6d8d52fb590b2bec554bd2b940f24d4648 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 11:55:53 +0900 Subject: [PATCH 302/393] =?UTF-8?q?feat/#131:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EC=9D=B4=ED=9B=84=EC=97=90=20=ED=97=A4=EB=8D=94=EA=B0=80=20?= =?UTF-8?q?=EB=8B=A4=EB=A5=B4=EA=B2=8C=20=EB=9C=A8=EB=8F=84=EB=A1=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 --- .../Source/Reactor/PopupSearchReactor.swift | 43 ++++++++++++++++--- .../Source/View/PopupSearchView.swift | 10 +++-- .../View/PopupSearchViewController.swift | 2 - .../Source/View/SearchResultHeaderView.swift | 43 ++++++++++++++----- 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index f8f01b32..2e23bd3b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -138,7 +138,10 @@ public final class PopupSearchReactor: Reactor { .just(.setupRecentSearch(items: [])), .just(.setupCategory(items: [])), .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: 0))), // FIXME: API에 해당 결과값이 아직 없음 + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput( + keyword: owner.makePostPositionedText(text), + count: Int64(response.popupStoreList.count) + ))), // FIXME: API에 해당 결과값이 아직 없음 .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), .just(.updateSearching(to: true)), @@ -206,17 +209,21 @@ public final class PopupSearchReactor: Reactor { return .just(.present(target: .categorySelector)) case .recentSearchTagButtonTapped(let indexPath): - return fetchSearchResult(keyword: self.makeRecentSearchItem(at: indexPath)) + let keyword = self.makeRecentSearchItem(at: indexPath) + return fetchSearchResult(keyword: keyword) .withUnretained(self) .flatMap { (owner, response) -> Observable in return Observable.concat([ .just(.setupRecentSearch(items: [])), .just(.setupCategory(items: [])), .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: 0))), // FIXME: API에 해당 결과값이 아직 없음 + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput( + keyword: owner.makePostPositionedText(keyword), + count: Int64(response.popupStoreList.count) + ))), .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), - .just(.updateSearchBar(to: self.makeRecentSearchItem(at: indexPath))), + .just(.updateSearchBar(to: keyword)), .just(.updateSearching(to: true)), .just(.clearButton(state: .hidden)), .just(.endEditing), @@ -376,8 +383,32 @@ private extension PopupSearchReactor { } } - func makeSearchResultHeaderInput(count: Int64, title: String = Filter.shared.title) -> SearchResultHeaderView.Input { - return SearchResultHeaderView.Input(count: Int(count), filterStatusTitle: title) + func makeSearchResultHeaderInput( + keyword afterTitle: String? = nil, + count: Int64, + filter filterTitle: String? = Filter.shared.title) -> SearchResultHeaderView.Input { + return SearchResultHeaderView.Input( + title: afterTitle, + count: Int(count), + filterStatusText: filterTitle + ) + } + + /// 받침에 따라 이/가 를 판단해서 붙여준다. + func makePostPositionedText(_ text: String?) -> String { + + guard let text, let lastCharacter = text.last else { return "" } + + let unicodeValue = Int(lastCharacter.unicodeScalars.first!.value) + + // 한글 유니코드 범위 체크 + let base = 0xAC00 + let last = 0xD7A3 + guard base...last ~= unicodeValue else { return text + "가" } + + // 종성 인덱스 계산 (받침이 있으면 1 이상) + let finalConsonantIndex = (unicodeValue - base) % 28 + return (finalConsonantIndex != 0) ? text + "이" : text + "가" } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 6b6ca100..f1212280 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -39,7 +39,7 @@ final class PopupSearchView: UIView { } let searchBar = PPSearchBarView() - + lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { $0.setCollectionViewLayout(self.makeLayout(), animated: false) @@ -229,7 +229,7 @@ private extension PopupSearchView { // Header let headerSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(22) + heightDimension: .estimated(22) ) let header = NSCollectionLayoutBoundarySupplementaryItem( layoutSize: headerSize, @@ -326,7 +326,11 @@ extension PopupSearchView { if let input = self.searchResultHeaderInput { header.injection(with: input) - } else { header.injection(with: SearchResultHeaderView.Input(count: 0, filterStatusTitle: "nil")) } + } else { header.injection(with: SearchResultHeaderView.Input( + title: nil, + count: nil, filterStatusText: nil + )) + } header.filterStatusButton.rx.tap .bind(to: self.filterStatusButtonTapped) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index fa011616..2b5c86ff 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -43,7 +43,6 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.tapGestureRecognizer.rx.event - .debug("DEBUG: Tap Gesture Recognizer") .map { _ in Reactor.Action.searchBarEndEditing } .bind(to: reactor.action) .disposed(by: disposeBag) @@ -81,7 +80,6 @@ extension PopupSearchViewController { .disposed(by: disposeBag) mainView.collectionView.rx.itemSelected - .debug("DEBUG: collectionView.rx.itemSelected") .compactMap { indexPath in let sections = self.mainView.getSectionsFromDataSource() guard indexPath.section < sections.count else { return nil } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index 8e3802d3..a7a234ff 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -16,6 +16,10 @@ public final class SearchResultHeaderView: UICollectionReusableView { var disposeBag = DisposeBag() + private let afterSearchTitleLabel = PPLabel(style: .bold, fontSize: 16).then { + $0.isHidden = true + } + private let cellCountLabel = PPLabel(style: .regular, fontSize: 13).then { $0.textColor = .g400 } @@ -51,7 +55,7 @@ public final class SearchResultHeaderView: UICollectionReusableView { // MARK: - SetUp private extension SearchResultHeaderView { func addViews() { - [cellCountLabel, filterStatusButton].forEach { + [afterSearchTitleLabel, cellCountLabel, filterStatusButton].forEach { addSubview($0) } @@ -61,16 +65,23 @@ private extension SearchResultHeaderView { } func setupConstraints() { + afterSearchTitleLabel.snp.makeConstraints { make in + make.height.equalTo(0) + make.horizontalEdges.equalToSuperview() + make.top.equalToSuperview() + make.bottom.equalTo(cellCountLabel.snp.top).offset(-4) + } + cellCountLabel.snp.makeConstraints { make in make.leading.equalToSuperview() - make.height.equalTo(22) - make.centerY.equalToSuperview() + make.height.equalTo(20) + make.bottom.equalToSuperview() } filterStatusButton.snp.makeConstraints { make in make.trailing.equalToSuperview() make.height.equalTo(22) - make.centerY.equalToSuperview() + make.bottom.equalToSuperview() } filterStatusLabel.snp.makeConstraints { make in @@ -89,16 +100,28 @@ private extension SearchResultHeaderView { extension SearchResultHeaderView: Inputable { public struct Input { - var count: Int? - var filterStatusTitle: String? + let title: String? + let count: Int? + let filterStatusText: String? } public func injection(with input: Input) { - if let count = input.count { + if let afterSearchTitle = input.title, + let count = input.count { + filterStatusButton.isHidden = true + afterSearchTitleLabel.isHidden = false + afterSearchTitleLabel.text = afterSearchTitle + " 포함된 팝업" + cellCountLabel.text = "총 \(count)개를 찾았어요." + + afterSearchTitleLabel.snp.updateConstraints { make in + make.height.equalTo(24) + } + + } else if let count = input.count, + let filterStatusTitle = input.filterStatusText { + filterStatusButton.isHidden = false + afterSearchTitleLabel.isHidden = true cellCountLabel.text = "총 \(count)개" - } - - if let filterStatusTitle = input.filterStatusTitle { filterStatusLabel.text = filterStatusTitle } } From 9b804a56c0bb0e3e519861ec43e77bd61712dd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 7 May 2025 13:22:54 +0900 Subject: [PATCH 303/393] =?UTF-8?q?refactor/#130:=20UICollectionViewCell?= =?UTF-8?q?=20identifier=20extension=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Map/FillterSheetView/BalloonBackgroundView.swift | 4 ++-- .../Scene/Map/FillterSheetView/BalloonChipCell.swift | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift index e5cc9467..fff36989 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonBackgroundView.swift @@ -34,7 +34,7 @@ final class BalloonBackgroundView: UIView { let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) collectionView.backgroundColor = .clear collectionView.isScrollEnabled = false - collectionView.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifier) + collectionView.register(BalloonChipCell.self, forCellWithReuseIdentifier: BalloonChipCell.identifiers) return collectionView }() @@ -289,7 +289,7 @@ extension BalloonBackgroundView: UICollectionViewDataSource { cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: BalloonChipCell.identifier, + withReuseIdentifier: BalloonChipCell.identifiers, for: indexPath ) as? BalloonChipCell, let input = tagSection?.inputDataList[indexPath.item] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 711f1fd3..558af950 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -1,10 +1,11 @@ +import UIKit + import Infrastructure + import SnapKit -import UIKit import Then final class BalloonChipCell: UICollectionViewCell { - static let identifier = "BalloonChipCell" private enum Constant { static let verticalInset: CGFloat = 6 From 43bbd14d5f3432bd086cb1a4a7fc24f553d976bb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 May 2025 04:24:25 +0000 Subject: [PATCH 304/393] style/#130: Apply SwiftLint autocorrect --- .../Scene/Map/FillterSheetView/BalloonChipCell.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 1a0212e3..11c8dc01 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -4,8 +4,6 @@ import Infrastructure import SnapKit import Then -import UIKit - final class BalloonChipCell: UICollectionViewCell { private enum Constant { From d9ea5b7027d054cf7a362421d94b82fe316ec3bf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 14:05:02 +0900 Subject: [PATCH 305/393] =?UTF-8?q?feat/#131:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EA=B0=80=20=EC=97=86=EC=9D=84=EB=95=8C=20?= =?UTF-8?q?=EB=B3=B4=EC=97=AC=EC=A4=84=20label=20=EB=9D=84=EC=9A=B0?= =?UTF-8?q?=EA=B8=B0=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 17 +++++ .../Source/View/PopupSearchView.swift | 65 ++++++++++++----- .../View/PopupSearchViewController.swift | 3 +- .../SearchResultEmptyCollectionViewCell.swift | 72 +++++++++++++++++++ .../Source/View/SearchResultHeaderView.swift | 4 ++ 5 files changed, 143 insertions(+), 18 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 2e23bd3b..5429b19d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -51,6 +51,7 @@ public final class PopupSearchReactor: Reactor { case updateSearchBar(to: String?) case updateCurrentPage(to: Int32) case updateSearching(to: Bool) + case updateSearchResultEmptyCase case updateDataSource } @@ -77,6 +78,7 @@ public final class PopupSearchReactor: Reactor { var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] var searchResultHeader: SearchResultHeaderView.Input? = nil + var searchResultEmptyCase: SearchResultEmptyCollectionViewCell.EmptyCase? @Pulse var present: PresentTarget? @Pulse var clearButton: ClearButtonState? @@ -123,6 +125,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), + .just(.updateSearchResultEmptyCase), .just(.updateDataSource) ]) } @@ -145,6 +148,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), .just(.updateSearching(to: true)), + .just(.updateSearchResultEmptyCase), .just(.clearButton(state: .hidden)), .just(.endEditing), .just(.updateDataSource) @@ -170,6 +174,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearching(to: false)), + .just(.updateSearchResultEmptyCase), .just(.clearTextField), .just(.endEditing), .just(.updateDataSource) @@ -225,6 +230,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateCurrentPage(to: 0)), .just(.updateSearchBar(to: keyword)), .just(.updateSearching(to: true)), + .just(.updateSearchResultEmptyCase), .just(.clearButton(state: .hidden)), .just(.endEditing), .just(.updateDataSource) @@ -245,6 +251,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), + .just(.updateSearchResultEmptyCase), .just(.updateDataSource) ]) } @@ -266,6 +273,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), + .just(.updateSearchResultEmptyCase), .just(.updateDataSource) ]) } @@ -305,6 +313,9 @@ public final class PopupSearchReactor: Reactor { case .updateSearching(let isSearching): newState.isSearching = isSearching + case .updateSearchResultEmptyCase: + newState.searchResultEmptyCase = makeSearchResultEmptyCase(state: newState) + case .updateDataSource: newState.updateDataSource = () @@ -394,6 +405,12 @@ private extension PopupSearchReactor { ) } + func makeSearchResultEmptyCase(state: State) -> SearchResultEmptyCollectionViewCell.EmptyCase? { + if !currentState.searchResultItems.isEmpty { return nil } + else if currentState.isSearching { return .keyword } + else { return .option } + } + /// 받침에 따라 이/가 를 판단해서 붙여준다. func makePostPositionedText(_ text: String?) -> String { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index f1212280..69a7f025 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -19,6 +19,7 @@ final class PopupSearchView: UIView { case recentSearchItem(TagCollectionViewCell.Input) case categoryItem(TagCollectionViewCell.Input) case searchResultItem(PPPopupGridCollectionViewCell.Input) + case searchResultEmptyItem(SearchResultEmptyCollectionViewCell.EmptyCase) } /// Section의 헤더를 구분하기 위한 변수 @@ -43,16 +44,6 @@ final class PopupSearchView: UIView { lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { $0.setCollectionViewLayout(self.makeLayout(), animated: false) - $0.register( - TagCollectionViewCell.self, - forCellWithReuseIdentifier: TagCollectionViewCell.identifiers - ) - - $0.register( - PPPopupGridCollectionViewCell.self, - forCellWithReuseIdentifier: PPPopupGridCollectionViewCell.identifiers - ) - $0.register( TagCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.recentSearch.rawValue, @@ -64,12 +55,28 @@ final class PopupSearchView: UIView { forSupplementaryViewOfKind: SectionHeaderKind.category.rawValue, withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue ) + + $0.register( + TagCollectionViewCell.self, + forCellWithReuseIdentifier: TagCollectionViewCell.identifiers + ) + $0.register( SearchResultHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, withReuseIdentifier: SearchResultHeaderView.Identifier.searchResult.rawValue ) + $0.register( + PPPopupGridCollectionViewCell.self, + forCellWithReuseIdentifier: PPPopupGridCollectionViewCell.identifiers + ) + + $0.register( + SearchResultEmptyCollectionViewCell.self, + forCellWithReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers + ) + // UICollectionView 최 상/하단 빈 영역 $0.contentInset = UIEdgeInsets(top: 24, left: 0, bottom: 48, right: 0) $0.contentInsetAdjustmentBehavior = .never @@ -182,9 +189,21 @@ private extension PopupSearchView { } func makeSearchResultSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { + // Determine if there is a searchResultEmptyItem in the current visible items to adjust item width + let sectionItems = collectionView.indexPathsForVisibleItems.compactMap { + dataSource?.itemIdentifier(for: $0) + } + + let hasEmptyItem = sectionItems.contains { + if case .searchResultEmptyItem = $0 { return true } + return false + } + + let itemWidth: NSCollectionLayoutDimension = hasEmptyItem ? .fractionalWidth(1.0) : .fractionalWidth(0.5) + // Item let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(0.5), + widthDimension: itemWidth, heightDimension: .absolute(249) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) @@ -283,6 +302,14 @@ extension PopupSearchView { ) as! PPPopupGridCollectionViewCell cell.injection(with: searchResultItem) return cell + + case .searchResultEmptyItem(let emptyCase): + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers, + for: indexPath + ) as! SearchResultEmptyCollectionViewCell + cell.injection(with: SearchResultEmptyCollectionViewCell.Input(emptyCase: emptyCase)) + return cell } } @@ -345,7 +372,8 @@ extension PopupSearchView { recentSearchItems: [SectionItem], categoryItems: [SectionItem], searchResultItems: [SectionItem], - headerInput searchResultHeaderInput: SearchResultHeaderView.Input? = nil + headerInput searchResultHeaderInput: SearchResultHeaderView.Input? = nil, + searchResultEmpty: SearchResultEmptyCollectionViewCell.EmptyCase? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() @@ -359,12 +387,15 @@ extension PopupSearchView { snapshot.appendItems(categoryItems, toSection: .category) } - if !searchResultItems.isEmpty { - self.searchResultHeaderInput = searchResultHeaderInput - snapshot.appendSections([PopupSearchView.Section.searchResult]) - snapshot.appendItems(searchResultItems, toSection: .searchResult) - snapshot.reloadSections([.searchResult]) + snapshot.appendSections([PopupSearchView.Section.searchResult]) + self.searchResultHeaderInput = searchResultHeaderInput + + if let searchResultEmpty { + snapshot.appendItems([.searchResultEmptyItem(searchResultEmpty)], toSection: .searchResult) + } else { + snapshot.appendItems(searchResultItems, toSection: .searchResult) } + snapshot.reloadSections([.searchResult]) dataSource?.apply(snapshot, animatingDifferences: true) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 2b5c86ff..08459b5f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -179,7 +179,8 @@ extension PopupSearchViewController { recentSearchItems: state.recentSearchItems.map(PopupSearchView.SectionItem.recentSearchItem), categoryItems: state.categoryItems.map(PopupSearchView.SectionItem.categoryItem), searchResultItems: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), - headerInput: state.searchResultHeader + headerInput: state.searchResultHeader, + searchResultEmpty: state.searchResultEmptyCase ) } .disposed(by: disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift new file mode 100644 index 00000000..841eadef --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift @@ -0,0 +1,72 @@ +import UIKit + +import DesignSystem +import Infrastructure + +import SnapKit +import Then + +final class SearchResultEmptyCollectionViewCell: UICollectionViewCell { + + // MARK: - Properties + private let emptyLabel = PPLabel( + style: .medium, + fontSize: 14 + ).then { + $0.textAlignment = .center + $0.numberOfLines = 2 + $0.textColor = .g400 + } + + + // MARK: - init + override init(frame: CGRect) { + super.init(frame: frame) + + self.addViews() + self.setupConstraints() + self.configureUI() + } + + required init?(coder: NSCoder) { + fatalError("\(#file), \(#function) Error") + } +} + +// MARK: - SetUp +private extension SearchResultEmptyCollectionViewCell { + func addViews() { + [emptyLabel].forEach { + self.addSubview($0) + } + } + + func setupConstraints() { + emptyLabel.snp.makeConstraints { make in + make.top.equalToSuperview().inset(145) + make.horizontalEdges.equalToSuperview() + make.height.equalTo(42) + } + } + + func configureUI() { } +} + +extension SearchResultEmptyCollectionViewCell: Inputable { + enum EmptyCase { + case option + case keyword + } + + struct Input: Hashable { + let emptyCase: EmptyCase + } + + func injection(with input: Input) { + switch input.emptyCase { + case .option: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" + case .keyword: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" + } + } +} + diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index a7a234ff..7ce11899 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -123,6 +123,10 @@ extension SearchResultHeaderView: Inputable { afterSearchTitleLabel.isHidden = true cellCountLabel.text = "총 \(count)개" filterStatusLabel.text = filterStatusTitle + + afterSearchTitleLabel.snp.updateConstraints { make in + make.height.equalTo(0) + } } } } From 22225f693e8681d23584a6af389018a40b9ffd0e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 14:14:51 +0900 Subject: [PATCH 306/393] =?UTF-8?q?fix/#131:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=EA=B0=80=20=EC=97=86=EC=9D=84=EB=95=8C?= =?UTF-8?q?=EB=8A=94=20=ED=97=A4=EB=8D=94=EB=A5=BC=20=EC=A7=80=EC=9B=8C?= =?UTF-8?q?=EC=A7=80=EA=B2=8C=20=ED=95=B4=EC=A4=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/PopupSearchView.swift | 2 +- .../Source/View/SearchResultHeaderView.swift | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 69a7f025..380f0b8e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -397,7 +397,7 @@ extension PopupSearchView { } snapshot.reloadSections([.searchResult]) - dataSource?.apply(snapshot, animatingDifferences: true) + dataSource?.apply(snapshot, animatingDifferences: false) } func getSectionsFromDataSource() -> [Section] { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index 7ce11899..ca515a7a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -113,8 +113,12 @@ extension SearchResultHeaderView: Inputable { afterSearchTitleLabel.text = afterSearchTitle + " 포함된 팝업" cellCountLabel.text = "총 \(count)개를 찾았어요." - afterSearchTitleLabel.snp.updateConstraints { make in - make.height.equalTo(24) + if count == 0 { self.isHidden = true } + else { + self.isHidden = false + afterSearchTitleLabel.snp.updateConstraints { make in + make.height.equalTo(24) + } } } else if let count = input.count, @@ -124,6 +128,7 @@ extension SearchResultHeaderView: Inputable { cellCountLabel.text = "총 \(count)개" filterStatusLabel.text = filterStatusTitle + self.isHidden = false afterSearchTitleLabel.snp.updateConstraints { make in make.height.equalTo(0) } From 66e0d410945f9dd91a42fa53bcaa9dc3e0556ce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Wed, 7 May 2025 18:08:25 +0900 Subject: [PATCH 307/393] =?UTF-8?q?reafcator/#130:=20=EB=A9=94=EC=9D=B8?= =?UTF-8?q?=EC=A7=80=EC=97=AD=20=EB=9D=BC=EB=B2=A8=20=EC=A7=A4=EB=A6=BC?= =?UTF-8?q?=EC=9D=B4=EC=8A=88=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20=EA=B7=B8?= =?UTF-8?q?=EB=9D=BC=EC=9A=B4=EB=93=9C=EB=A3=B0=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FillterSheetView/BalloonChipCell.swift | 1 - .../FilterBottomSheetView.swift | 177 +++++++++--------- 2 files changed, 87 insertions(+), 91 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 1a0212e3..558af950 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -4,7 +4,6 @@ import Infrastructure import SnapKit import Then -import UIKit final class BalloonChipCell: UICollectionViewCell { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift index 0ecff3df..7be75de4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift @@ -1,39 +1,43 @@ -import SnapKit import UIKit +import SnapKit +import Then + final class FilterBottomSheetView: UIView { - // MARK: - UI Components - private let containerView: UIView = { - let view = UIView() - view.backgroundColor = .white - view.layer.cornerRadius = 20 - view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] - view.layer.masksToBounds = true - return view - }() + private enum Constant { + static let cornerRadius: CGFloat = 20 + static let topInset: CGFloat = 30 + static let horizontalInset: CGFloat = 16 + static let segmentedTopOffset: CGFloat = 16 + static let scrollViewHeight: CGFloat = 36 + static let categoryHeight: CGFloat = 160 + static let balloonTopOffset: CGFloat = 16 + static let filterChipsHeight: CGFloat = 80 + static let buttonStackSpacing: CGFloat = 12 + static let buttonStackHeight: CGFloat = 52 + } - let titleLabel: PPLabel = { - let label = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요") - label.textColor = .black - return label - }() + private let containerView = UIView().then { + $0.backgroundColor = .white + $0.layer.cornerRadius = Constant.cornerRadius + $0.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + $0.layer.masksToBounds = true + } - let closeButton: UIButton = { - let button = UIButton(type: .system) - button.setImage(UIImage(named: "icon_xmark"), for: .normal) - button.tintColor = .black - return button - }() + let titleLabel = PPLabel(style: .bold, fontSize: 18, text: "보기 옵션을 선택해주세요").then { + $0.textColor = .black + } - let segmentedControl: PPSegmentedControl = { - return PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) - }() + let closeButton = UIButton(type: .system).then { + $0.setImage(UIImage(named: "icon_xmark"), for: .normal) + $0.tintColor = .black + } - let locationScrollView: UIScrollView = { - let scrollView = UIScrollView() - scrollView.showsHorizontalScrollIndicator = false - return scrollView - }() + let segmentedControl = PPSegmentedControl(type: .tab, segments: ["지역", "카테고리"], selectedSegmentIndex: 0) + + let locationScrollView = UIScrollView().then { + $0.showsHorizontalScrollIndicator = false + } let locationContentView = UIView() var categoryHeightConstraint: Constraint? @@ -42,13 +46,13 @@ final class FilterBottomSheetView: UIView { let layout = UICollectionViewCompositionalLayout { section, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), - heightDimension: .estimated(36) + heightDimension: .estimated(Constant.scrollViewHeight) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(36) + heightDimension: .estimated(Constant.scrollViewHeight) ) let group = NSCollectionLayoutGroup.horizontal( layoutSize: groupSize, @@ -67,35 +71,25 @@ final class FilterBottomSheetView: UIView { return section } - - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout) - collectionView.backgroundColor = .clear - collectionView.isScrollEnabled = false - collectionView.register(TagSectionCell.self, forCellWithReuseIdentifier: TagSectionCell.identifiers) - return collectionView + return UICollectionView(frame: .zero, collectionViewLayout: layout).then { + $0.backgroundColor = .clear + $0.isScrollEnabled = false + $0.register(TagSectionCell.self, forCellWithReuseIdentifier: TagSectionCell.identifiers) + } }() let balloonBackgroundView = BalloonBackgroundView() - let resetButton: PPButton = { - return PPButton(style: .secondary, text: "초기화") - }() - - let saveButton: PPButton = { - return PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - }() + let resetButton = PPButton(style: .secondary, text: "초기화") + let saveButton = PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - private let buttonStack: UIStackView = { - let stack = UIStackView() - stack.axis = .horizontal - stack.spacing = 12 - stack.distribution = .fillEqually - return stack - }() + private let buttonStack = UIStackView().then { + $0.axis = .horizontal + $0.spacing = Constant.buttonStackSpacing + $0.distribution = .fillEqually + } - let filterChipsView: FilterChipsView = { - return FilterChipsView() - }() + let filterChipsView = FilterChipsView() private var balloonHeightConstraint: Constraint? @@ -142,25 +136,25 @@ final class FilterBottomSheetView: UIView { } titleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().offset(16) - make.top.equalToSuperview().offset(30) + make.leading.equalToSuperview().offset(Constant.horizontalInset) + make.top.equalToSuperview().offset(Constant.topInset) } closeButton.snp.makeConstraints { make in - make.trailing.equalToSuperview().inset(16) + make.trailing.equalToSuperview().inset(Constant.horizontalInset) make.centerY.equalTo(titleLabel) make.size.equalTo(24) } segmentedControl.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(16) + make.top.equalTo(titleLabel.snp.bottom).offset(Constant.segmentedTopOffset) make.leading.trailing.equalToSuperview() } locationScrollView.snp.makeConstraints { make in make.top.equalTo(segmentedControl.snp.bottom).offset(20) make.leading.trailing.equalToSuperview() - make.height.equalTo(36) + make.height.equalTo(Constant.scrollViewHeight) } locationContentView.snp.makeConstraints { make in @@ -169,28 +163,28 @@ final class FilterBottomSheetView: UIView { } categoryCollectionView.snp.makeConstraints { make in - make.top.equalTo(segmentedControl.snp.bottom).offset(16) + make.top.equalTo(segmentedControl.snp.bottom).offset(Constant.segmentedTopOffset) make.leading.trailing.equalToSuperview() - categoryHeightConstraint = make.height.equalTo(160).constraint + categoryHeightConstraint = make.height.equalTo(Constant.categoryHeight).constraint } balloonBackgroundView.snp.makeConstraints { make in - make.top.equalTo(locationScrollView.snp.bottom).offset(16) - make.leading.trailing.equalToSuperview().inset(16) + make.top.equalTo(locationScrollView.snp.bottom).offset(Constant.balloonTopOffset) + make.leading.trailing.equalToSuperview().inset(Constant.horizontalInset) balloonHeightConstraint = make.height.equalTo(0).constraint } filterChipsView.snp.makeConstraints { make in make.top.equalTo(balloonBackgroundView.snp.bottom).offset(24) - make.leading.trailing.equalToSuperview().inset(16) - make.height.equalTo(80) + make.leading.trailing.equalToSuperview().inset(Constant.horizontalInset) + make.height.equalTo(Constant.filterChipsHeight) } buttonStack.snp.makeConstraints { make in make.top.equalTo(filterChipsView.snp.bottom).offset(24) - make.leading.trailing.equalToSuperview().inset(16) + make.leading.trailing.equalToSuperview().inset(Constant.horizontalInset) make.bottom.equalToSuperview().inset(40) - make.height.equalTo(52) + make.height.equalTo(Constant.buttonStackHeight) } } @@ -305,41 +299,44 @@ final class FilterBottomSheetView: UIView { let button = PPButton( style: .secondary, text: title, - font: .korFont(style: .medium, size: 13), + font: .korFont(style: isSelected ? .bold : .medium, size: 13), cornerRadius: 18 ) - button.setBackgroundColor(.w100, for: .normal) - button.setTitleColor(.g400, for: .normal) + button.setBackgroundColor(isSelected ? .blu500 : .w100, for: .normal) + button.setTitleColor(isSelected ? .w100 : .g400, for: .normal) + button.layer.borderWidth = isSelected ? 0 : 1 button.layer.borderColor = UIColor.g200.cgColor - button.layer.borderWidth = 1 - - if isSelected { - button.setBackgroundColor(.blu500, for: .normal) - button.setTitleColor(.w100, for: .normal) - button.layer.borderWidth = 0 - } - button.contentEdgeInsets = UIEdgeInsets(top: 9, left: 16, bottom: 9, right: 16) + button.titleLabel?.setLineHeightText( + text: title, + font: .korFont(style: isSelected ? .bold : .medium, size: 13), + lineHeight: 1.2 + ) + return button } + func updateMainLocationSelection(_ index: Int) { locationContentView.subviews.enumerated().forEach { (idx, view) in guard let button = view as? PPButton else { return } - if idx == index { - button.setBackgroundColor(.blu500, for: .normal) - button.setTitleColor(.w100, for: .normal) - button.layer.borderWidth = 0 - button.titleLabel?.font = .korFont(style: .bold, size: 13) - } else { - button.setBackgroundColor(.w100, for: .normal) - button.setTitleColor(.g400, for: .normal) - button.layer.borderColor = UIColor.g200.cgColor - button.titleLabel?.font = .korFont(style: .medium, size: 13) - button.layer.borderWidth = 1 - } + + let isSelected = idx == index + button.setBackgroundColor(isSelected ? .blu500 : .w100, for: .normal) + button.setTitleColor(isSelected ? .w100 : .g400, for: .normal) + button.layer.borderWidth = isSelected ? 0 : 1 + button.layer.borderColor = UIColor.g200.cgColor + + // 버튼의 타이틀 레이블에 setLineHeightText 적용 + let title = button.currentTitle ?? "" + button.titleLabel?.setLineHeightText( + text: title, + font: .korFont(style: isSelected ? .bold : .medium, size: 13), + lineHeight: 1.2 + ) } + } func updateBalloonHeight(isHidden: Bool, dynamicHeight: CGFloat = 160) { From 29ab38fd8fd9069b6b6b7e9769599f2da471a1be Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 7 May 2025 09:09:46 +0000 Subject: [PATCH 308/393] style/#130: Apply SwiftLint autocorrect --- .../Scene/Map/FillterSheetView/FilterBottomSheetView.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift index 7be75de4..a1b0dca4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/FilterBottomSheetView.swift @@ -317,7 +317,6 @@ final class FilterBottomSheetView: UIView { return button } - func updateMainLocationSelection(_ index: Int) { locationContentView.subviews.enumerated().forEach { (idx, view) in guard let button = view as? PPButton else { return } From 46217e4a8fbf13bf067e2b54aaa10e1a2dc3ce53 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 18:37:45 +0900 Subject: [PATCH 309/393] =?UTF-8?q?refactor/#131:=20View=EB=A1=9C=EB=B6=80?= =?UTF-8?q?=ED=84=B0=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B4=80?= =?UTF-8?q?=EC=8B=AC=EC=82=AC=EB=A5=BC=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/PopupSearchLayoutFactory.swift | 136 ++++++++++++++++++ .../Source/View/PopupSearchView.swift | 134 +---------------- 2 files changed, 140 insertions(+), 130 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift new file mode 100644 index 00000000..08e2c95a --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift @@ -0,0 +1,136 @@ +import UIKit + +// MARK: - Layout +final class PopupSearchLayoutFactory { + + func makeCollectionViewLayout( + dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? + ) -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, environment -> NSCollectionLayoutSection? in + guard let self = self, + let dataSource = dataSourceProvider() else { return nil } + + // sectionIndex를 사용하여 현재 dataSource에서 Section 타입을 가져옴 + guard sectionIndex < dataSource.snapshot().numberOfSections, + let sectionType = dataSource.sectionIdentifier(for: sectionIndex) else { return nil } + + switch sectionType { + case .recentSearch: + return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) + + case .category: + return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + + case .searchResult: + let sectionSnapshot = dataSource.snapshot(for: sectionType) + let hasEmptyItem = sectionSnapshot.items.contains { item in + if case .searchResultEmptyItem = item { return true } + return false + } + return makeSearchResultSectionLayout( + PopupSearchView.SectionHeaderKind.searchResult.rawValue, + hasEmptyItem: hasEmptyItem + ) + } + }) + } + + func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .absolute(31) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .estimated(31) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + + if headerKind == PopupSearchView.SectionHeaderKind.recentSearch.rawValue { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) + } else { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) + } + + section.interGroupSpacing = 6 + + section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] + + return section + } + + func makeSearchResultSectionLayout( + _ headerKind: String, + hasEmptyItem: Bool + ) -> NSCollectionLayoutSection { + let itemWidth: NSCollectionLayoutDimension = hasEmptyItem ? .fractionalWidth(1.0) : .fractionalWidth(0.5) + + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: itemWidth, + heightDimension: .absolute(249) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(249) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item, item] + ) + group.interItemSpacing = .fixed(16) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 24 + + section.boundarySupplementaryItems = [makePopupGridCollectionHeaderLayout(headerKind)] + + return section + } + + func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(24) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + + return header + } + + func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + // Header + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(22) + ) + let header = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + + return header + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 380f0b8e..5d108572 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -30,6 +30,7 @@ final class PopupSearchView: UIView { } // MARK: - Properties + private let layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() let recentSearchTagRemoveButtonTapped = PublishRelay() let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() @@ -42,7 +43,9 @@ final class PopupSearchView: UIView { let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { - $0.setCollectionViewLayout(self.makeLayout(), animated: false) + let layout = layoutFactory.makeCollectionViewLayout { [weak self] in self?.dataSource } + + $0.setCollectionViewLayout(layout, animated: false) $0.register( TagCollectionHeaderView.self, @@ -131,135 +134,6 @@ private extension PopupSearchView { } } -// MARK: - Layout -private extension PopupSearchView { - private func makeLayout() -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout { [weak self] sectionIndex, environment in - guard let self else { return nil } - - let sections = getSectionsFromDataSource() - guard sectionIndex < sections.count else { return nil } - - switch sections[sectionIndex] { - case .recentSearch: - return makeTagSectionLayout(SectionHeaderKind.recentSearch.rawValue) - - case .category: - return makeTagSectionLayout(SectionHeaderKind.category.rawValue) - - case .searchResult: - return makeSearchResultSectionLayout(SectionHeaderKind.searchResult.rawValue) - } - } - } - - func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .estimated(31) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .continuous - - if headerKind == SectionHeaderKind.recentSearch.rawValue { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) - } else { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) - } - - section.interGroupSpacing = 6 - - section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] - - return section - } - - func makeSearchResultSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { - // Determine if there is a searchResultEmptyItem in the current visible items to adjust item width - let sectionItems = collectionView.indexPathsForVisibleItems.compactMap { - dataSource?.itemIdentifier(for: $0) - } - - let hasEmptyItem = sectionItems.contains { - if case .searchResultEmptyItem = $0 { return true } - return false - } - - let itemWidth: NSCollectionLayoutDimension = hasEmptyItem ? .fractionalWidth(1.0) : .fractionalWidth(0.5) - - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: itemWidth, - heightDimension: .absolute(249) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(249) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item, item] - ) - group.interItemSpacing = .fixed(16) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) - section.interGroupSpacing = 24 - - section.boundarySupplementaryItems = [makePopupGridCollectionHeaderLayout(headerKind)] - - return section - } - - func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(24) - ) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) - - return header - } - - func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(22) - ) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) - - return header - } -} - // MARK: - DataSource extension PopupSearchView { private func configurationDataSourceItem() { From 1ed55633a41252d3536dad9dae32cbffc340e55d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 22:02:41 +0900 Subject: [PATCH 310/393] =?UTF-8?q?fix/#131:=20=EC=8B=A4=EA=B8=B0=EA=B8=B0?= =?UTF-8?q?=20=EB=B9=8C=EB=93=9C=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Infrastructure.xcodeproj/project.pbxproj | 8 -- .../DesignSystem.xcodeproj/project.pbxproj | 16 ---- .../SearchFeature.xcodeproj/project.pbxproj | 85 +++++++++++-------- .../Source/View/PopupSearchView.swift | 2 +- .../SearchFeatureDemo/Resource/Info.plist | 2 + 5 files changed, 53 insertions(+), 60 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj index d5ab556c..28bbe296 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure.xcodeproj/project.pbxproj @@ -9,7 +9,6 @@ /* Begin PBXBuildFile section */ 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D62DB67B4F00B141FF /* RxSwift */; }; 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93D82DB6605100771CB3 /* RxCocoa */; }; - 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC93DD2DB6612100771CB3 /* RxGesture */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -43,7 +42,6 @@ buildActionMask = 2147483647; files = ( 0522C1D72DB67B4F00B141FF /* RxSwift in Frameworks */, - 05EC93DE2DB6612100771CB3 /* RxGesture in Frameworks */, 05EC93D92DB6605100771CB3 /* RxCocoa in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -108,7 +106,6 @@ name = Infrastructure; packageProductDependencies = ( 05EC93D82DB6605100771CB3 /* RxCocoa */, - 05EC93DD2DB6612100771CB3 /* RxGesture */, 0522C1D62DB67B4F00B141FF /* RxSwift */, ); productName = Infrastructure; @@ -432,11 +429,6 @@ package = 05C1D82E2DB53CE300508FFD /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxCocoa; }; - 05EC93DD2DB6612100771CB3 /* RxGesture */ = { - isa = XCSwiftPackageProductDependency; - package = 05EC93DC2DB6612100771CB3 /* XCRemoteSwiftPackageReference "RxGesture" */; - productName = RxGesture; - }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 058CC90F2DB5383C0084221A /* Project object */; diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj index ebe58ead..307aab52 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 0516312C2DC3D1E900A6C0D1 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */; }; - 0516312D2DC3D1E900A6C0D1 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 051631602DC3D28400A6C0D1 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0516315F2DC3D28400A6C0D1 /* RxCocoa */; }; 051631622DC3D28400A6C0D1 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 051631612DC3D28400A6C0D1 /* RxSwift */; }; 051631652DC3D29400A6C0D1 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 051631642DC3D29400A6C0D1 /* SnapKit */; }; @@ -16,20 +15,6 @@ 0516316B2DC3D50700A6C0D1 /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 0516316A2DC3D50700A6C0D1 /* Pageboy */; }; /* End PBXBuildFile section */ -/* Begin PBXCopyFilesBuildPhase section */ - 0516312E2DC3D1E900A6C0D1 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 0516312D2DC3D1E900A6C0D1 /* Infrastructure.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 051630B82DC3D1A000A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0516312B2DC3D1E900A6C0D1 /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -106,7 +91,6 @@ 051630B42DC3D1A000A6C0D1 /* Sources */, 051630B52DC3D1A000A6C0D1 /* Frameworks */, 051630B62DC3D1A000A6C0D1 /* Resources */, - 0516312E2DC3D1E900A6C0D1 /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 0dffd886..2ff75c52 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,8 +7,21 @@ objects = { /* Begin PBXBuildFile section */ - 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; - 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; + 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBE82DCB90070051129F /* RxSwift */; }; + 05CFFBEA2DCB90210051129F /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; }; + 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; }; + 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBEE2DCB90460051129F /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; }; + 05CFFBEF2DCB90460051129F /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBF02DCB90580051129F /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; }; + 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBF32DCB906F0051129F /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBF22DCB906F0051129F /* RxCocoa */; }; + 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; + 05CFFBF52DCB908B0051129F /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBF62DCB90A10051129F /* SnapKit */; }; 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233B2DC49A7600C761A5 /* RxCocoa */; }; 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233D2DC49A7600C761A5 /* RxSwift */; }; 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC23402DC49A8B00C761A5 /* ReactorKit */; }; @@ -16,15 +29,6 @@ 05EC234B2DC49AB400C761A5 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234A2DC49AB400C761A5 /* Then */; }; 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234D2DC49AC100C761A5 /* SnapKit */; }; 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; - 05EC28602DC5C1CF00C761A5 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC28662DC5FDF800C761A5 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; }; - 05EC28672DC5FDF800C761A5 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28642DC5FDF800C761A5 /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC28682DC5FDF800C761A5 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; }; - 05EC28692DC5FDF800C761A5 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC28652DC5FDF800C761A5 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC286B2DC5FE5B00C761A5 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; }; - 05EC286C2DC5FE5B00C761A5 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286A2DC5FE5B00C761A5 /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; }; - 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC28E32DC696E000C761A5 /* PanModal */; }; 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC2AE82DC7C07400C761A5 /* RxRelay */; }; /* End PBXBuildFile section */ @@ -40,28 +44,18 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 0516364F2DC45E6400A6C0D1 /* Embed Frameworks */ = { + 05CFFBE72DCB8F6C0051129F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 05EC286C2DC5FE5B00C761A5 /* Data.framework in Embed Frameworks */, - 05EC28692DC5FDF800C761A5 /* DomainInterface.framework in Embed Frameworks */, - 05EC286F2DC5FE6000C761A5 /* Infrastructure.framework in Embed Frameworks */, - 05EC28672DC5FDF800C761A5 /* Domain.framework in Embed Frameworks */, - 0516364C2DC45E6300A6C0D1 /* SearchFeature.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 05EC28612DC5C1CF00C761A5 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05EC28602DC5C1CF00C761A5 /* DesignSystem.framework in Embed Frameworks */, + 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */, + 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */, + 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */, + 05CFFBF52DCB908B0051129F /* DesignSystem.framework in Embed Frameworks */, + 05CFFBEF2DCB90460051129F /* DomainInterface.framework in Embed Frameworks */, + 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -126,11 +120,15 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 05EC286B2DC5FE5B00C761A5 /* Data.framework in Frameworks */, - 05EC28682DC5FDF800C761A5 /* DomainInterface.framework in Frameworks */, - 05EC286E2DC5FE6000C761A5 /* Infrastructure.framework in Frameworks */, - 05EC28662DC5FDF800C761A5 /* Domain.framework in Frameworks */, - 0516364B2DC45E6300A6C0D1 /* SearchFeature.framework in Frameworks */, + 05CFFBEE2DCB90460051129F /* DomainInterface.framework in Frameworks */, + 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */, + 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, + 05CFFBEA2DCB90210051129F /* Data.framework in Frameworks */, + 05CFFBF32DCB906F0051129F /* RxCocoa in Frameworks */, + 05CFFBF02DCB90580051129F /* Infrastructure.framework in Frameworks */, + 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */, + 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */, + 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -190,7 +188,6 @@ 051633692DC457A900A6C0D1 /* Sources */, 0516336A2DC457A900A6C0D1 /* Frameworks */, 0516336B2DC457A900A6C0D1 /* Resources */, - 05EC28612DC5C1CF00C761A5 /* Embed Frameworks */, ); buildRules = ( ); @@ -220,7 +217,7 @@ 0516362B2DC457DE00A6C0D1 /* Sources */, 0516362C2DC457DE00A6C0D1 /* Frameworks */, 0516362D2DC457DE00A6C0D1 /* Resources */, - 0516364F2DC45E6400A6C0D1 /* Embed Frameworks */, + 05CFFBE72DCB8F6C0051129F /* Embed Frameworks */, ); buildRules = ( ); @@ -232,6 +229,9 @@ ); name = SearchFeatureDemo; packageProductDependencies = ( + 05CFFBE82DCB90070051129F /* RxSwift */, + 05CFFBF22DCB906F0051129F /* RxCocoa */, + 05CFFBF62DCB90A10051129F /* SnapKit */, ); productName = SearchFeatureDemo; productReference = 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */; @@ -682,6 +682,21 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 05CFFBE82DCB90070051129F /* RxSwift */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxSwift; + }; + 05CFFBF22DCB906F0051129F /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; + 05CFFBF62DCB90A10051129F /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; 05EC233B2DC49A7600C761A5 /* RxCocoa */ = { isa = XCSwiftPackageProductDependency; package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 5d108572..8c9838e8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -271,7 +271,7 @@ extension PopupSearchView { } snapshot.reloadSections([.searchResult]) - dataSource?.apply(snapshot, animatingDifferences: false) + dataSource?.apply(snapshot, animatingDifferences: true) } func getSectionsFromDataSource() -> [Section] { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist index b0c124ea..a343d54d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Info.plist @@ -2,6 +2,8 @@ + UIUserInterfaceStyle + Light UIAppFonts GothicA1-Bold.ttf From 648af9b0b941dcf279296cbbb8ef1a65202d2a30 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 22:17:10 +0900 Subject: [PATCH 311/393] =?UTF-8?q?refactor/#131:=20Int=ED=98=95=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=A0=84=ED=99=98=EC=9D=84=20toDomain=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EC=9E=98=20=EB=B3=B4=EC=9D=B4=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift | 2 +- .../Entity/AuthResponse/CategoryResponse.swift | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift index 424d9cff..63e0520b 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/CategoryAPI/ResponseDTO/GetCategoryListResponseDTO.swift @@ -15,6 +15,6 @@ struct CategoryResponseDTO: Codable { extension CategoryResponseDTO { func toDomain() -> CategoryResponse { - return CategoryResponse(categoryId: categoryId, category: categoryName) + return CategoryResponse(categoryId: Int(categoryId), category: categoryName) } } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift index d45f60d2..84810945 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AuthResponse/CategoryResponse.swift @@ -1,8 +1,8 @@ import Foundation public struct CategoryResponse { - public init(categoryId: Int32, category: String) { - self.categoryId = Int(categoryId) + public init(categoryId: Int, category: String) { + self.categoryId = categoryId self.category = category } From 081bccf4028ec94d43f3df52172f4d12823b1c64 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Wed, 7 May 2025 22:29:02 +0900 Subject: [PATCH 312/393] =?UTF-8?q?fix/#131:=20=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=EC=8B=9C=EC=8A=A4=ED=85=9C=20=ED=8F=B0=ED=8A=B8=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=EB=90=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 --- .../DetailCommentSection/DetailCommentSectionCell.swift | 6 +++--- .../DetailCommentTitleSectionCell.swift | 2 +- .../DetailEmptyCommetSectionCell.swift | 2 +- .../Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift | 2 +- .../MyPageMyCommentTitleSectionCell.swift | 2 +- .../MyPage/ProfileEdit/Main/View/ProfileEditView.swift | 4 ++-- .../View/SearchTitleSection/SearchTitleSectionCell.swift | 2 +- .../Presentation/Scene/Search/Main/SearchMainView.swift | 2 +- .../SignUp/SignUpComplete/SignUpCompleteController.swift | 4 ++-- .../Presentation/Scene/SignUp/Step2/SignUpStep2View.swift | 4 ++-- .../Scene/TabbarController/TabbarController.swift | 4 ++-- 11 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift index 9af61fc9..b270dcd1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentSection/DetailCommentSectionCell.swift @@ -321,20 +321,20 @@ extension DetailCommentSectionCell: Inputable { // 기본 스타일 (폰트, 색상 등) let normalAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.korFont(style: .regular, size: 14)!, + .font: UIFont.korFont(style: .regular, size: 14), .foregroundColor: UIColor.g1000, .paragraphStyle: paragraphStyle ] // 스타일을 다르게 할 부분 (팝업스토어명, 생생한 후기) let popupStoreAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.korFont(style: .bold, size: 14)!, // 다른 폰트 스타일 + .font: UIFont.korFont(style: .bold, size: 14), // 다른 폰트 스타일 .foregroundColor: UIColor.blu500, // 다른 색상 .paragraphStyle: paragraphStyle ] let reviewAttributes: [NSAttributedString.Key: Any] = [ - .font: UIFont.korFont(style: .bold, size: 14)!, // 이탤릭체 + .font: UIFont.korFont(style: .bold, size: 14), // 이탤릭체 .foregroundColor: UIColor.g1000, // 다른 색상 .paragraphStyle: paragraphStyle ] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift index b1c01a55..c0ebbf91 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailCommentTitleSection/DetailCommentTitleSectionCell.swift @@ -23,7 +23,7 @@ final class DetailCommentTitleSectionCell: UICollectionViewCell { let attributedTitle = NSAttributedString( string: "전체보기", attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 + .font: UIFont.korFont(style: .regular, size: 13), // 커스텀 폰트 적용 .underlineStyle: NSUnderlineStyle.single.rawValue // 밑줄 스타일 ] ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift index 782726dd..58fb2834 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/DetailEmptyCommetSection/DetailEmptyCommetSectionCell.swift @@ -22,7 +22,7 @@ final class DetailEmptyCommetSectionCell: UICollectionViewCell { let attributedTitle = NSAttributedString( string: "첫번째 코멘트 남기기", attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 커스텀 폰트 적용 + .font: UIFont.korFont(style: .regular, size: 13), // 커스텀 폰트 적용 .underlineStyle: NSUnderlineStyle.single.rawValue // 밑줄 스타일 ] ) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift index 8bec6e51..e6d6d13b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkView.swift @@ -37,7 +37,7 @@ final class MyPageBookmarkView: UIView { let buttonTitle = NSAttributedString( string: "추천 팝업 보러가기", attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, + .font: UIFont.korFont(style: .regular, size: 13), .underlineStyle: NSUnderlineStyle.single.rawValue, .foregroundColor: UIColor.g1000 ] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift index e38af1f4..e2c3bb94 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/View/MyPageMyCommentTitleSection/MyPageMyCommentTitleSectionCell.swift @@ -62,7 +62,7 @@ extension MyPageMyCommentTitleSectionCell: Inputable { let buttonTitle = NSAttributedString( string: input.buttonTitle ?? "", attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, + .font: UIFont.korFont(style: .regular, size: 13), .underlineStyle: NSUnderlineStyle.single.rawValue, .foregroundColor: UIColor.g600 ] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift index 9b51c288..55d974ef 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift @@ -91,7 +91,7 @@ final class ProfileEditView: UIView { let attributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13), // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g1000 // 텍스트 색상 ] @@ -99,7 +99,7 @@ final class ProfileEditView: UIView { let disabledAttributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13), // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g300 // 텍스트 색상 ] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift index e18fc6fc..98453aa0 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift @@ -68,7 +68,7 @@ extension SearchTitleSectionCell: Inputable { titleButton.isHidden = false let attributes: [NSAttributedString.Key: Any] = [ .underlineStyle: NSUnderlineStyle.single.rawValue, - .font: UIFont.korFont(style: .regular, size: 13)! + .font: UIFont.korFont(style: .regular, size: 13) ] let attributedTitle = NSAttributedString(string: buttonTitle, attributes: attributes) titleButton.setAttributedTitle(attributedTitle, for: .normal) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift index 16428a54..f71bf7ac 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift @@ -35,7 +35,7 @@ final class SearchMainView: UIView { $0.setPlaceholder( text: "팝업스토어명을 입력해보세요", color: .g400, - font: .korFont(style: .regular, size: 14)! + font: .korFont(style: .regular, size: 14) ) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift index 4b2e36a8..c76da8a8 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/SignUpComplete/SignUpCompleteController.swift @@ -64,13 +64,13 @@ extension SignUpCompleteController { let attributedText = NSMutableAttributedString( string: categoryString, attributes: [ - .font: UIFont.korFont(style: .bold, size: 15)!, + .font: UIFont.korFont(style: .bold, size: 15), .foregroundColor: UIColor.g600, NSAttributedString.Key.paragraphStyle: paragraphStyle ] ) attributedText.append(NSAttributedString(string: "와 연관된 팝업스토어 정보를 안내해드릴게요.", attributes: [ - .font: UIFont.korFont(style: .regular, size: 15)!, + .font: UIFont.korFont(style: .regular, size: 15), .foregroundColor: UIColor.g600, NSAttributedString.Key.paragraphStyle: paragraphStyle ])) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift index 3654affd..3e1c98f7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift @@ -70,7 +70,7 @@ final class SignUpStep2View: UIView { let attributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13), // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g1000 // 텍스트 색상 ] @@ -78,7 +78,7 @@ final class SignUpStep2View: UIView { let disabledAttributedTitle = NSAttributedString( string: title, attributes: [ - .font: UIFont.korFont(style: .regular, size: 13)!, // 폰트 + .font: UIFont.korFont(style: .regular, size: 13), // 폰트 .underlineStyle: NSUnderlineStyle.single.rawValue, // 밑줄 스타일 .foregroundColor: UIColor.g300 // 텍스트 색상 ] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift index 8bee4e8b..69d4639b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/TabbarController/TabbarController.swift @@ -236,11 +236,11 @@ class WaveTabBarController: UITabBarController, UITabBarControllerDelegate { // 폰트 설정 let appearance = UITabBarAppearance() appearance.stackedLayoutAppearance.normal.titleTextAttributes = [ - .font: UIFont.korFont(style: .medium, size: 11)!, + .font: UIFont.korFont(style: .medium, size: 11), .paragraphStyle: paragraphStyle ] appearance.stackedLayoutAppearance.selected.titleTextAttributes = [ - .font: UIFont.korFont(style: .bold, size: 11)!, + .font: UIFont.korFont(style: .bold, size: 11), .paragraphStyle: paragraphStyle ] From 1d8aaa38a13d16d06da6f16cc6b876f4076529d0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 00:55:35 +0900 Subject: [PATCH 313/393] =?UTF-8?q?refactor/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EB=A6=AC=EC=95=A1=ED=84=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/FilterSelectReactor.swift | 59 +++++++++++++++---- .../FilterSelectViewController.swift | 38 +++++++----- .../View/PopupSearchViewController.swift | 4 +- 3 files changed, 72 insertions(+), 29 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift index 433736ff..196b1e5f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift @@ -8,21 +8,26 @@ final class FilterSelectReactor: Reactor { // MARK: - Reactor enum Action { - case changeStatus(status: PopupStatus) - case changeSort(sort: PopupSort) + case closeButtonTapped + case statusSegmentChanged(index: Int) + case sortSegmentChanged(index: Int) case saveButtonTapped } enum Mutation { + case dismiss case changeStatus(status: PopupStatus) case changeSort(sort: PopupSort) + case updateSaveButtonEnable case saveCurrentFilter } struct State { var selectedFilter: Filter var saveButtonIsEnable: Bool = false - var isSaveButtonTapped: Bool = false + + @Pulse var saveButtonTapped: Void? + @Pulse var dismiss: Void? } // MARK: - properties @@ -38,14 +43,42 @@ final class FilterSelectReactor: Reactor { // MARK: - Reactor Methods func mutate(action: Action) -> Observable { switch action { - case .changeStatus(let status): - return Observable.just(.changeStatus(status: status)) - - case .changeSort(let sort): - return Observable.just(.changeSort(sort: sort)) + case .closeButtonTapped: + return .just(.dismiss) + + case .statusSegmentChanged(let index): + switch index == 0 { + case true: + return .concat([ + .just(.changeStatus(status: .open)), + .just(.updateSaveButtonEnable) + ]) + case false: + return .concat([ + .just(.changeStatus(status: .closed)), + .just(.updateSaveButtonEnable) + ]) + } + + case .sortSegmentChanged(let index): + switch index == 0 { + case true: + return .concat([ + .just(.changeSort(sort: .newest)), + .just(.updateSaveButtonEnable) + ]) + case false: + return .concat([ + .just(.changeSort(sort: .popularity)), + .just(.updateSaveButtonEnable) + ]) + } case .saveButtonTapped: - return Observable.just(.saveCurrentFilter) + return .concat([ + .just(.saveCurrentFilter), + .just(.dismiss) + ]) } } @@ -53,18 +86,22 @@ final class FilterSelectReactor: Reactor { var newState = state switch mutation { + case .dismiss: + newState.dismiss = () + case .changeStatus(let status): newState.selectedFilter.status = status - newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) case .changeSort(let sort): newState.selectedFilter.sort = sort + + case .updateSaveButtonEnable: newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) case .saveCurrentFilter: Filter.shared.status = newState.selectedFilter.status Filter.shared.sort = newState.selectedFilter.sort - newState.isSaveButtonTapped = true + newState.saveButtonTapped = () } return newState diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index 2315100b..36b8b66d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -32,46 +32,54 @@ extension FilterSelectViewController { // MARK: - Methods extension FilterSelectViewController { func bind(reactor: Reactor) { + self.bindAction(reactor: reactor) + self.bindState(reactor: reactor) + } + + private func bindAction(reactor: Reactor) { mainView.closeButton.rx.tap - .withUnretained(self) - .subscribe(onNext: { (owner, _) in owner.dismiss(animated: true) }) + .map { _ in Reactor.Action.closeButtonTapped } + .bind(to: reactor.action) .disposed(by: disposeBag) mainView.statusSegmentControl.rx.controlEvent(.valueChanged) .withUnretained(self) - .map { (owner, _) in - if owner.mainView.statusSegmentControl.selectedSegmentIndex == 0 { - Reactor.Action.changeStatus(status: .open) - } else { Reactor.Action.changeStatus(status: .closed) } - } + .map { (owner, _) in Reactor.Action.statusSegmentChanged(index: owner.mainView.statusSegmentControl.selectedSegmentIndex) } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.sortSegmentControl.rx.controlEvent(.valueChanged) .withUnretained(self) - .map { (owner, _) in - if owner.mainView.sortSegmentControl.selectedSegmentIndex == 0 { - Reactor.Action.changeSort(sort: .newest) - } else { Reactor.Action.changeSort(sort: .popularity) } - } + .map { (owner, _) in Reactor.Action.sortSegmentChanged(index: owner.mainView.sortSegmentControl.selectedSegmentIndex) } .bind(to: reactor.action) .disposed(by: disposeBag) mainView.saveButton.rx.tap .withUnretained(self) - .do { (owner, _) in owner.dismiss(animated: true) } .map { (owner, _) in Reactor.Action.saveButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + } - reactor.state + private func bindState(reactor: Reactor) { + reactor.state.distinctUntilChanged(\.selectedFilter) .withUnretained(self) .subscribe { (owner, state) in owner.mainView.statusSegmentControl.selectedSegmentIndex = state.selectedFilter.status.index owner.mainView.sortSegmentControl.selectedSegmentIndex = state.selectedFilter.sort.index - owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } .disposed(by: disposeBag) + + reactor.state.distinctUntilChanged(\.saveButtonIsEnable) + .withUnretained(self) + .subscribe { (owner, state) in owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } + .disposed(by: disposeBag) + + + reactor.pulse(\.$dismiss) + .withUnretained(self) + .subscribe { (owner, _) in owner.dismiss(animated: true) } + .disposed(by: disposeBag) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 08459b5f..94a5d020 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -138,7 +138,6 @@ extension PopupSearchViewController { let viewController = CategorySelectViewController() viewController.reactor = categoryReactor - #warning("pulse에서 bind하는 구조...? 개선 가능하려나") categoryReactor.state .filter { $0.isSaveOrResetButtonTapped } .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } @@ -151,8 +150,7 @@ extension PopupSearchViewController { let viewController = FilterSelectViewController() viewController.reactor = FilterSelectReactor() - viewController.reactor?.state - .filter { $0.isSaveButtonTapped == true } + viewController.reactor?.pulse(\.$saveButtonTapped) .map { _ in Reactor.Action.filterSaveButtonTapped } .bind(to: reactor.action) .disposed(by: owner.disposeBag) From 1984486f20aaa0fa2c2e8491a12d02d4d8dde665 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 01:56:15 +0900 Subject: [PATCH 314/393] =?UTF-8?q?refactor/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EB=A6=AC=EC=95=A1=ED=84=B0=20=EA=B0=9C?= =?UTF-8?q?=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reactor/CategorySelectReactor.swift | 69 ++++++++++++------- .../Source/Reactor/PopupSearchReactor.swift | 7 +- .../CategorySelectViewController.swift | 61 ++++++++-------- .../View/PopupSearchViewController.swift | 15 ++-- 4 files changed, 83 insertions(+), 69 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index 840467f1..bf254033 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -11,30 +11,35 @@ final class CategorySelectReactor: Reactor { // MARK: - Reactor enum Action { case viewWillAppear + case closeButtonTapped case resetButtonTapped case saveButtonTapped - case cellTapped(categoryID: Int) + case categoryTagButtonTapped(indexPath: IndexPath) } enum Mutation { case setupCategotyTag(items: [TagCollectionViewCell.Input]) + case dismiss case resetCategory case saveCategory - case toggleTappedCell(categoryID: Int) + case updateCategoryTagSelection(categoryID: Int) + case updateSaveButtonEnable + case updateSelectedCategory } struct State { var categoryItems: [TagCollectionViewCell.Input] = [] var saveButtonIsEnable: Bool = false - var isSaveOrResetButtonTapped: Bool = false + var selectedCategoryChanged: Bool? + + @Pulse var dismiss: Void? } // MARK: - properties - var initialState: State - private var originCategoryItems: [TagCollectionViewCell.Input] = [] var disposeBag = DisposeBag() + private var originCategoryItems: [TagCollectionViewCell.Input] = [] private let fetchCategoryListUseCase: FetchCategoryListUseCase // MARK: - init @@ -53,23 +58,34 @@ final class CategorySelectReactor: Reactor { .withUnretained(self) .map { (owner, response) in let items = response.map { - return TagCollectionViewCell.Input( - title: $0.category, - id: $0.categoryId, - isCancelable: false - ) + return TagCollectionViewCell.Input(title: $0.category, id: $0.categoryId, isCancelable: false) } return .setupCategotyTag(items: items) } - + + case .closeButtonTapped: + return .just(.dismiss) + case .resetButtonTapped: - return Observable.just(.resetCategory) + return Observable.concat([ + .just(.resetCategory), + .just(.dismiss), + .just(.updateSelectedCategory) + ]) case .saveButtonTapped: - return Observable.just(.saveCategory) - - case .cellTapped(let categoryID): - return Observable.just(.toggleTappedCell(categoryID: categoryID)) + return Observable.concat([ + .just(.saveCategory), + .just(.dismiss), + .just(.updateSelectedCategory) + ]) + + case .categoryTagButtonTapped(let indexPath): + guard let categoryID = currentState.categoryItems[indexPath.row].id else { return .empty() } + return Observable.concat([ + .just(.updateCategoryTagSelection(categoryID: categoryID)), + .just(.updateSaveButtonEnable) + ]) } } @@ -79,29 +95,34 @@ final class CategorySelectReactor: Reactor { switch mutation { case .setupCategotyTag(let items): let fetchedItems = items.map { - if let id = $0.id, Category.shared.contains(id: id) { - return $0.selectionToggledItem() - } else { return $0 } + if let id = $0.id, Category.shared.contains(id: id) { return $0.selectionToggledItem() } + else { return $0 } } - originCategoryItems = fetchedItems newState.categoryItems = fetchedItems + case .dismiss: + newState.dismiss = () + case .resetCategory: - if originCategoryItems.isEmpty { break } Category.shared.resetItems() - newState.isSaveOrResetButtonTapped = true + newState.selectedCategoryChanged = true case .saveCategory: Category.shared.items = newState.categoryItems.filter { $0.isSelected == true } - newState.isSaveOrResetButtonTapped = true + newState.selectedCategoryChanged = true - case .toggleTappedCell(let categoryID): + case .updateCategoryTagSelection(let categoryID): newState.categoryItems = state.categoryItems.map { if $0.id == categoryID { return $0.selectionToggledItem() } else { return $0 } } + + case .updateSaveButtonEnable: newState.saveButtonIsEnable = (originCategoryItems != newState.categoryItems) + + case .updateSelectedCategory: + newState.selectedCategoryChanged = (originCategoryItems != newState.categoryItems) } return newState diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 5429b19d..76f7d378 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -25,13 +25,12 @@ public final class PopupSearchReactor: Reactor { case categoryTagRemoveButtonTapped(categoryID: Int) case categoryTagButtonTapped + case categoryChangedBySelector case searchResultFilterButtonTapped + case searchResultFilterChangedBySelector case searchResultItemTapped case searchResultPrefetchItems(indexPathList: [IndexPath]) - - case filterSaveButtonTapped - case categorySaveOrResetButtonTapped } public enum Mutation { @@ -240,7 +239,7 @@ public final class PopupSearchReactor: Reactor { case .searchResultItemTapped: return .empty() - case .filterSaveButtonTapped, .categorySaveOrResetButtonTapped: + case .searchResultFilterChangedBySelector, .categoryChangedBySelector: return fetchSearchResult() .withUnretained(self) .flatMap { (owner, response) -> Observable in diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 896ab0ca..51c6ad95 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -32,59 +32,54 @@ extension CategorySelectViewController { // MARK: - Methods extension CategorySelectViewController { func bind(reactor: Reactor) { + self.bindAction(reactor: reactor) + self.bindState(reactor: reactor) + } + + private func bindAction(reactor: Reactor) { rx.viewWillAppear .map { Reactor.Action.viewWillAppear } .bind(to: reactor.action) .disposed(by: disposeBag) - reactor.state - .map { $0.categoryItems } - .distinctUntilChanged() - .bind(to: mainView.collectionView.rx.items( - cellIdentifier: TagCollectionViewCell.identifiers, - cellType: TagCollectionViewCell.self - )) { index, input, cell in - cell.injection(with: input) - } + mainView.closeButton.rx.tap + .map { Reactor.Action.closeButtonTapped } + .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.collectionView.rx.itemSelected - .withLatestFrom( - reactor.state.map { Array($0.categoryItems) }, - resultSelector: { indexPath, items in items[indexPath.item] } - ) - .compactMap { $0.id } - .map { Reactor.Action.cellTapped(categoryID: $0) } + mainView.resetButton.rx.tap + .map { Reactor.Action.resetButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) - mainView.closeButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in owner.dismiss(animated: true) } + mainView.saveButton.rx.tap + .map { Reactor.Action.saveButtonTapped } + .bind(to: reactor.action) .disposed(by: disposeBag) - - mainView.resetButton.rx.tap - .withUnretained(self) - .do { (owner, _) in owner.dismiss(animated: true) } - .map { (owner, _) in Reactor.Action.resetButtonTapped } + mainView.collectionView.rx.itemSelected + .map(Reactor.Action.categoryTagButtonTapped) .bind(to: reactor.action) .disposed(by: disposeBag) + } - mainView.saveButton.rx.tap + private func bindState(reactor: Reactor) { + reactor.pulse(\.$dismiss) .withUnretained(self) - .do { (owner, _) in owner.dismiss(animated: true) } - .map { (owner, _) in Reactor.Action.saveButtonTapped } - .bind(to: reactor.action) + .subscribe { (owner, _) in owner.dismiss(animated: true) } .disposed(by: disposeBag) + reactor.state.distinctUntilChanged(\.categoryItems) + .map(\.categoryItems) + .bind(to: mainView.collectionView.rx.items( + cellIdentifier: TagCollectionViewCell.identifiers, + cellType: TagCollectionViewCell.self + )) { _, item, cell in cell.injection(with: item) } + .disposed(by: disposeBag) - reactor.state + reactor.state.distinctUntilChanged(\.saveButtonIsEnable) .withUnretained(self) - .subscribe { (owner, state) in - owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable - // owner.mainView.collectionView.reloadItems - } + .subscribe { (owner, state) in owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } .disposed(by: disposeBag) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 94a5d020..b6003d41 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -132,16 +132,15 @@ extension PopupSearchViewController { .subscribe { owner, target in switch target { case .categorySelector: - let categoryReactor = CategorySelectReactor( + let viewController = CategorySelectViewController() + viewController.reactor = CategorySelectReactor( fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) ) - let viewController = CategorySelectViewController() - viewController.reactor = categoryReactor - categoryReactor.state - .filter { $0.isSaveOrResetButtonTapped } - .map { _ in Reactor.Action.categorySaveOrResetButtonTapped } - .bind(to: owner.reactor!.action) + viewController.reactor?.state.distinctUntilChanged(\.selectedCategoryChanged) + .filter { $0.selectedCategoryChanged == true } + .map { _ in Reactor.Action.categoryChangedBySelector } + .bind(to: reactor.action) .disposed(by: owner.disposeBag) owner.presentPanModal(viewController) @@ -151,7 +150,7 @@ extension PopupSearchViewController { viewController.reactor = FilterSelectReactor() viewController.reactor?.pulse(\.$saveButtonTapped) - .map { _ in Reactor.Action.filterSaveButtonTapped } + .map { _ in Reactor.Action.searchResultFilterChangedBySelector } .bind(to: reactor.action) .disposed(by: owner.disposeBag) From 2d7e31d176cc1dfd7542c677dc78cae50416c88b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 02:16:26 +0900 Subject: [PATCH 315/393] =?UTF-8?q?refactor/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=20=EB=A6=AC=EC=95=A1=ED=84=B0=20=EC=A2=80=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 - 지우기 버튼이 한번만 작동하는것을 수정 - 네이밍 통일 및 과도한 enum case화 제거 --- .../Source/Reactor/PopupSearchReactor.swift | 67 +++++++------------ .../View/PopupSearchViewController.swift | 14 ++-- 2 files changed, 29 insertions(+), 52 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 76f7d378..0d70bb78 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -42,16 +42,15 @@ public final class PopupSearchReactor: Reactor { case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) - case present(target: PresentTarget) - case clearButton(state: ClearButtonState) - case clearTextField - case endEditing - + case updateEditingState case updateSearchBar(to: String?) + case updateClearButtonIsHidden(to: Bool) case updateCurrentPage(to: Int32) - case updateSearching(to: Bool) + case updateSearchingState(to: Bool) case updateSearchResultEmptyCase case updateDataSource + + case present(target: PresentTarget) } public enum PresentTarget { @@ -59,29 +58,16 @@ public final class PopupSearchReactor: Reactor { case filterSelector } - public enum ClearButtonState { - var value: Bool { - switch self { - case .visible: return false - case .hidden: return true - } - } - - case visible - case hidden - } - public struct State { - var searchBarText: String? = nil var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] var searchResultHeader: SearchResultHeaderView.Input? = nil var searchResultEmptyCase: SearchResultEmptyCollectionViewCell.EmptyCase? + @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? - @Pulse var clearButton: ClearButtonState? - @Pulse var clearButtonTapped: Void? + @Pulse var clearButtonIsHidden: Bool? @Pulse var endEditing: Void? @Pulse var updateDataSource: Void? @@ -130,7 +116,7 @@ public final class PopupSearchReactor: Reactor { } case .searchBarEditing(let text): - return .just(.clearButton(state: text.isEmpty ? .hidden : .visible)) + return .just(.updateClearButtonIsHidden(to: text.isEmpty ? true : false)) case .searchBarExitEditing(let text): return fetchSearchResult(keyword: text) @@ -146,18 +132,18 @@ public final class PopupSearchReactor: Reactor { ))), // FIXME: API에 해당 결과값이 아직 없음 .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), - .just(.updateSearching(to: true)), + .just(.updateSearchingState(to: true)), .just(.updateSearchResultEmptyCase), - .just(.clearButton(state: .hidden)), - .just(.endEditing), + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateEditingState), .just(.updateDataSource) ]) } case .searchBarEndEditing: return .concat([ - .just(.clearButton(state: .hidden)), - .just(.endEditing) + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateEditingState) ]) case .searchBarCancelButtonTapped: @@ -172,10 +158,10 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearching(to: false)), + .just(.updateSearchingState(to: false)), .just(.updateSearchResultEmptyCase), - .just(.clearTextField), - .just(.endEditing), + .just(.updateSearchBar(to: nil)), + .just(.updateEditingState), .just(.updateDataSource) ]) } @@ -228,10 +214,10 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), .just(.updateSearchBar(to: keyword)), - .just(.updateSearching(to: true)), + .just(.updateSearchingState(to: true)), .just(.updateSearchResultEmptyCase), - .just(.clearButton(state: .hidden)), - .just(.endEditing), + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateEditingState), .just(.updateDataSource) ]) } @@ -257,8 +243,8 @@ public final class PopupSearchReactor: Reactor { case .searchBarClearButtonTapped: return Observable.concat([ - .just(.clearButton(state: .hidden)), - .just(.clearTextField) + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateSearchBar(to: nil)) ]) case .categoryTagRemoveButtonTapped(let categoryID): @@ -309,7 +295,7 @@ public final class PopupSearchReactor: Reactor { case .updateCurrentPage(let currentPage): newState.currentPage = currentPage - case .updateSearching(let isSearching): + case .updateSearchingState(let isSearching): newState.isSearching = isSearching case .updateSearchResultEmptyCase: @@ -321,13 +307,10 @@ public final class PopupSearchReactor: Reactor { case .present(let target): newState.present = target - case .clearButton(let state): - newState.clearButton = state - - case .clearTextField: - newState.clearButtonTapped = () + case .updateClearButtonIsHidden(let state): + newState.clearButtonIsHidden = state - case .endEditing: + case .updateEditingState: newState.endEditing = () } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index b6003d41..ce2b487c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -117,14 +117,10 @@ extension PopupSearchViewController { .subscribe { (owner, _) in owner.mainView.endEditing(true) } .disposed(by: disposeBag) - reactor.pulse(\.$clearButton) + reactor.pulse(\.$clearButtonIsHidden) + .compactMap { $0 } .withUnretained(self) - .subscribe { (owner, state) in owner.mainView.searchBar.clearButton.isHidden = state?.value ?? true } - .disposed(by: disposeBag) - - reactor.pulse(\.$clearButtonTapped) - .withUnretained(self) - .subscribe { (owner, _) in owner.mainView.searchBar.searchBar.searchTextField.text = nil } + .subscribe { (owner, state) in owner.mainView.searchBar.clearButton.isHidden = state } .disposed(by: disposeBag) reactor.pulse(\.$present) @@ -161,13 +157,11 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) - reactor.state.distinctUntilChanged(\.searchBarText) - .map { $0.searchBarText } + reactor.pulse(\.$searchBarText) .withUnretained(self) .subscribe { (owner, text) in owner.mainView.searchBar.searchBar.text = text } .disposed(by: disposeBag) - reactor.pulse(\.$updateDataSource) .withLatestFrom(reactor.state) .withUnretained(self) From 2b5f0afff8a4a15ddbd2b56baa26c336705867c6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 15:17:33 +0900 Subject: [PATCH 316/393] =?UTF-8?q?refactor/#131:=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=97=90=EC=84=9C=EB=A7=8C=20=EA=B5=AC=EC=B2=B4?= =?UTF-8?q?=EC=A0=81=EC=9D=B8=20=EB=B9=84=ED=8A=B8=EB=A5=BC=20=EB=94=B0?= =?UTF-8?q?=EB=A5=B4=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 --- .../API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift | 2 +- .../Data/RepositoryImpl/AdminRepositoryImpl.swift | 6 +++--- .../Data/Data/RepositoryImpl/MapRepositoryImpl.swift | 8 ++++---- .../Data/RepositoryImpl/SignUpRepositoryImpl.swift | 2 +- .../Data/RepositoryImpl/UserAPIRepositoryImpl.swift | 12 ++++++------ .../Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift | 4 ++-- .../Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift | 2 +- .../Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift | 2 +- .../Domain/UseCaseImpl/UserAPIUseCaseImpl.swift | 6 +++--- .../Entity/AdminResponse/AdminStoreDetail.swift | 4 ++-- .../Entity/AdminResponse/Params/AdminParams.swift | 8 ++++---- .../DomainInterface/Repository/MapRepository.swift | 4 ++-- .../Repository/SignUpRepository.swift | 2 +- .../Repository/UserAPIRepository.swift | 6 +++--- .../Domain/DomainInterface/UseCase/MapUseCase.swift | 4 ++-- .../DomainInterface/UseCase/PopUpAPIUseCase.swift | 2 +- .../DomainInterface/UseCase/SignUpAPIUseCase.swift | 2 +- .../DomainInterface/UseCase/UserAPIUseCase.swift | 2 +- Poppool/Poppool.xcodeproj/project.pbxproj | 6 ++++++ .../AdminRegister/PopUpStoreRegisterReactor.swift | 4 ++-- .../Presentation/Scene/Map/MapView/MapReactor.swift | 6 +++--- .../CategoryEditModal/CategoryEditModalReactor.swift | 12 ++++++------ .../Scene/Search/BeforeSearch/SearchReactor.swift | 2 +- .../CancelableTagSectionCell.swift | 2 +- .../CategoryController/SearchCategoryReactor.swift | 6 +++--- .../Scene/SignUp/Main/SignUpMainReactor.swift | 8 ++++---- .../Scene/SignUp/Step3/SignUpStep3Reactor.swift | 6 +++--- .../Step3/View/TagSection/TagSectionCell.swift | 2 +- 28 files changed, 69 insertions(+), 63 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift index 74cd1019..044c86ff 100644 --- a/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift +++ b/Poppool/DataLayer/Data/Data/Network/API/SignUpAPI/RequestDTO/SignUpRequestDTO.swift @@ -13,6 +13,6 @@ struct SignUpRequestDTO: Encodable { var age: Int32 var socialEmail: String var socialType: String - var interestCategories: [Int64] + var interestCategories: [Int] var appleAuthorizationCode: String? } diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift index da3f0d33..838ddbad 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/AdminRepositoryImpl.swift @@ -44,7 +44,7 @@ public final class AdminRepositoryImpl: AdminRepository { AdminStoreDetail( id: dto.id, name: dto.name, - categoryId: dto.categoryId, + categoryId: Int(dto.categoryId), categoryName: dto.categoryName, description: dto.desc, address: dto.address, @@ -77,7 +77,7 @@ public final class AdminRepositoryImpl: AdminRepository { public func createStore(params: CreateStoreParams) -> Completable { let dto = CreatePopUpStoreRequestDTO( name: params.name, - categoryId: params.categoryId, + categoryId: Int64(params.categoryId), desc: params.desc, address: params.address, startDate: params.startDate, @@ -99,7 +99,7 @@ public final class AdminRepositoryImpl: AdminRepository { popUpStore: UpdatePopUpStoreRequestDTO.PopUpStore( id: params.id, name: params.name, - categoryId: params.categoryId, + categoryId: Int64(params.categoryId), desc: params.desc, address: params.address, startDate: params.startDate, diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift index 236aad21..7ccb8492 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/MapRepositoryImpl.swift @@ -18,7 +18,7 @@ public final class MapRepositoryImpl: MapRepository { northEastLon: Double, southWestLat: Double, southWestLon: Double, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> { return provider.requestData( with: MapAPIEndpoint.locations_fetchStoresInBounds( @@ -26,7 +26,7 @@ public final class MapRepositoryImpl: MapRepository { northEastLon: northEastLon, southWestLat: southWestLat, southWestLon: southWestLon, - categories: categories + categories: categories.map { Int64($0 ) } ), interceptor: TokenInterceptor() ) @@ -35,12 +35,12 @@ public final class MapRepositoryImpl: MapRepository { public func searchStores( query: String, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> { return provider.requestData( with: MapAPIEndpoint.locations_searchStores( query: query, - categories: categories + categories: categories.map { Int64($0 ) } ), interceptor: TokenInterceptor() ) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift index f8009fbb..8b769303 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/SignUpRepositoryImpl.swift @@ -30,7 +30,7 @@ public final class SignUpRepositoryImpl: SignUpRepository { age: Int32, socialEmail: String, socialType: String, - interests: [Int64], + interests: [Int], appleAuthorizationCode: String? ) -> Completable { let endPoint = SignUpAPIEndpoint.signUp_trySignUp(with: .init( diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift index 2e8d9c98..ae4713ae 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/UserAPIRepositoryImpl.swift @@ -86,14 +86,14 @@ public final class UserAPIRepositoryImpl: UserAPIRepository { } public func putUserCategory( - interestCategoriesToAdd: [Int64], - interestCategoriesToDelete: [Int64], - interestCategoriesToKeep: [Int64] + interestCategoriesToAdd: [Int], + interestCategoriesToDelete: [Int], + interestCategoriesToKeep: [Int] ) -> Completable { let request = PutUserCategoryRequestDTO( - interestCategoriesToAdd: interestCategoriesToAdd, - interestCategoriesToDelete: interestCategoriesToDelete, - interestCategoriesToKeep: interestCategoriesToKeep + interestCategoriesToAdd: interestCategoriesToAdd.map { Int64($0 ) }, + interestCategoriesToDelete: interestCategoriesToDelete.map { Int64($0 ) }, + interestCategoriesToKeep: interestCategoriesToKeep.map { Int64($0 ) } ) let endPoint = UserAPIEndPoint.putUserCategory(request: request) return provider.request(with: endPoint, interceptor: tokenInterceptor) diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift index 5253690e..615e8776 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/MapUseCaseImpl.swift @@ -22,7 +22,7 @@ public final class MapUseCaseImpl: MapUseCase { northEastLon: Double, southWestLat: Double, southWestLon: Double, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> { return repository.fetchStoresInBounds( northEastLat: northEastLat, @@ -40,7 +40,7 @@ public final class MapUseCaseImpl: MapUseCase { public func searchStores( query: String, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> { return repository.searchStores( query: query, diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift index 716f1304..ee20c061 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/PopUpAPIUseCaseImpl.swift @@ -12,7 +12,7 @@ public final class PopUpAPIUseCaseImpl: PopUpAPIUseCase { self.repository = repository } - public func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable { + public func getSearchBottomPopUpList(isOpen: Bool, categories: [Int], page: Int32?, size: Int32, sort: String?) -> Observable { var categoryString: String? if !categories.isEmpty { categoryString = categories.map { String($0) + "," }.reduce("", +) diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift index 1086c284..f1b3497e 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/SignUpAPIUseCaseImpl.swift @@ -17,7 +17,7 @@ public final class SignUpAPIUseCaseImpl: SignUpAPIUseCase { age: Int32, socialEmail: String, socialType: String, - interests: [Int64], + interests: [Int], appleAuthorizationCode: String? ) -> Completable { return repository.trySignUp( diff --git a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift index c70de393..b42fed78 100644 --- a/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift +++ b/Poppool/DomainLayer/Domain/Domain/UseCaseImpl/UserAPIUseCaseImpl.swift @@ -76,9 +76,9 @@ public final class UserAPIUseCaseImpl: UserAPIUseCase { } public func putUserCategory( - interestCategoriesToAdd: [Int64], - interestCategoriesToDelete: [Int64], - interestCategoriesToKeep: [Int64] + interestCategoriesToAdd: [Int], + interestCategoriesToDelete: [Int], + interestCategoriesToKeep: [Int] ) -> Completable { return repository.putUserCategory( interestCategoriesToAdd: interestCategoriesToAdd, diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift index 7dbb50b8..fe4a5a2e 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/AdminStoreDetail.swift @@ -1,7 +1,7 @@ import Foundation public struct AdminStoreDetail { - public init(id: Int64, name: String, categoryId: Int64, categoryName: String, description: String, address: String, startDate: String, endDate: String, createUserId: String, createDateTime: String, mainImageUrl: String, bannerYn: Bool, images: [StoreImage], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String) { + public init(id: Int64, name: String, categoryId: Int, categoryName: String, description: String, address: String, startDate: String, endDate: String, createUserId: String, createDateTime: String, mainImageUrl: String, bannerYn: Bool, images: [StoreImage], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String) { self.id = id self.name = name self.categoryId = categoryId @@ -23,7 +23,7 @@ public struct AdminStoreDetail { public let id: Int64 public let name: String - public let categoryId: Int64 + public let categoryId: Int public let categoryName: String public let description: String public let address: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift index 029b9793..8e5f46fe 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Entity/AdminResponse/Params/AdminParams.swift @@ -1,7 +1,7 @@ import Foundation public struct CreateStoreParams { - public init(name: String, categoryId: Int64, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { + public init(name: String, categoryId: Int, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { self.name = name self.categoryId = categoryId self.desc = desc @@ -18,7 +18,7 @@ public struct CreateStoreParams { } public let name: String - public let categoryId: Int64 + public let categoryId: Int public let desc: String public let address: String public let startDate: String @@ -33,7 +33,7 @@ public struct CreateStoreParams { } public struct UpdateStoreParams { - public init(id: Int64, name: String, categoryId: Int64, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], imagesToDelete: [Int64], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { + public init(id: Int64, name: String, categoryId: Int, desc: String, address: String, startDate: String, endDate: String, mainImageUrl: String, imageUrlList: [String?], imagesToDelete: [Int64], latitude: Double, longitude: Double, markerTitle: String, markerSnippet: String, startDateBeforeEndDate: Bool) { self.id = id self.name = name self.categoryId = categoryId @@ -53,7 +53,7 @@ public struct UpdateStoreParams { public let id: Int64 public let name: String - public let categoryId: Int64 + public let categoryId: Int public let desc: String public let address: String public let startDate: String diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift index 4f086267..cf86192f 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/MapRepository.swift @@ -8,12 +8,12 @@ public protocol MapRepository { northEastLon: Double, southWestLat: Double, southWestLon: Double, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> func searchStores( query: String, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> func fetchCategories() -> Observable<[CategoryResponse]> diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift index 7ce31985..5aca1507 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/SignUpRepository.swift @@ -11,7 +11,7 @@ public protocol SignUpRepository { age: Int32, socialEmail: String, socialType: String, - interests: [Int64], + interests: [Int], appleAuthorizationCode: String? ) -> Completable } diff --git a/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift b/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift index 2b2658a0..ba4f8e6c 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/Repository/UserAPIRepository.swift @@ -27,9 +27,9 @@ public protocol UserAPIRepository { func putUserTailoredInfo(gender: String?, age: Int32) -> Completable func putUserCategory( - interestCategoriesToAdd: [Int64], - interestCategoriesToDelete: [Int64], - interestCategoriesToKeep: [Int64] + interestCategoriesToAdd: [Int], + interestCategoriesToDelete: [Int], + interestCategoriesToKeep: [Int] ) -> Completable func putUserProfile( diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift index b4c82a70..a56de0c9 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/MapUseCase.swift @@ -9,12 +9,12 @@ public protocol MapUseCase { northEastLon: Double, southWestLat: Double, southWestLon: Double, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> func searchStores( query: String, - categories: [Int64] + categories: [Int] ) -> Observable<[MapPopUpStore]> func filterStoresByLocation(_ stores: [MapPopUpStore], selectedRegions: [String]) -> [MapPopUpStore] diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift index 7748b47f..5c34b07b 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/PopUpAPIUseCase.swift @@ -3,7 +3,7 @@ import Foundation import RxSwift public protocol PopUpAPIUseCase { - func getSearchBottomPopUpList(isOpen: Bool, categories: [Int64], page: Int32?, size: Int32, sort: String?) -> Observable + func getSearchBottomPopUpList(isOpen: Bool, categories: [Int], page: Int32?, size: Int32, sort: String?) -> Observable func getSearchPopUpList(query: String?) -> Observable func getPopUpDetail(commentType: String?, popUpStoredId: Int64, isViewCount: Bool?) -> Observable func getPopUpComment(commentType: String?, page: Int32?, size: Int32?, sort: String?, popUpStoreId: Int64) -> Observable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift index dc8fb97d..89883291 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/SignUpAPIUseCase.swift @@ -9,7 +9,7 @@ public protocol SignUpAPIUseCase { age: Int32, socialEmail: String, socialType: String, - interests: [Int64], + interests: [Int], appleAuthorizationCode: String? ) -> Completable diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift index 6f74f9f8..b488b1a1 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/UserAPIUseCase.swift @@ -16,7 +16,7 @@ public protocol UserAPIUseCase { func postWithdrawl(surveyList: [GetWithdrawlListDataResponse]) -> Completable func getMyProfile() -> Observable func putUserTailoredInfo(gender: String?, age: Int32) -> Completable - func putUserCategory(interestCategoriesToAdd: [Int64], interestCategoriesToDelete: [Int64], interestCategoriesToKeep: [Int64]) -> Completable + func putUserCategory(interestCategoriesToAdd: [Int], interestCategoriesToDelete: [Int], interestCategoriesToKeep: [Int]) -> Completable func putUserProfile(profileImageUrl: String?, nickname: String?, email: String?, instagramId: String?, intro: String?) -> Completable func getMyCommentedPopUp(page: Int32?, size: Int32?, sort: String?) -> Observable func getBlockUserList(page: Int32?, size: Int32?, sort: String?) -> Observable diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index db866640..94d3a29d 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -20,6 +20,8 @@ 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; }; 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05CFFC432DCC83290051129F /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; }; + 05CFFC442DCC83290051129F /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; @@ -35,6 +37,7 @@ 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */, 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */, 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */, + 05CFFC442DCC83290051129F /* DesignSystem.framework in Embed Frameworks */, 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */, 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */, ); @@ -50,6 +53,7 @@ 05C1D6082DB53A4900508FFD /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D6092DB53A4900508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D60A2DB53A4900508FFD /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05CFFC422DCC83290051129F /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BDCA41BD2CF35AC0005EECF6 /* Poppool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Poppool.app; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -93,6 +97,7 @@ 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */, 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */, + 05CFFC432DCC83290051129F /* DesignSystem.framework in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, ); @@ -104,6 +109,7 @@ 05C1D6062DB53A4900508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 05CFFC422DCC83290051129F /* DesignSystem.framework */, 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */, 05C1D6072DB53A4900508FFD /* Data.framework */, 05C1D6082DB53A4900508FFD /* Domain.framework */, diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift index 324f2183..8319bb65 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterReactor.swift @@ -110,7 +110,7 @@ final class PopUpStoreRegisterReactor: Reactor { var lon: String = "" var description: String = "" var category: String = "" - var categoryId: Int64 = 0 + var categoryId: Int = 0 var markerTitle: String = "마커 제목" var markerSnippet: String = "마커 설명" @@ -277,7 +277,7 @@ final class PopUpStoreRegisterReactor: Reactor { case let .setCategory(category): newState.category = category - newState.categoryId = Int64(getCategoryId(from: category)) + newState.categoryId = getCategoryId(from: category) case let .setMarkerTitle(title): newState.markerTitle = title diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift index dd6d5db1..dba5b43b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapReactor.swift @@ -46,7 +46,7 @@ final class MapReactor: Reactor { case setSelectedStore(MapPopUpStore) // 선택된 스토어 상태 case setViewportStores([MapPopUpStore]) case setError(Error?) - case setCategoryMapping([String: Int64]) + case setCategoryMapping([String: Int]) } struct State { @@ -64,7 +64,7 @@ final class MapReactor: Reactor { var selectedStore: MapPopUpStore? = nil // 선택된 스토어 var viewportStores: [MapPopUpStore] = [] var error: Error? = nil - var categoryMapping: [String: Int64] = [:] + var categoryMapping: [String: Int] = [:] } let initialState: State @@ -96,7 +96,7 @@ final class MapReactor: Reactor { case .fetchCategories: return mapUseCase.fetchCategories() .map { categories in - let mapping = categories.reduce(into: [String: Int64]()) { dict, category in + let mapping = categories.reduce(into: [String: Int]()) { dict, category in dict[category.category] = category.categoryId } return .setCategoryMapping(mapping) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index 625fa98d..8aab9568 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -24,7 +24,7 @@ final class CategoryEditModalReactor: Reactor { struct State { var sections: [any Sectionable] = [] - var originSelectedID: [Int64] + var originSelectedID: [Int] var saveButtonIsEnable: Bool = false } @@ -32,7 +32,7 @@ final class CategoryEditModalReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private let originSelectedID: [Int64] + private let originSelectedID: [Int] lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in @@ -54,7 +54,7 @@ final class CategoryEditModalReactor: Reactor { // MARK: - init init( - selectedID: [Int64], + selectedID: [Int], userAPIUseCase: UserAPIUseCase, signUpAPIUseCase: SignUpAPIUseCase ) { @@ -88,9 +88,9 @@ final class CategoryEditModalReactor: Reactor { } return Observable.just(.loadView) case .saveButtonTapped(let controller): - var addList: [Int64] = [] - var keepList: [Int64] = [] - var deleteList: [Int64] = [] + var addList: [Int] = [] + var keepList: [Int] = [] + var deleteList: [Int] = [] let currentArray = tagSection.inputDataList.filter { $0.isSelected == true }.compactMap { $0.id } for index in currentArray { if originSelectedID.contains(index) { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift index 14f3a94a..fa83cf82 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift @@ -19,7 +19,7 @@ final class SearchReactor: Reactor { case cellTapped(indexPath: IndexPath, controller: BaseViewController) case sortedButtonTapped(controller: BaseViewController) case changeSortedFilterIndex(filterIndex: Int, sortedIndex: Int) - case changeCategory(categoryList: [Int64], categoryTitleList: [String?]) + case changeCategory(categoryList: [Int], categoryTitleList: [String?]) case categoryDelteButtonTapped(indexPath: IndexPath) case resetCategory case loadNextPage diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift index 0f6c98fd..5156ec89 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift @@ -71,7 +71,7 @@ private extension CancelableTagSectionCell { extension CancelableTagSectionCell: Inputable { struct Input { var title: String? - var id: Int64? = nil + var id: Int? = nil var isSelected: Bool = false var isCancelAble: Bool = true } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift index 91646a85..911d59c5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift @@ -27,7 +27,7 @@ final class SearchCategoryReactor: Reactor { struct State { var sections: [any Sectionable] = [] - var categoryIDList: [Int64] = [] + var categoryIDList: [Int] = [] var categoryTitleList: [String?] = [] var saveButtonIsEnable: Bool = false var isSave: Bool = false @@ -38,7 +38,7 @@ final class SearchCategoryReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - var originCategoryList: [Int64] + var originCategoryList: [Int] private let signUpAPIUseCase: SignUpAPIUseCase private var tagSection = TagSection(inputDataList: []) lazy var compositionalLayout: UICollectionViewCompositionalLayout = { @@ -57,7 +57,7 @@ final class SearchCategoryReactor: Reactor { // MARK: - init init( - originCategoryList: [Int64], + originCategoryList: [Int], signUpAPIUseCase: SignUpAPIUseCase ) { self.initialState = State() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index 0838183d..d81a5b60 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -20,7 +20,7 @@ final class SignUpMainReactor: Reactor { case step4SkipButtonTapped(controller: BaseTabmanController) case changeTerms(isMarketingAgree: Bool) case changeNickName(nickName: String?) - case changeCategory(categorys: [Int64], categoryTitles: [String], categoryIDList: [Int64]) + case changeCategory(categorys: [Int], categoryTitles: [String], categoryIDList: [Int]) case changeGender(gender: String?) case changeAge(age: Int?) } @@ -34,7 +34,7 @@ final class SignUpMainReactor: Reactor { case moveToCompleteScene(controller: BaseTabmanController) case setTerms(isMarketingAgree: Bool) case setNickName(nickName: String?) - case setCategory(categorys: [Int64], categoryTitles: [String], categoryIDList: [Int64]) + case setCategory(categorys: [Int], categoryTitles: [String], categoryIDList: [Int]) case setGender(gender: String?) case setAge(age: Int?) } @@ -43,10 +43,10 @@ final class SignUpMainReactor: Reactor { var currentIndex: Int = 0 var isMarketingAgree: Bool = false var nickName: String? - var categorys: [Int64] = [] + var categorys: [Int] = [] var categoryTitles: [String] = [] var gender: String? = "선택안함" - var categoryIDList: [Int64] = [] + var categoryIDList: [Int] = [] var age: Int? } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 6f8b11e1..906c8ee7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -21,9 +21,9 @@ final class SignUpStep3Reactor: Reactor { struct State { var sections: [any Sectionable] = [] - var selectedCategory: [Int64] = [] + var selectedCategory: [Int] = [] var selectedCategoryTitle: [String] = [] - var categoryIDList: [Int64] = [] + var categoryIDList: [Int] = [] } // MARK: - properties @@ -31,7 +31,7 @@ final class SignUpStep3Reactor: Reactor { var initialState: State var disposeBag = DisposeBag() private let signUpAPIUseCase: SignUpAPIUseCase - private var cetegoryIDList: [Int64] = [] + private var cetegoryIDList: [Int] = [] lazy var compositionalLayout: UICollectionViewCompositionalLayout = { UICollectionViewCompositionalLayout { [weak self] section, env in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift index ea33ac45..e4c1f754 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/View/TagSection/TagSectionCell.swift @@ -44,7 +44,7 @@ extension TagSectionCell: Inputable { struct Input { var title: String? var isSelected: Bool - var id: Int64? + var id: Int? } func injection(with input: Input) { From 2d981bacb7cad57df70f6f4d74391d3da9b7a156 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 15:19:08 +0900 Subject: [PATCH 317/393] =?UTF-8?q?chore/#131:=20=ED=8F=B0=ED=8A=B8?= =?UTF-8?q?=EA=B0=80=20=EB=B9=A0=EC=A0=B8=EC=9E=88=EB=8D=98=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DesignSystem/Font/GothicA1-Bold.ttf | Bin 0 -> 2285888 bytes .../DesignSystem/Font/GothicA1-Light.ttf | Bin 0 -> 2296968 bytes .../DesignSystem/Font/GothicA1-Medium.ttf | Bin 0 -> 2287684 bytes .../DesignSystem/Font/GothicA1-Regular.ttf | Bin 0 -> 2294192 bytes .../DesignSystem/Font/Poppins-Bold.ttf | Bin 0 -> 153944 bytes .../DesignSystem/Font/Poppins-Light.ttf | Bin 0 -> 159892 bytes .../DesignSystem/Font/Poppins-Medium.ttf | Bin 0 -> 156520 bytes .../DesignSystem/Font/Poppins-Regular.ttf | Bin 0 -> 158240 bytes 8 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Bold.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Light.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Medium.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Regular.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Bold.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Light.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Medium.ttf create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Regular.ttf diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Bold.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Bold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..769db39bb919760528f3f6808f1fc6033ac8b6b1 GIT binary patch literal 2285888 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


3RIaI9 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

+}(?FKJhEyl(bXZCyYfQ78r!@<`R_C#U7C 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

  • w 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

    >Me8JZd=GV3O%F$@4#eD2Bx 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 k_$5Un;}OCHAs3%IiC)=CT>p~=CHS!p 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 z

    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

    Bj|SO)|w^TE%+5gmq%JF9WST)$)_ zrU#8gUGQ8;iRtq|XGi$>@Wga0w71^DjmlR9Tz=bNY1%AMWZ>V1!CJzopp<$^G%Bd- z^Sk#3ir-+ZO{BY51~>4PgO^f|i-oymX%oK`d4>_9U;$M|MgCGse4OR=Zm8-@f{8ps ze~0#OBG0hlvfhPX0Yj%4688;MA%7a&bRx+`0T#`&#K;KaOW;6_@$(x;_w6kl#U&1Y z6yN3Wa>J!*6Te_h#G~k)*@);tqJIN{(s~r(-y}EiCInmfuu-l1%+sV|+|sluTdNi0 z044A-C3=i9@ai_7igC|^+%?QcK7xW{vXF~pj}DX|0YNK`)3yKzf*}DAl!wxeZxTv7 zTa>oa4#!IdNu{0DFeEQBPlvv5Z&f2xQXw3v4NO0gDUVtdWq7F(_2MO_h;YM;2$Vsl z97}~c%Gs7PFNYS4=WG&7R1_>s&IQx`RoUBdILp< zg+%c?M9z!fNu}oaLzIkqb$g!k3~}E^`1x}B>p>jF?feB<$ISpr`BAL@rg_XB)>1Jr zduU{|H&PoM#asOn{*~{dtMBc*KG$r__?tYm!;IkIS;U>+0>RceIK})69$wX7)@#Nm z-1JM`4v0d2$kBh7JcomRI@`hmDyHBNLv>1YIX7q0H9|W-8XU=v+j7U}*r)*(OL01?`Q9mavA2umU%slFc zAqmwlL8t}7GB+eUE7yTVL+?B~t|8gW$z5m=DCBS*$X#Lb3~+h5I&TAWkL{N^1PuaN4eDn>uoP8A3W8%# z`7%pXl<6hU&?hIzeK@LB0>M6Y@F=95aL8{OEJ>TZ1qo`EkPXR`|Eo zY}_AeNbVSi@teB#mtvdZvROxfq4QXJj)La7UZLBNPyi?#3X=x5JmESK34==a_Z;f9hg%B1G(!K zkrBskRFpa}7xW$XHF2@IV2DY8jC@Z~q^lOKr938Ck6>$RVBj>0NvK0*I~((d9>w3g zW#zoJwEim7Sx8*;+YfY>`?y z(7?@GIj%#n46(tjmr9YmnhJ;A11D4INo?hC?82ec%CRbO!Y*4YM+TLijF-@(f{G}@ zs#gi)Uqv^JvSb0WOM%wxwSb){Lnau=Ag7PjB{WZ;LrlUuZ!Q;#qVoX zY?3v^pBv%N)(Nq-@#iM^vpD(rE;Axpfh&9aj0lW}#=q3G__y;Q+Iw)iGEtVBZ7Ds< z{7ae*9{TJOredwX`s%B_`X4g%H*;LH_1H`LY-Oe%VjaMAqKT4N3na0`6*tSQvoft> zHNvVg{9pY~;?e&z>%i;VMYN-6C8y4@h?6ZQ&-oJnUVco@4&AHyL>UwOprdNEb24cO1ahLHd z5vHS!?2*yfI~cwlRMvb*!NR3X%O2mcW5<3q9mZ@I9UVq!j1D8c`J~_^vqwZ@?_xq} z(2-^na~CdSI`;V2Uw^${Q`yJ(&wefm_1NEx`Px1vh_wv|F_%%peH+=V|I&0kG_$cC zZ!3nJXgOpA7pcRZ-Mfo*<#;5QrX;YTN^`VH36YM#8ctyJCRrQVa}U&Pz&hS-7XE*0 z6^~=OYRK`HgGRnGm+4viJ$v>X(jPVPArNj5&lJPOqse?zSi?u_j_tp2g~eWIT{v*j zt1O9m9x5(ArZ3Xb7zhkKPmj|_>f)fe5{NdF(d)*KnC8mPUS!c{TTbf#$|7cAT@D;L zP^Ld(1fELs`v)qG(MAQtE5%)#&AYy;Ovt_ZvpZe&L-A$h=Pt46bFC*1;6}ge!2<`% zbziGudGi-D#HzwoF%auxXf1_lZaa{7&YrTm>Hp-O|D73Wg6PEV5y<9aCs~i9&4&(O z_#&+1lRJ0rJfOdsp}USTRQ1?WeWoIV-1H#~x1Gt;%_@K9@S+{TVT?K3vS9SeMJ$&9 zRGO=*9-FF8jZINQEShMkVBH@*6Y_3(1q(jfVo1TWOPPj8Aw@cfcRJOD#M7k2C6jnZ zAl?K<)9oIi19|KO-1hMn1qBP2GXrbCo7^^keUDye(P#*zo(~|YGW_;5BX+lroA4rN z;F0E8h0ngg)U56H?c4VyUhSp5nBfZRIA7a4K1~a;_Ef%){ERr^Rfo~xHC80L>M8ve ztwbckt)m&DT>==wMN_2FVWcUNQ&<2O?UAsK`|u1y{DoGN1}=J?rQ+F<;^L#G3;Z=x zn2#CiUU4ZT)+!i%4Qp_lSof;2KO_QKd-5y`JK1u`NVsVO>$rE%o)SKUp2v;7<8)%& zxCPqEV}IddO}ob=rn)%)F7mMZz524^EDNoGIEe6dqBy2Kn%JGxEyeBV4|-oK6vn`X@@#yyT&ETjU~jW!_^ec7eh-=%P###6Cu7j1*}j4-Tu8sfGgtf zG%LmjQG8wez6E~Y!k$_4kqabNo1tTQlEPiE@#C1tk78XT>an26=#ZEQh(&Q>3VVW! z_1vQb3cL&8E%J1$iTxJ73_P)O(f<8MbY(_rKm+D6NNJ4WspWPa&C|m|RT0L$-mO2x zWbDFh81L79$Pj7viJDirTlmjgvbWyvL&sbQi= zll#USqY_B89e96m7Gt+*%%ShrUNFW~SK3S(yz+U(g7VVR(h5tpC_@{rC3pM=#8S(? z;bJ+wJ6?+yEy7N<9yIEeITSciRmHM3E*jGqBHUPXbX;gWx#LYpA@gK69}SVV;u+)c zQ>_M#d1W3#+o$&L+jlr=TYNO;FhHc(==e}6!g#0lMBc#PM&{6=?P2(-h|{gpXL4~m zadE_eDbkGxO!3js&{xpV6xpfU?vg`Wzn$E`e+=yVqg}E%WVn=tz3u<*o%NPak>H>yoji{kT|+aA0nbiV12kq)>585 zHT6JH-A~Gnv+jqR4H?EudF`m?r5E}n{MygOP>;QD%-8fWLbSbzDhgSzN9Pb8ICFtT zpJ_3Xsd5Yu>n&(ZC55<;1ZMFLFnB_=%da} z;9`9UpqgHWtcuv}dB5zAUA#kaU^xNQ%cx9FaiHNsvgQmpDL|MssUpHehcKSgy&e^@ zs#X6b++F`oZ{4*_Dg-UoWhi;IL@XjfOXK4f=_?DcU1RIkouvO?A|#_ zNs1Nze1f}V@?93f-)+8&!d?Y`Y3dOwh1-HCk#a(!>5rRL79T_#CLJ5kQZ4ASnEjDNK0vgqS#E&wf9p1)g?0CUTCb4^?h{2;vm53<|9TedW~pZ z1On(Q^NhdtcLksKufR6?61G8PJpCOEY=`$5)ygfB$ zcObwf)*{{tz&jj%%rtzL>k2gLRYSHai?p>H2XU&z<+X}+ufFR}WB*Wm7g3bsh(QCS z7ZhbMXpU9m@G4AECNdk_(BY8B9wFK!=gc2(F=PaH$(($rKAohlz*|{vtWcdCn(rV>R%$V;LnQ?^`E`6&1DH_ow>sXzBW&)dJhRR2qk;itZ?K|luGVp-!ud-|^j0*Ojp41tf)IP*mEpHA0D94C38- zWJKd#3};#Z45;d2Akng@L4!m~r~4&#F@!uihf(TcKrmI+#c-vU=0ZA&HBZCMMs2+J z@lovzX)eGMvMv9H@NXq4Q~qmxCfte|(m(Sk-XyaQIH2Q469KG5<)%ns=zkN*%9v+> z-?zly7jtRxdt3aO{?6n0{y6@9`a4e)-vvx=mKZf7`r&C1#P7D0KJ5LAw^T+LdA`TrWVctb8CYp!7&3rkyD?-6?q*u7x8+Wg@j!0Nu001> zm)$MKn#R7&y7uhOl7m7{q^9c2@Qj10PJBJpDHZ>M@Z}JGj!k%{b}A?K6tOOQT8=f% zc#dyMinuBCdZxKfoSXWjN_A4LK8Z~!#-=Qk>Z#6mQ-Y*NK)9zpTzr`M9Befqaoj5A z8`gtm_6R$ZmZrzs())CEV(KZ175odEvL6m*nS^XR`KA;TpY}-WNlC*#WIe-svb>Op z^XcijQj7{V)`_d9I;K<}Q>l(&Q}*DbER~uhoWyn%LHMAu){~Qle8hT0gs{FL5f{?a z^?3bwyQ)r9z1ZEc{0qWU(Jfz^fZ^mPVl!+<=iMy^W)5A$x>Xre8l90@7jf6G2L6Rj zImB&8tQ3$swLv{{u$0LUv>co>>>cKhm@K15*lE~~s~APsyRO|p5bSE_tu|HQJqRgu&TYL(OlDNuVG`>z^E#v zy4s&$W7coqCFXTwma-?2*Cp_{Om`OZOp@!U>S?cP`4=|k2mqy7(!>uZw=KtaBQBv} zZCv$WO?wf%UNN;nUL&S9+d+(4DmxnA3b!x-9b=N-j)XwH(jbm4^PVhFC?mdsCrY2rZ(_ZRb zN9CsfQP01yDf_W0Gi=wy>EVw<`wuZsflS^or$56{0CV@AM^jSt`}A>pd2Vx1p`b5p%2$~FFhZK7;v`}GPHVP1<`vdO zj98DSrW$u?V|Qxm#JI-okKrePgn@zkNnWciy0ZFuQf5-)yQ_^cE!f-ii_KY zP1%W)vQ&0a3cenBKz%P-U3wgCGbL&8CQ1g36<)ET780R{3Bqak2_Piz4s7oz#+s5 zL*8crq76W1s-s7+j06myP%24&2>(IlXTU(4OTs&SWcdjuZA6@GGd(4LCyR{?WrM?` zuV!W{_8VgN>VbpCm&EfgY|KZM>ucw*dd^}8Ht*cWI_+vP);Rhl_LP)sq@<)COo%^V zs1sXHc_cpmh*Eh3g6~J8;tbZvSp*kz3B(9vS28bKE`e8|6~#sp{HQ{4RH-})!S`Zh z%yTx7_mQ{!2M$x)LF+=xxV0Re%IO(?u6J+CZbXJ!1V`}{8OF!&hu~`__Onkt-cBW*SC(|-7Hcl%=nJIIfydGY-zO6pI~oFxvgw5ThZ@tOb)b7w3WJl z&wTJNAYEH%xzUrc1Nl$~l@kpF2vCb7V`QR252Pg-TTc;n#w5 zq>-i={Rs}n2C8puZ#LRE#p@CPUp{h{m%x&&(g*yZPgy9E`f=a;%1{0ZmSgQFM}1`i%MYM>2%-e z_22PpMDyRoar{mWR=)ieFQ1P}7`2Q&VJn~O?@}oic?DuWp_7EE5_e!!!sw;dgx6J| z>CjR~QBQqQ&A)8dM10)q&tN}9QpL2Gsn1l~82wSR;)v-n0GdShRI5*6V^$-6*CJpO z$@0!^Kg3*qlySy9PKy?E`Q&22S_vH10;VOxS`^pku?T5XoF3@hvTHxTCbcAC7BDa8AE5x8)e<@W98V)vwI5;8=hx#p_mIh`0 z2{xvr=n(VT-(rG!2wE>}8>2g!q&i`#qpYX7sNrASzI=p;_D^$SC=KfHH3GLWFIJ28 z`FyU_+fU4DoEWW*Q10|LX1x_i9sOc8iTR|8ThNYhFK!TtnxWc}AtrxH#DHRTX$fTq5qW@VX~YLBPY>eDLKX`Bi% zdKG7v{24aJ8o=Ei*&a>A#yl2ZPfZ(xFY9$-9!`ZNSp&7(Y(-LuF}^qfy%x%23I(-W zKzI>k%y-i0^)~SJ@IIyj!jPkMn^*hN`DJu2ddZsg7e)zCv5|NLve!Q*Vl`dv@^JYTdJF zQBhSS18LG+*6=TEN;!tC&H3}N%xxXCV_#ui;g-Y>S~NAODw66b+^TKNer(K(()77M zf!&oFMkiaOhS5H}XHr#$)?(goO|8ZF!im`f+p^R~zjyjf(AG>k^dr{O)=Y{_Fm?}7 z@Z%{B6f;4w=h})`JGFfgjc&_{iQ`vsBDU0XU6G_Zo>V8^O>;r5zM#=uz-ifx(~`x8 zIgOXTb0%02W;F!0Z`M&7+pvvih)t)(_g&9AN*Q8GijLLphtXjD>Lj=&vZiLUa07!I znqqfKd2lBV;d4$SbKCAvQv-s#nF<7VL|DY^7aHpDbFSg1T+TYEV>AlCf^|@b?+r0B z5}fAbR1F9U=+UfB$M#i!=3@-X_Z|=f-=XU`do49Mv?4W?1_phT#pmK+1PTVBU}Hq^ zR-6OqoQLMNs=E!m9k#(XHzzGk_d}N9yDZ9W~Nj;)ab}(_BcS(mPd% z^!o3yDQnTBt&s*9I{Bv9*ehd}iDQ;HdqqnmjdXOL8i=7`C24#DK7u$3(aASOB5Bx8 zia4fCb2&|W@s5)sHgAiA6rI7iRJotKx}$&7z?~ z<6f&~brW%ou3}@{t8tz7q6hIACJu4@6P%cT)4**ty1E~9{7d#2c*ra9OcOoh*+kYt z)=}omno1+fy2#q|p?=m0rFTw2Y$KEN|NZunW_KCg*o2Qy+$57>mf2r0;qNcLn`KYo zpEdDl9I{FF6y^f9tcZC`c3jp{b`76enK5l-uJ}zWe7fNif{zBDG<*i)GXbA@_^iNZ zJwBh}vj?C0nVV&vwx8?i%YUXPex|4OXL{midg5n#;%9o|XL{midg5n#;%9o|XL{mi zdg5n#;%9o|XL{miV)|gkZ%0hBpulF2A=h}UdvIVt^JY!!bnMW%N7Lqk0l`7tJ9q3* zXA7q4hn%cGb>QMx*qGBoIU6Oso~>5oWoa~77N5?YeB_-w`{;k_ZMfdYUG2egd8?n zax&GLY>RK_PTt;~I{WH>N{4#fRE=3~d_2^%nA8&-5)$09b?cTbTakKpK|PC^u}VF% zU+jH*p2Eqd!KaTy{03gtZ;^fOjD2p&{ojy%XD+a)^Yy0>d44sUd`>7~6NHy@HHto2 zT3vRMZTdSZUCMS>d9Yrwv7=&Q76td{5gZ)cBdA5IRxR*>3RYFViGJeT zDizciFnbuVb;nMLH{|)3+30FFBtxUkq8lQY>1Ld zM@UEyenYqcTV1)Tc3(E8ntGc5Pd7vy5zOF5nD1SD&F|cn|JfUCh8^~lJsj*@AsDmA+@et;@ z26Ou^d4o+kBOGSqgjaJk%G@lCHai(kUheZ$lJ2-kc{tfE#)E}MMGcCGm=Wk7fM0?B z&04f*)~q@9znr^x@2dT;F<^EuU?=G9)ng5&_Fb}!jXoxvWW$8jSt><#rb?ZeB!8-- z-1{lB9=a(_;kt>8B;Al^pdSCGEm|~f+5);c1l_#Ir<97}m-l_#4X=BPj;kzIw+H9n zyCDkhW{rZoAqwus{}Y1$*5H$d&p>=8;4=@O75J>j=Tm(4;8Q>IKKy%7_l_NoRGeX< zry7ngn7^78RtU$W@HaP0t<6l9cQTn0b;bJleP;LQM$9WBB0nr_vcIpNpP#RvZ_{So zx?$&wke9r`x>enZ``Gz=%uA|v-l-z1L)iPU(+vxUE?LPYk>?g21NL46tJLW!x%?@M z{-{w|lI#wv)FU!-U_`|90Dph!X3r?c6N;-Mp}B z|GPf8liw95zpM4+cg4x?iu2qR`CnI@{H{3pU2*ce;^cS5$?uAj-xVjnD^7k_ocyl$ zaq)j(f*yCYgd7+_WedKZ?s|e%!xEL`JNr~z0XdbW#MPsrwx8-Ih%L}Y8gwOng>hSNj^MS>g;RyF3oTy z*B$v@dR*M7*x2WSd(eM+2Jv*9CmW?u&&$>FJ)isV)!z4ZEBJeavqP-vBO?Gl^A!)eNo=xx7f@JLNO~8R^(}veX_N>oMgWJDY}bE+EeN7 z3J<1@i5V6ZH7_VIFsOS_aA5P6Et|`+`LAK~a~QqwfBzUE@s1C|;itRz?pm`GNV9KPJA zQ)eGDU5spZWn-2c7dL`mjNss);2zyuv}`HCx(A@1#e8Z*O(1N?K)Cb-C5QFr3CWTwZn3&dRC@V zoss1IR0p}yWH^Y&woUF)9;{nom_8+(*ZGV$y z*#E=u8;f{Pqhy%w<6_(+Zun=r7~nG;!?V4(8I$rCyuwD)%@`tvyID$ACchcp9ZjbF z+PGatx5!4U6W7fIU%*;EetteS-5i8&UXWGV?nYIH{`DEN>FeQQ& zji%)HvPma|<2(yjDRQ$_8q_jSNivy@ zJJc~>0@Q89+JuGWhJ=hEP#3VSyh+ohO$gTAhm*fT_IKOn*Q9yB;}xVdub5t}N1E4f z!8$gbd;-rVl)2ezEz-OWJ|?s2v-rqOO82lv%snI|qeqVdZ%ztrJk>GYzkHo(Zq#4g_qo5Z`Im(KY`XA5AFZlyw%(AFlA4^HYN#-& zj-*IlR+S~Y$9b@DPAQlf=_u5vVVB^)Br!ffB=LzK$WoVpH?obJ(ab#ZF|j`{~c2!(q%h#*vcdSh7$^wyvhns z!pd{9ELnNr+*GkPTapiB)SAUT+C$p5e^}Vm z>I#4opn~0%%WJRX^8m%vt0u_#o%3(Svd>>(@s}IU96JB6Y|dFQVNrj}(enCRc1qGy zzLuoKz1p~)#`;KmJBEk%2@Rd#Cr93k?5b-4d&Zne;kF z^=$)k-oSE?g5e8ZVx!80GiS`e2&K)G!%S#gobAI>^Z_4 zzr5rq%r$A;n3Aq-l`CpLBm1XKF*VWW?_LRtKEL9G0FN2{7OZ8{Y+=8G=wn$aiC~YJ zKUYR=QB$_tm~Z?@>^Z)$@trzh+e_ey7s>u*+xDvaV7KV=VcrMFKu62U2?O#NJ{%a( z*z)S=s46qryJH9M1Y^=y03mc7ga{4I?%8t`SBsC2cax^g8k1s*V5pYzCm(8D#2rn4 zcjtRcE}{e&MCeJ6sr{Z^#ikq=F^DWR$3`<#jR2;NNuS_>jd=IaM(iV|mceOXNh1bCKr_U_$(2gDb6Y^BEux4GFX7PAr5V+VQ+HF5D4 z6_EP8y*qdD(KD7~`o|zQZDTfCshp@#yc~*OAz@)+2Sl5;(9nKpXz6V&L{C1*9+f|v z>!2Lt5C`(yW(|D)O;&hbI3#v^Df4)@mrvI&a`0b$b-!dLeA~}m%yr#Tkhh~Y@C#YMs!Nj5S-((XA;uh-*L>Wi31kV5yb>&9Ip(OX% zMl6_9#;5t?mtUaNEJ69-Dztj#-e&0P{ZH4(x!Sw#$=z2q5L5qM$VqDNdXDSzXxag z$HY93&U=Ya?kD!%uY#}1WjdQ~YOaobt`6@ku0Jae${AC2bwpOPyz^6Xx;nb+sk-yo zZeTzqfr1tty%6+35TGQ6Ag7>->Z!48U zhK>KZ@9+K^HVCzRi|3;a#t(&R#u7`Xnre~=wM4(M>M&~NycZl@mou&X#$wHUEl46c ztgTHY_Y(`;V+reFs>BgcOQfC~2(~=dXma2A2&AbIT2C^ z8jl5h^fyxU7qWf~W0K_LY_6y1&S#Q(5@TaQR)01Ky&RmZj^4oLylu7!>RG}cRTR}z zW7+I+d&EZn`g8g$evK81b$eT@O)|8DUJi@ytVMGojUoX>bo9`us5#vO1G@)y4?-j$ zH3*7fbbO6vdk}G2ee`bfEa&zxnz9`EQ=R4dE6Lh383Yg{#Ksn&&x422zzC?V79Uu) zwYWMt>`9w?YOanwt_}qSfkzvS9=c@dZ#q3J;Pg^Zpf52fioo{rpr8P3ugREC;^*t{ zUlkOrLp3v3_J20rRF#>3KZd?XKKsKA*#21XhS%E=uf5Gx1KP!W3?36{7wa;W3N+zx z+6B1o#-uM${nt>LxoJoU%FL+#%dHhRPP=#mm!=PYQc?6bHOUPwP4%HB_n+LX<-O<) zCk$QklC2j#2Q3I@5Bh%g)-9SyGbY(fJ90=tVd$T%FZ7Tmq+xo3@f?4q6xm_pY5Xv4{U(NQ?E zD*2=-WIXvqPg^H%%Ms$Xx=GYZ(27B91-n(sJ~_3rtnzhGD@RtlJvAHxyKo3jpe`MG z&TaO<6)&@iwz{;UPo_?XL9o0o-PKpiYUP-J8RV{O%*Lox;1IkL8WvVHMUis|YUU_E zz&Xn9b8=r^!p71#0cn^Sag+HNQt!?f-1K*vqfCQ(^KPG`Ts5ed1~)BXDb+4e4M1QQ zfPkvhP!I5%>T{GO3XV8O+3;g;!?iq`5}U)-BhFFo*|XZ1>rxI7B-N2ICmPt%-;j7#=>IhLEDQ$gf#*8bnIu zn(Y{K{S1Exqz2)%W3_WPA6HX<$`D{^6Ym^X0|45GZiwo9lKMotd%Ooz#l!$ao6A8o z4B6r^4UeD8D_7Tku4Y`#1gOTI#pnt?cc8(vyv0k|2yx8T8tW8a3y5h{?Ubu7C2LM) zxIsPon3#g7sAswZL8BRHRVdm)+@8fO&}P7@P_!DPhR^rCKR`fwt=hG1-KrhEo>0s)m;N;x`p+*j-D@X)6#@DPpH$~QFL%X@Z0uP) zt}s>{BuMXDSga$P^|!K&KlXRiG-ksTit%xAoB-W3G_)tk&n-cMmK3zK=7B4FM`lN? za~_|6olT&b%}`IS`1}ZfGl`$B`;lHyq6e<3W&ZTQRTVJvHSW(+mQ!_qYEsL4O)Yny zzs%w;yUiWEVilWy36lgT3ol}lVBajgJ}1eqYZo7%E?xcf|CeF>F5ewD#MV<2(vzO) zsd}P^z?T=;{PuO16C|!W5|RdK&y5~#HJ^5GD(%g+zpM{ zbhVnCOjtyCcvwVaH7BzQL!5K@^UF2Kza5i*iR#RoIKV`=ax{39M)W3v$K={?QuP=4 z6J*tgI9r+M(i3E_Lp`b0E>I1)YzJ_;x_YX0aomnxYJVVbnOsa(T-<2uBYgxe(+hF@ z2{Q5a{C7PzHN4t?_rzs&?Nn>y5IdkG_L&yX_gxepZQujY`J)X%J*4ORh~6Yl;nvq& z9sA;D(iHBwc`q*`Sd-p60X2}nOvcM*G=dBL zrUlB*u%P&C<@#aq3T|E-_d+d{IQ>4lBv4OrtE*64>pHg;R%TCGX9&ap3qXgyO4 zo6PI5#yP&mKkT>0xmvSs9b`WnT(WDy9)W zF|FroY{D8%AHW)FmLaURe9YJQq_!V(x91w956Gcm$->)-f5RGO(1%KP#C~g()f#c@ z8}^Z))3f%%Y5hE`aT9Cg%gXJy#?4yeZe3-tU>oIlDTMq>@KC2{t==OtX{9}8N3&rNS(jT=~FI3@tu&6eEY_k54gL(gJ_&-pz+Q~43} zam{{foT>Hvts>eh9SainB`k*=A3_d4*;)Io@uAh0=$Tw7O<0UIZbFWJvLEfY#?4ye zZ(U+rg*Y9H6uvUOhBYcG-@#rjvBMg8ho1aLMXjZ`_ORnZ=cLP*<8!b=N#*;sLuBXe zu)>`l%FMb8`?)r5V2ym)Is2_~qt=957uhx;*31HhqJ$SA$C=8%!jc@f-x_CX&A(N` z4hg;{<|&jJp2HgAsru4>Yg9W`^@Sg`6R<`pZRzy#CBZ1Z^!WyGxhwZmUQLS~iK4#wtP4w(Z;f#JR z)+nyrP&)!ouG&rG#kGds`kwtwFs864gtPiNSmRRV+ptyJ?YG9IT2pVGW?u{8$*iSt zMK=R$oT_{mr^=3Z{8X(axAxJ=wXjCQciKr<;{&YWFI#N~Ii!>O0pvKvb_ijXpMO@+ z$t{L&Qrh!|7uOnl>k9i!P$b>Dr5u43u3&|H*#`T`aK-AylLSFI5-Xg>3WH>?*l&f? z@EWD8Snx~w?mI<)tZ)h|jFhdl-wLN{y#Nn>Oz4vI>#vFeu7{1ad$I<0SV6KN#kB_9 z`UiVQNYb<3!dlBltZ^N;yB{6~vD?kNUaJru<8vWK&n&{biSJ{Ljab89cF2BfQ2r-b z!gj)W{cOI*2Z#$V+j9*mH%p+{(@2pB$>C?uxNsxS$C3Z(S(Nal@eSJ=mmYu|dKMrY zNLYq7#AwHk@olwe=XN>HJPZ|t|m)vB`_BJ480 zj5S0o$&Na&h9ytD*@SYacwSbuxQZ8oc`v&B@RMLcG|ovLOLI|(NY^Z8CuytB`KYtT;d zxXnVhTt~6tRqRzhYiz$YY_~(lGKDuSpJ0u1998(verwonhmM5_JB+Vljf+@guszWc z8|tQGxPb<;x@DD{fKb}e)7lV9HS-k?B`m=jH?c-Pd){&z0;9Hh!Sg_+N61dvPaih) zP{p1SD)b8^osYDqC9$E0I%Y*dMV%kCpB&Y6-deb%BNUW8R8RI7`>jz@tDm*nVG{nD z_zu<(s~xxOw??&U2dV_T+QHYbrySMRAUWQJ9Ho4(+SpHy(pveqeqkGg6g}%NtVsG? z+N*p#wqm!a{Th$jd1WX~SeN*bRUeGma}8Vd$sl}~NEI$_6PcU+)~IF^Q-lqPA8)u>C@JJsXgfB`i-M)MF$3rvHPIb7Lmk zo{790a;n*g++1OiX$)WU6Rc@`Fmi6pU^_Apts!61u;m2>!hmG*M;j}t5*z%$*8DmH zast`Yu&Mp~3)9VoShE~wNF}@VVAd?pWS?Z9PWy?V(J^&mqOeKZ4{M5*+6T1e%}ll} z19d4>Tl7q0u?U~&^0B5^xxM^g4_GjKI7td{=PA3$pGqL7*?CnU*QhFeF<$NYP zk}1|)bu8X&7Pe^nU`?^Id;Gzyc`lQcWukUjCV1*uXQfg&uIz<1$@ayw&JSkIl1%n# z1}Z|I3MxGtiTk@& znQ?0uJ1KP3vBZ=V;XU0TtjTjx=K5gPB-kO55}4|;V<)9n+LPz%?$Q= z2CAXJQnf4}d;2oq+Y`Y5N6J2XFl(O3VilR#+X~?+3u_P&A^Z@Z06D3483H8wf#|a| z!{)P1EGs=-SY@CpAk{MS!M1rIYvSB~nE{*{*sF%BSK9$IQAIO@enQU{$ zt!r$vpfpKq_LlV7Pj>Xdta&4oeUWLcRCk5FJ*pymd!F=(%EJSZ6Mp4D=B*>_fDm9} z?c?Kx)5;XANtNZ1veOS{&C{9eSmv$0R3$PoU+D9Yn)G>!uleYw5z&Jy^CUs zJMM+v!E`WSz<>%8&A4FSxWviLy|$BkAKx^`iF-Gz|2^Ng5<)Jq6Uppv@<02)&vTPG zGiT1sJMYXn-*eUIO2Th*smjVq+y1;3$b1trJ5Vpqh|D+j)LYe8ULdhAmnuh$KS;;; z%aGZ_vdxUl%X;d=>MJLx4{ffw3YK>&=3FX%*cFIPo2?JEZAjGmdl)J>B4XUH_p zluYdBv-)us{i>d1Kbu&iGovT=^Vii^UL*6FI?6pU(e{3NA#BS`(mgjbGT$;#Z&sr# z|E4XtloDb;|CB-Y^Hs!zXew@IWWHKWJzag}6Y6POQ7(0_)oNQ(LOMpIQ*Xp8CNttg zf74Ikp}a{oFX-z=44LPD+b_O22<0W|Cy>+u%F}n5SVJzD;o<* zEy(COLpgiWe&$gX*v|uW&Sv=78S>jnJXkK30lyv2A%4qD3jA$m_Vai(^}PPdTh#Nm zm@+E4sK|DX&Y!O%4#nX0oEbSQuUAt?^jAKmj@U|aCvlALLgtLsyzfrtQcd7B89ssG zQg4|VJ+I+XuhwP6hklMYlxz1}5{H;taE5T-HML+wUY_lxECXa7L(UFm{c2`xdrUv! zw>ru{Bg6KG3{oR8d1Zz$^)-1V2fXH~T*9R?F`jhHW}xpdDJJAmg=J;7V|ipokW6`m zvObs@nUnUj)Xr;?I7H5;iHeySndE%hb6GBRx5Z++y<{0=GUt;wGcvC^pAFU3wygzZ zzJ=kbXUK=H;i<3s&~MRq&{FO*qvvnY1JGZ2kMsbtsRdP4w!Jhq`3Gd0sJSyElk@=e z%#Dj|N(V0Wa30}O-$5U#Yo>7DBtAZiN<}Ukwe#_$2k@tv(Q^_XpUc#OT2c!#KJ?Md z$h^jf5>fMB$gYD-(gTRbbE-2EhtLB!T#d;Jy8qczUvGP;i1Z?vxrZ6*xxTBXF6fyV zI(yHxXwihs8Pf9_nOF5>W-kh6#3AY z95WCUcV=Wx!U;K4EqWb0^2wzCGi2_93Z5aE(9_P<UN6Zk5WB(gI20lI_atrHb`jWi|-aVKn zXYSUIPlep}D)}$p%({jCFAMO0;eo#~6Cz<`LL@9AkVn2x7RF(7dAEv0;#+RH^@XaF zm9~#hp8Q9g$xs`UEXm<`mP&J}C7qqNdvp7@h_)4_q~z|9M))>FY1+sb(c4gBm|_ls zqbTuq)iag0Po8<^@3Cfm9hAsr(@HGs>ayLHyLg*uM{#Ou-jh(m7!} ~*s5~ryE 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%0AZ5fN-KvX4Rh z@&ASRKee4EZvyGd3L|u2s z7KMmAeb{wX(Q0Gg*t4=A zX7Jz`!2hr?ua1=%mODrtbb^1&`T4c4#F$jla^*+lPH{6$IPkEo2|`_Z#YK z8S$d23B(IpG-YLp96WKQr?}&1GnkA2&2Bb*jXZ1(X>V-&lkBz#$3iJ7WI<{>F1Ou< zdOvPn*)Udq-d(6}KjXp`db#UIi+Aa6%1>}bqAIeeJh`@^p|-Z6lHf)!NO^taa_n1K z?H}&lGX@J+qCxf%ibWF_6~Sulxs!soEb zEG`-=exiFaTAq2A8QrVm@>BD90CDY$Rm@X!hT%Y<6*s_PY957cG=mt|K-{d7`4bLj zi+z9fTcz5UtCV1?6t=4vYf2ES@4SOpm53F5|DkIFV*f|IysE6eL0GmDBSO5S3 literal 0 HcmV?d00001 diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Regular.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..6afc020af03e584c5a43434b9a643d430905698c GIT binary patch literal 2294192 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

    TJVlrQ+JZsuC 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

    )9zMlhO~x+o0s2nv*gh9hYD{KbI+a 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�WnztF;zu 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 z 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

    ?`Gv@m33w%MB3*cmPO;zP!nYnFQ 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

    ;9npKQ_59|Zb?MK}osk_OT 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 z

    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 Z&>;mv2^ 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%0AC7VmlS@LZ-TEZV}oirP&oT11@@uCp8_l( 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 Date: Thu, 8 May 2025 15:19:08 +0900 Subject: [PATCH 318/393] =?UTF-8?q?chore/#131:=20=ED=8F=B0=ED=8A=B8?= =?UTF-8?q?=EB=A5=BC=20=EB=94=94=EC=9E=90=EC=9D=B8=EC=8B=9C=EC=8A=A4?= =?UTF-8?q?=ED=85=9C=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 알고보니 검색 피쳐에 두고있었음 ㅋㅋ --- .../DesignSystem}/Font/GothicA1-Bold.ttf | Bin .../DesignSystem}/Font/GothicA1-Light.ttf | Bin .../DesignSystem}/Font/GothicA1-Medium.ttf | Bin .../DesignSystem}/Font/GothicA1-Regular.ttf | Bin .../DesignSystem}/Font/Poppins-Bold.ttf | Bin .../DesignSystem}/Font/Poppins-Light.ttf | Bin .../DesignSystem}/Font/Poppins-Medium.ttf | Bin .../DesignSystem}/Font/Poppins-Regular.ttf | Bin 8 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/GothicA1-Bold.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/GothicA1-Light.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/GothicA1-Medium.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/GothicA1-Regular.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/Poppins-Bold.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/Poppins-Light.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/Poppins-Medium.ttf (100%) rename Poppool/PresentationLayer/{SearchFeature/SearchFeatureDemo/Resource => DesignSystem/DesignSystem}/Font/Poppins-Regular.ttf (100%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Bold.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Bold.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Bold.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Bold.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Light.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Light.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Light.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Light.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Medium.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Medium.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Medium.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Medium.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Regular.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Regular.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/GothicA1-Regular.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Regular.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Bold.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Bold.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Bold.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Bold.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Light.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Light.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Light.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Light.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Medium.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Medium.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Medium.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Medium.ttf diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Regular.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Regular.ttf similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/Resource/Font/Poppins-Regular.ttf rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/Poppins-Regular.ttf From 11ac4593da49d9aa34224848948383321c7bbc0c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 15:24:02 +0900 Subject: [PATCH 319/393] =?UTF-8?q?fix/#131:=20Int64=20=E2=86=92=20Int=20?= =?UTF-8?q?=EB=8C=80=EC=9D=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/SearchFeature/Source/Model/Category.swift | 4 ++-- .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index 775c4c40..c443908f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -43,8 +43,8 @@ extension Category { items = [Category.defaultItem] } - func getSelectedCategoryIDs() -> [Int64] { - return items.filter { $0.isSelected == true }.compactMap { $0.id }.map { Int64($0) } + func getSelectedCategoryIDs() -> [Int] { + return items.filter { $0.isSelected == true }.compactMap { $0.id } } func getCancelableCategoryItems() -> [TagCollectionViewCell.Input] { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 0d70bb78..bbc51b1d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -323,7 +323,7 @@ private extension PopupSearchReactor { func fetchSearchResult( isOpen: Bool = Filter.shared.status.requestValue, - categories: [Int64] = Category.shared.getSelectedCategoryIDs(), + categories: [Int] = Category.shared.getSelectedCategoryIDs(), page: Int32 = 0, size: Int32 = 10, sort: String = Filter.shared.sort.requestValue From c220a7d4b5ffbc029388d612f7e8fbd1498b77d0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 16:51:48 +0900 Subject: [PATCH 320/393] =?UTF-8?q?refactor/#131:=20=ED=8C=AC=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=9E=90=EA=BE=B8=20=EC=97=90=EB=9F=AC=EB=82=98?= =?UTF-8?q?=EC=84=9C=20=EC=9D=BC=EB=8B=A8=20=EC=A7=80=EC=9B=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategorySelectViewController.swift | 20 ------------------- .../FilterSelectViewController.swift | 20 ------------------- .../View/PopupSearchViewController.swift | 20 +++++++++++++++++-- 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 51c6ad95..d5df7681 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -2,7 +2,6 @@ import UIKit import DesignSystem -import PanModal import ReactorKit import RxCocoa import RxSwift @@ -83,22 +82,3 @@ extension CategorySelectViewController { .disposed(by: disposeBag) } } - -// MARK: - PanModalPresentable -extension CategorySelectViewController: PanModalPresentable { - var panScrollable: UIScrollView? { - return nil - } - var longFormHeight: PanModalHeight { - return .intrinsicHeight - } - var shortFormHeight: PanModalHeight { - return .intrinsicHeight - } - var showDragIndicator: Bool { - return false - } - var cornerRadius: CGFloat { - return 20 - } -} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index 36b8b66d..dbfbb260 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -2,7 +2,6 @@ import UIKit import DesignSystem -import PanModal import ReactorKit import RxCocoa import RxSwift @@ -82,22 +81,3 @@ extension FilterSelectViewController { .disposed(by: disposeBag) } } - -// MARK: - PanModalPresentable -extension FilterSelectViewController: PanModalPresentable { - var panScrollable: UIScrollView? { - return nil - } - var longFormHeight: PanModalHeight { - return .intrinsicHeight - } - var shortFormHeight: PanModalHeight { - return .intrinsicHeight - } - var showDragIndicator: Bool { - return false - } - var cornerRadius: CGFloat { - return 20 - } -} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index ce2b487c..81726e57 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -7,6 +7,7 @@ import Infrastructure import ReactorKit import RxSwift import RxCocoa +import Then public final class PopupSearchViewController: BaseViewController, View { @@ -16,6 +17,7 @@ public final class PopupSearchViewController: BaseViewController, View { public var disposeBag = DisposeBag() private let mainView = PopupSearchView() + } // MARK: - Life Cycle @@ -27,6 +29,19 @@ extension PopupSearchViewController { public override func viewDidLoad() { super.viewDidLoad() } + + public override func present( + _ viewControllerToPresent: UIViewController, + animated flag: Bool, + completion: (() -> Void)? = nil + ) { + if let sheet = viewControllerToPresent.sheetPresentationController { + sheet.detents = [.medium()] + sheet.preferredCornerRadius = 20 + } + + super.present(viewControllerToPresent, animated: flag, completion: completion) + } } // MARK: - Bind @@ -124,6 +139,7 @@ extension PopupSearchViewController { .disposed(by: disposeBag) reactor.pulse(\.$present) + .debug("DEBUG: present") .withUnretained(self) .subscribe { owner, target in switch target { @@ -139,7 +155,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: owner.disposeBag) - owner.presentPanModal(viewController) + owner.present(viewController, animated: true) case .filterSelector: let viewController = FilterSelectViewController() @@ -150,7 +166,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: owner.disposeBag) - owner.presentPanModal(viewController) + owner.present(viewController, animated: true) default: break } From 4ad4acc92c06009610190e643811dc8330a1ca37 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 18:06:23 +0900 Subject: [PATCH 321/393] =?UTF-8?q?feat/#131:=20PanModal=20=EB=8C=80?= =?UTF-8?q?=EC=B2=B4=ED=92=88=20=EC=83=9D=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/UIViewController+.swift | 190 ++++++++++++++++++ .../Interfaces/PPModalPresentable.swift | 11 + 2 files changed, 201 insertions(+) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/PPModalPresentable.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift new file mode 100644 index 00000000..e8e5387b --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift @@ -0,0 +1,190 @@ +import UIKit +import ObjectiveC + +private struct PPModalConstants { + static let animationDuration: TimeInterval = 0.2 + static let presentAnimationOptions: UIView.AnimationOptions = .curveEaseOut + static let dismissAnimationOptions: UIView.AnimationOptions = .curveEaseIn +} + +// modal의 subView와 상태를 들고있는 매니저 +private class PPModalManager { + weak var presentingVC: UIViewController? + weak var presentedViewController: (UIViewController & PPModalPresentable)? + var dimmingView: UIView? + var containerView: UIView? + + init(presenting: UIViewController) { + self.presentingVC = presenting + } +} + +extension UIViewController { + // MARK: - Storage + private struct PPModalStorage { + static var managers: [ObjectIdentifier: PPModalManager] = [:] + } + + private var pp_manager: PPModalManager { + let id = ObjectIdentifier(self) + + if let modalManager = PPModalStorage.managers[id] { + return modalManager + } + + let modalManager = PPModalManager(presenting: self) + PPModalStorage.managers[id] = modalManager + + return modalManager + } + + // MARK: - Present as Bottom Sheet + /// view controller를 bottom-sheet modal처럼 present 해줍니다 + public func PPPresent(_ viewController: UIViewController & PPModalPresentable, animated: Bool = true) { + let manager = pp_manager + manager.presentedViewController = viewController + + // presentingView에 dimming을 조절 + let dimView = UIView(frame: view.bounds) + dimView.backgroundColor = viewController.backgroundColor + dimView.alpha = 0 + dimView.autoresizingMask = [.flexibleWidth, .flexibleHeight] + view.addSubview(dimView) + manager.dimmingView = dimView + let tapGesture = UITapGestureRecognizer(target: self, action: #selector(handlePPTap(_:))) + dimView.addGestureRecognizer(tapGesture) + + // 높이를 결정. 값이 주워진다면 해당 값을 쓰고, 없다면 view의 기본 frame을 씀 + let height = viewController.modalHeight ?? viewController.view.frame.height + let container = UIView(frame: CGRect( + x: 0, + y: view.bounds.height, + width: view.bounds.width, + height: height + )) + container.backgroundColor = .clear + container.autoresizingMask = [.flexibleWidth, .flexibleTopMargin] + view.addSubview(container) + manager.containerView = container + + // Embed child + addChild(viewController) + viewController.view.frame = container.bounds + viewController.view.layer.cornerRadius = viewController.cornerRadius + viewController.view.clipsToBounds = true + container.addSubview(viewController.view) + viewController.didMove(toParent: self) + + let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePPPan(_:))) + container.addGestureRecognizer(pan) + + // Animate in + let animateIn = { + dimView.alpha = 1 + container.frame.origin.y = self.view.bounds.height - height + } + if animated { + UIView.animate( + withDuration: PPModalConstants.animationDuration, + delay: 0, + options: PPModalConstants.presentAnimationOptions, + animations: animateIn, + completion: nil + ) + } else { + animateIn() + } + } + + @objc private func handlePPTap(_ gesture: UITapGestureRecognizer) { + PPDismiss(animated: true) + } + + // MARK: - Dismiss + /// bottom-sheet modal을 Dismiss 합니다. + func PPDismiss(animated: Bool = true) { + guard let manager = PPModalStorage.managers[ObjectIdentifier(self)], + let container = manager.containerView, + let dimView = manager.dimmingView else { return } + + let finish = { + if let vc = manager.presentedViewController { + vc.willMove(toParent: nil) + vc.view.removeFromSuperview() + vc.removeFromParent() + } + dimView.removeFromSuperview() + container.removeFromSuperview() + PPModalStorage.managers.removeValue(forKey: ObjectIdentifier(self)) + } + + let animateOut = { + container.frame.origin.y = self.view.bounds.height + dimView.alpha = 0 + } + if animated { + UIView.animate( + withDuration: PPModalConstants.animationDuration, + delay: 0, + options: PPModalConstants.dismissAnimationOptions, + animations: animateOut, + completion: { _ in finish() } + ) + } else { + animateOut() + finish() + } + } + + // MARK: - Pan Gesture Handler + @objc private func handlePPPan(_ pan: UIPanGestureRecognizer) { + guard let manager = PPModalStorage.managers[ObjectIdentifier(self)], + let container = manager.containerView, + let dimView = manager.dimmingView, + let vc = manager.presentedViewController else { return } + + let translation = pan.translation(in: container) + let velocity = pan.velocity(in: container) + let height = vc.modalHeight ?? vc.view.frame.height + let threshold: CGFloat = 100 + + switch pan.state { + case .changed: + let minY = view.bounds.height - height + let newY = max(minY, container.frame.origin.y + translation.y) + container.frame.origin.y = newY + let progress = 1 - ((newY - minY) / height) + dimView.alpha = progress + pan.setTranslation(.zero, in: container) + + case .ended: + let minY = view.bounds.height - height + let shouldDismiss = velocity.y > threshold || container.frame.origin.y > minY + height / 2 + let animate = { + if shouldDismiss { + container.frame.origin.y = self.view.bounds.height + dimView.alpha = 0 + } else { + container.frame.origin.y = minY + dimView.alpha = 1 + } + } + UIView.animate( + withDuration: PPModalConstants.animationDuration, + delay: 0, + options: PPModalConstants.presentAnimationOptions, + animations: animate, + completion: { _ in if shouldDismiss { self.PPDismiss(animated: false) } } + ) + default: + break + } + } +} + +// Convenience dismiss inside presented VC +public extension PPModalPresentable where Self: UIViewController { + func dismissModal(animated: Bool = true) { + parent?.PPDismiss(animated: animated) + } +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/PPModalPresentable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/PPModalPresentable.swift new file mode 100644 index 00000000..45ddb08f --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/PPModalPresentable.swift @@ -0,0 +1,11 @@ +import UIKit + +// Protocol that presented view controllers must conform to +public protocol PPModalPresentable: AnyObject { + /// The height of the modal view. If nil, falls back to the view's own height. + var modalHeight: CGFloat? { get } + /// The background dimming color behind the modal view + var backgroundColor: UIColor { get } + /// The corner radius for the modal's top corners + var cornerRadius: CGFloat { get } +} From 99652b61d5de3260cf67a538be1ec9143c81c3ac Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 18:19:04 +0900 Subject: [PATCH 322/393] =?UTF-8?q?refactor/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=EC=BB=A4=EC=8A=A4=ED=85=80=20=EB=AA=A8?= =?UTF-8?q?=EB=8B=AC=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../FilterSelectModal/FilterSelectView.swift | 17 +++++++++-------- .../FilterSelectViewController.swift | 12 ++++++++++-- .../Source/View/PopupSearchViewController.swift | 2 +- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift index 22ce4713..efbbb69a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift @@ -49,40 +49,41 @@ private extension FilterSelectView { // FIXME: 레이아웃 에러로 인한 Modal이 살짝 내려가지는 문제 발생중 func setupConstraints() { titleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) + make.leading.equalTo(safeAreaLayoutGuide).inset(20) } closeButton.snp.makeConstraints { make in make.size.equalTo(24) - make.trailing.equalToSuperview().inset(20) + make.trailing.equalTo(safeAreaLayoutGuide).inset(20) make.centerY.equalTo(titleLabel) } statusLabel.snp.makeConstraints { make in make.top.equalTo(titleLabel.snp.bottom).offset(36) - make.leading.equalToSuperview().inset(20) + make.leading.equalTo(safeAreaLayoutGuide).inset(20) } statusSegmentControl.snp.makeConstraints { make in make.top.equalTo(statusLabel.snp.bottom).offset(8) - make.leading.trailing.equalToSuperview().inset(20) + make.leading.trailing.equalTo(safeAreaLayoutGuide).inset(20) } sortLabel.snp.makeConstraints { make in make.top.equalTo(statusSegmentControl.snp.bottom).offset(20) - make.leading.equalToSuperview().inset(20) + make.leading.equalTo(safeAreaLayoutGuide).inset(20) } sortSegmentControl.snp.makeConstraints { make in make.top.equalTo(sortLabel.snp.bottom).offset(8) - make.horizontalEdges.equalToSuperview().inset(20) + make.horizontalEdges.equalTo(safeAreaLayoutGuide).inset(20) } saveButton.snp.makeConstraints { make in make.top.equalTo(sortSegmentControl.snp.bottom).offset(32) - make.horizontalEdges.equalToSuperview().inset(20) - make.bottom.equalTo(self.safeAreaLayoutGuide) + make.height.equalTo(50) + make.horizontalEdges.equalTo(safeAreaLayoutGuide.snp.horizontalEdges).inset(20) + make.bottom.equalTo(safeAreaLayoutGuide.snp.bottom) } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index dbfbb260..0ce28688 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -22,7 +22,7 @@ extension FilterSelectViewController { override func loadView() { self.view = mainView } - + override func viewDidLoad() { super.viewDidLoad() } @@ -77,7 +77,15 @@ extension FilterSelectViewController { reactor.pulse(\.$dismiss) .withUnretained(self) - .subscribe { (owner, _) in owner.dismiss(animated: true) } + .subscribe { (owner, _) in owner.dismissModal() } .disposed(by: disposeBag) } } + +extension FilterSelectViewController: PPModalPresentable { + var modalHeight: CGFloat? { return 379 } + + var backgroundColor: UIColor { return .pb60 } + + var cornerRadius: CGFloat { return 20 } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 81726e57..f35e3526 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -166,7 +166,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: owner.disposeBag) - owner.present(viewController, animated: true) + owner.PPPresent(viewController, animated: true) default: break } From 4530ec9f377c1edb6476e8d612c6069da7ddde2d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 18:34:00 +0900 Subject: [PATCH 323/393] =?UTF-8?q?refactor/#131:=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EC=84=A0=ED=83=9D=20=EC=BB=A4=EC=8A=A4?= =?UTF-8?q?=ED=85=80=20=EB=AA=A8=EB=8B=AC=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategorySelectModal/CategorySelectView.swift | 15 +++++++-------- .../CategorySelectViewController.swift | 10 +++++++++- .../Source/View/PopupSearchViewController.swift | 4 ++-- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift index 2983c77d..44d2c68c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift @@ -63,27 +63,26 @@ private extension CategorySelectView { // FIXME: 레이아웃 에러로 인한 Modal이 살짝 내려가지는 문제 발생중 func setupConstraints() { titleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) make.top.equalToSuperview().inset(32) + make.leading.equalTo(safeAreaLayoutGuide).inset(20) } closeButton.snp.makeConstraints { make in make.size.equalTo(24) - make.trailing.equalToSuperview().inset(20) + make.trailing.equalTo(safeAreaLayoutGuide).inset(20) make.centerY.equalTo(titleLabel) } collectionView.snp.makeConstraints { make in - make.horizontalEdges.equalToSuperview() + make.horizontalEdges.equalTo(safeAreaLayoutGuide) make.top.equalTo(titleLabel.snp.bottom).offset(24) - make.height.equalTo(195) + make.bottom.equalTo(buttonStackView.snp.top).offset(24) } buttonStackView.snp.makeConstraints { make in - make.top.equalTo(collectionView.snp.bottom).offset(24) - make.leading.trailing.equalToSuperview().inset(20) + make.leading.trailing.equalTo(safeAreaLayoutGuide).inset(20) make.height.equalTo(52) - make.bottom.equalTo(self.safeAreaLayoutGuide) + make.bottom.equalTo(safeAreaLayoutGuide) } } } @@ -100,7 +99,7 @@ private extension CategorySelectView { let groupSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(1000) + heightDimension: .estimated(200) ) let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) group.interItemSpacing = .fixed(12) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index d5df7681..67090d36 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -65,7 +65,7 @@ extension CategorySelectViewController { private func bindState(reactor: Reactor) { reactor.pulse(\.$dismiss) .withUnretained(self) - .subscribe { (owner, _) in owner.dismiss(animated: true) } + .subscribe { (owner, _) in owner.dismissModal() } .disposed(by: disposeBag) reactor.state.distinctUntilChanged(\.categoryItems) @@ -82,3 +82,11 @@ extension CategorySelectViewController { .disposed(by: disposeBag) } } + +extension CategorySelectViewController: PPModalPresentable { + var modalHeight: CGFloat? { return 384 } + + var backgroundColor: UIColor { return .pb60 } + + var cornerRadius: CGFloat { return 20 } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index f35e3526..cbd03c65 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -155,7 +155,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: owner.disposeBag) - owner.present(viewController, animated: true) + owner.PPPresent(viewController) case .filterSelector: let viewController = FilterSelectViewController() @@ -166,7 +166,7 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: owner.disposeBag) - owner.PPPresent(viewController, animated: true) + owner.PPPresent(viewController) default: break } From 4fb4617e758630d462bd02daf4cc01a674d2184e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 18:40:45 +0900 Subject: [PATCH 324/393] =?UTF-8?q?refactor/#131:=20PPSearchBar=20?= =?UTF-8?q?=EB=94=94=EC=9E=90=EC=9D=B8=EC=8B=9C=EC=8A=A4=ED=85=9C=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DesignSystem.xcodeproj/project.pbxproj | 17 +++++++++++++++++ .../Components}/PPSearchBarView.swift | 8 +++----- .../Source/View/PopupSearchView.swift | 4 +++- 3 files changed, 23 insertions(+), 6 deletions(-) rename Poppool/PresentationLayer/{SearchFeature/SearchFeature/Source/View => DesignSystem/DesignSystem/Components}/PPSearchBarView.swift (92%) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj index 307aab52..c6b12b47 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem.xcodeproj/project.pbxproj @@ -13,6 +13,7 @@ 051631652DC3D29400A6C0D1 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 051631642DC3D29400A6C0D1 /* SnapKit */; }; 051631682DC3D3FA00A6C0D1 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 051631672DC3D3FA00A6C0D1 /* Tabman */; }; 0516316B2DC3D50700A6C0D1 /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 0516316A2DC3D50700A6C0D1 /* Pageboy */; }; + 05CFFCBE2DCCB3810051129F /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFCBD2DCCB3810051129F /* Then */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -36,6 +37,7 @@ 0516316B2DC3D50700A6C0D1 /* Pageboy in Frameworks */, 051631682DC3D3FA00A6C0D1 /* Tabman in Frameworks */, 051631622DC3D28400A6C0D1 /* RxSwift in Frameworks */, + 05CFFCBE2DCCB3810051129F /* Then in Frameworks */, 051631652DC3D29400A6C0D1 /* SnapKit in Frameworks */, 051631602DC3D28400A6C0D1 /* RxCocoa in Frameworks */, 0516312C2DC3D1E900A6C0D1 /* Infrastructure.framework in Frameworks */, @@ -106,6 +108,7 @@ 051631642DC3D29400A6C0D1 /* SnapKit */, 051631672DC3D3FA00A6C0D1 /* Tabman */, 0516316A2DC3D50700A6C0D1 /* Pageboy */, + 05CFFCBD2DCCB3810051129F /* Then */, ); productName = DesignSystem; productReference = 051630B82DC3D1A000A6C0D1 /* DesignSystem.framework */; @@ -140,6 +143,7 @@ 051631632DC3D29400A6C0D1 /* XCRemoteSwiftPackageReference "SnapKit" */, 051631662DC3D3FA00A6C0D1 /* XCRemoteSwiftPackageReference "Tabman" */, 051631692DC3D50700A6C0D1 /* XCRemoteSwiftPackageReference "Pageboy" */, + 05CFFCBC2DCCB3810051129F /* XCRemoteSwiftPackageReference "Then" */, ); preferredProjectObjectVersion = 77; productRefGroup = 051630B92DC3D1A000A6C0D1 /* Products */; @@ -427,6 +431,14 @@ minimumVersion = 4.2.0; }; }; + 05CFFCBC2DCCB3810051129F /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -455,6 +467,11 @@ package = 051631692DC3D50700A6C0D1 /* XCRemoteSwiftPackageReference "Pageboy" */; productName = Pageboy; }; + 05CFFCBD2DCCB3810051129F /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05CFFCBC2DCCB3810051129F /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; /* End XCSwiftPackageProductDependency section */ }; rootObject = 051630AF2DC3D1A000A6C0D1 /* Project object */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift similarity index 92% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift index 967fd74c..230078f1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPSearchBarView.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift @@ -1,7 +1,5 @@ import UIKit -import DesignSystem - import SnapKit import Then @@ -13,7 +11,7 @@ public class PPSearchBarView: UIView { $0.axis = .horizontal } - let searchBar = UISearchBar().then { + public let searchBar = UISearchBar().then { $0.searchTextField.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .korFont(style: .regular, size: 14)) $0.tintColor = .g400 $0.backgroundColor = .g50 @@ -23,12 +21,12 @@ public class PPSearchBarView: UIView { $0.searchTextField.subviews.first?.isHidden = true } - let clearButton = UIButton(type: .custom).then { + public let clearButton = UIButton(type: .custom).then { $0.setImage(UIImage(named: "icon_clear_button"), for: .normal) $0.isHidden = true } - let cancelButton = UIButton(type: .system).then { + public let cancelButton = UIButton(type: .system).then { $0.setTitle("취소", for: .normal) $0.tintColor = .g1000 $0.titleLabel?.font = .korFont(style: .regular, size: 14) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 8c9838e8..c4de0e48 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -1,5 +1,7 @@ import UIKit +import DesignSystem + import SnapKit import Then import RxSwift @@ -40,7 +42,7 @@ final class PopupSearchView: UIView { $0.cancelsTouchesInView = false } - let searchBar = PPSearchBarView() + public let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { let layout = layoutFactory.makeCollectionViewLayout { [weak self] in self?.dataSource } From 7eea641b8ded75857fac5cd1316d1d3548c28da0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 18:59:31 +0900 Subject: [PATCH 325/393] =?UTF-8?q?refactor/#131:=20PopupGrid=EC=97=90=20?= =?UTF-8?q?=EC=9E=88=EB=8D=98=20Inputable=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inputable은 디자인시스템에 존재 - Input 모델을 쓰는곳은 Reactor - Reactor가 View 내부 타입에 의존하게 됨 - 별도의 모델로 만들어서 관리하도록 수정 --- .../PPPopupGridCollectionViewCell.swift | 37 ++++++------------- .../Source/Model/SearchResultModel.swift | 15 ++++++++ .../Source/Reactor/PopupSearchReactor.swift | 10 ++--- .../Source/View/PopupSearchView.swift | 6 +-- 4 files changed, 35 insertions(+), 33 deletions(-) rename Poppool/PresentationLayer/{SearchFeature/SearchFeature/Source/View => DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell}/PPPopupGridCollectionViewCell.swift (80%) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift similarity index 80% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift index 1c8bffd4..231f7e14 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PPPopupGridCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift @@ -1,6 +1,6 @@ import UIKit -import DesignSystem +import Infrastructure import RxSwift import SnapKit @@ -134,38 +134,25 @@ private extension PPPopupGridCollectionViewCell { } } -extension PPPopupGridCollectionViewCell: Inputable { - public struct Input: Hashable { - var imagePath: String? - var id: Int64 - var category: String? - var title: String? - var address: String? - var startDate: String? - var endDate: String? - var isBookmark: Bool - var isLogin: Bool - var isPopular: Bool = false - var row: Int? - } +extension PPPopupGridCollectionViewCell { + public func configureCell(imagePath: String?, id: Int64, category: String?, title: String?, address: String?, startDate: String?, endDate: String?, isBookmark: Bool, isLogin: Bool, isPopular: Bool = false, row: Int?) { - public func injection(with input: Input) { - categoryLabel.text = "#" + (input.category ?? "") - titleLabel.text = input.title - addressLabel.text = input.address + categoryLabel.text = "#" + (category ?? "") + titleLabel.text = title + addressLabel.text = address - let date = input.startDate.toDate().toPPDateString() + " ~ " + input.endDate.toDate().toPPDateString() + let date = startDate.toDate().toPPDateString() + " ~ " + endDate.toDate().toPPDateString() dateLabel.text = date - let bookmarkImage = input.isBookmark ? UIImage(named: "icon_bookmark_fill") : UIImage(named: "icon_bookmark") + let bookmarkImage = isBookmark ? UIImage(named: "icon_bookmark_fill") : UIImage(named: "icon_bookmark") bookmarkButton.setImage(bookmarkImage, for: .normal) - imageView.setPPImage(path: input.imagePath) + imageView.setPPImage(path: imagePath) - bookmarkButton.isHidden = !input.isLogin - rankLabel.isHidden = !input.isPopular + bookmarkButton.isHidden = !isLogin + rankLabel.isHidden = !isPopular - if let rank = input.row { + if let rank = row { rankLabel.text = "\(rank)" rankLabel.isHidden = rank > 2 } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift new file mode 100644 index 00000000..fc24bdae --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift @@ -0,0 +1,15 @@ +import Foundation + +public struct SearchResultModel: Hashable { + var imagePath: String? + var id: Int64 + var category: String? + var title: String? + var address: String? + var startDate: String? + var endDate: String? + var isBookmark: Bool + var isLogin: Bool + var isPopular: Bool = false + var row: Int? +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index bbc51b1d..2d57cd53 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -36,11 +36,11 @@ public final class PopupSearchReactor: Reactor { public enum Mutation { case setupRecentSearch(items: [TagCollectionViewCell.Input]) case setupCategory(items: [TagCollectionViewCell.Input]) - case setupSearchResult(items: [PPPopupGridCollectionViewCell.Input]) + case setupSearchResult(items: [SearchResultModel]) case setupSearchResultHeader(item: SearchResultHeaderView.Input) case setupSearchResultTotalPageCount(count: Int32) - case appendSearchResult(items: [PPPopupGridCollectionViewCell.Input]) + case appendSearchResult(items: [SearchResultModel]) case updateEditingState case updateSearchBar(to: String?) @@ -61,7 +61,7 @@ public final class PopupSearchReactor: Reactor { public struct State { var recentSearchItems: [TagCollectionViewCell.Input] = [] var categoryItems: [TagCollectionViewCell.Input] = [] - var searchResultItems: [PPPopupGridCollectionViewCell.Input] = [] + var searchResultItems: [SearchResultModel] = [] var searchResultHeader: SearchResultHeaderView.Input? = nil var searchResultEmptyCase: SearchResultEmptyCollectionViewCell.EmptyCase? @@ -360,9 +360,9 @@ private extension PopupSearchReactor { return Category.shared.getCancelableCategoryItems() } - func makeSearchResultItems(_ popupStoreList: [PopUpStoreResponse], _ loginYn: Bool) -> [PPPopupGridCollectionViewCell.Input] { + func makeSearchResultItems(_ popupStoreList: [PopUpStoreResponse], _ loginYn: Bool) -> [SearchResultModel] { return popupStoreList.map { - PPPopupGridCollectionViewCell.Input( + SearchResultModel( imagePath: $0.mainImageUrl, id: $0.id, category: $0.category, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index c4de0e48..d861b285 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -20,7 +20,7 @@ final class PopupSearchView: UIView { enum SectionItem: Hashable { case recentSearchItem(TagCollectionViewCell.Input) case categoryItem(TagCollectionViewCell.Input) - case searchResultItem(PPPopupGridCollectionViewCell.Input) + case searchResultItem(SearchResultModel) case searchResultEmptyItem(SearchResultEmptyCollectionViewCell.EmptyCase) } @@ -171,12 +171,12 @@ extension PopupSearchView { return cell - case .searchResultItem(let searchResultItem): + case .searchResultItem(let item): let cell = collectionView.dequeueReusableCell( withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, for: indexPath ) as! PPPopupGridCollectionViewCell - cell.injection(with: searchResultItem) + cell.configureCell(imagePath: item.imagePath, id: item.id, category: item.category, title: item.title, address: item.address, startDate: item.startDate, endDate: item.endDate, isBookmark: item.isBookmark, isLogin: item.isLogin, isPopular: item.isPopular, row: item.row) return cell case .searchResultEmptyItem(let emptyCase): From 743f3ec84a62d8aa40cdde0ce25c7b2a9d3d7e94 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 19:12:24 +0900 Subject: [PATCH 326/393] =?UTF-8?q?refactor/#131:=20TagCollectionViewCell?= =?UTF-8?q?=20Input=20=EA=B0=9D=EC=B2=B4=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PopupGrid와 같은 이유로 프로토콜 의존 제거 --- .../TagCollectionViewCell.swift | 38 ++++++------------- .../SearchFeature/Source/Model/Category.swift | 10 ++--- .../SearchFeature/Source/Model/TagModel.swift | 17 +++++++++ .../Reactor/CategorySelectReactor.swift | 8 ++-- .../Source/Reactor/PopupSearchReactor.swift | 14 +++---- .../CategorySelectViewController.swift | 4 +- .../Source/View/PopupSearchView.swift | 14 +++---- 7 files changed, 54 insertions(+), 51 deletions(-) rename Poppool/PresentationLayer/{SearchFeature/SearchFeature/Source/View => DesignSystem/DesignSystem/Components/PPTagCollectionViewCell}/TagCollectionViewCell.swift (64%) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift similarity index 64% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift index 1073a110..61a0980e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift @@ -9,11 +9,11 @@ public final class TagCollectionViewCell: UICollectionViewCell { // MARK: - Components - var disposeBag = DisposeBag() + public var disposeBag = DisposeBag() - let titleLabel = PPLabel(style: .medium, fontSize: 11) + public let titleLabel = PPLabel(style: .medium, fontSize: 11) - let cancelButton = UIButton() + public let cancelButton = UIButton() private let contentStackView = UIStackView().then { $0.alignment = .center @@ -75,40 +75,24 @@ private extension TagCollectionViewCell { } } -extension TagCollectionViewCell: Inputable { - public struct Input: Hashable { - var title: String? - var id: Int? = nil - var isSelected: Bool = false - var isCancelable: Bool = true - - func selectionToggledItem() -> Input { - let toggledSelection = !isSelected - return Input(title: self.title, id: self.id, isSelected: toggledSelection, isCancelable: self.isCancelable) - } - - func cancelableItem() -> Input { - return Input(title: self.title, id: self.id, isSelected: self.isSelected, isCancelable: true) - } - } - - public func injection(with input: Input) { - let xmarkImage = input.isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") +extension TagCollectionViewCell { + public func configureCell(title: String? = nil, id: Int?, isSelected: Bool = false, isCancelable: Bool = true) { + let xmarkImage = isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") cancelButton.setImage(xmarkImage, for: .normal) - if input.isSelected { + if isSelected { contentView.backgroundColor = .blu500 - titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) titleLabel.textColor = .w100 contentView.layer.borderColor = UIColor.blu500.cgColor } else { contentView.backgroundColor = .clear - titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) titleLabel.textColor = .g400 contentView.layer.borderColor = UIColor.g200.cgColor } - cancelButton.isHidden = !input.isCancelable + cancelButton.isHidden = !isCancelable - if input.isCancelable { + if isCancelable { contentStackView.snp.updateConstraints { make in make.trailing.equalToSuperview().inset(8) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index c443908f..d46f1da6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -10,15 +10,15 @@ public final class Category: NSCopying, Equatable { static let shared = Category() /// 선택된 아이템들만 들어가는 인스턴스 - private var _items: [TagCollectionViewCell.Input] - public var items: [TagCollectionViewCell.Input] { + private var _items: [TagModel] + public var items: [TagModel] { get { _items } set { _items = newValue.isEmpty ? [Category.defaultItem] : newValue } } - private static let defaultItem = TagCollectionViewCell.Input(title: "카테고리", isSelected: false, isCancelable: false) + private static let defaultItem = TagModel(title: "카테고리", isSelected: false, isCancelable: false) - private init(items: [TagCollectionViewCell.Input] = [Category.defaultItem]) { + private init(items: [TagModel] = [Category.defaultItem]) { self._items = items.isEmpty ? [Category.defaultItem] : items } } @@ -47,7 +47,7 @@ extension Category { return items.filter { $0.isSelected == true }.compactMap { $0.id } } - func getCancelableCategoryItems() -> [TagCollectionViewCell.Input] { + func getCancelableCategoryItems() -> [TagModel] { if items == [Category.defaultItem] { return items } else { return items.filter { $0.isSelected == true }.map { $0.cancelableItem() } } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift new file mode 100644 index 00000000..98e30f69 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift @@ -0,0 +1,17 @@ +import Foundation + +public struct TagModel: Hashable { + var title: String? + var id: Int? = nil + var isSelected: Bool = false + var isCancelable: Bool = true + + func selectionToggledItem() -> TagModel { + let toggledSelection = !isSelected + return TagModel(title: self.title, id: self.id, isSelected: toggledSelection, isCancelable: self.isCancelable) + } + + func cancelableItem() -> TagModel { + return TagModel(title: self.title, id: self.id, isSelected: self.isSelected, isCancelable: true) + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index bf254033..a68d0ebe 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -18,7 +18,7 @@ final class CategorySelectReactor: Reactor { } enum Mutation { - case setupCategotyTag(items: [TagCollectionViewCell.Input]) + case setupCategotyTag(items: [TagModel]) case dismiss case resetCategory case saveCategory @@ -28,7 +28,7 @@ final class CategorySelectReactor: Reactor { } struct State { - var categoryItems: [TagCollectionViewCell.Input] = [] + var categoryItems: [TagModel] = [] var saveButtonIsEnable: Bool = false var selectedCategoryChanged: Bool? @@ -39,7 +39,7 @@ final class CategorySelectReactor: Reactor { var initialState: State var disposeBag = DisposeBag() - private var originCategoryItems: [TagCollectionViewCell.Input] = [] + private var originCategoryItems: [TagModel] = [] private let fetchCategoryListUseCase: FetchCategoryListUseCase // MARK: - init @@ -58,7 +58,7 @@ final class CategorySelectReactor: Reactor { .withUnretained(self) .map { (owner, response) in let items = response.map { - return TagCollectionViewCell.Input(title: $0.category, id: $0.categoryId, isCancelable: false) + return TagModel(title: $0.category, id: $0.categoryId, isCancelable: false) } return .setupCategotyTag(items: items) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 2d57cd53..3c1e19a7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -34,8 +34,8 @@ public final class PopupSearchReactor: Reactor { } public enum Mutation { - case setupRecentSearch(items: [TagCollectionViewCell.Input]) - case setupCategory(items: [TagCollectionViewCell.Input]) + case setupRecentSearch(items: [TagModel]) + case setupCategory(items: [TagModel]) case setupSearchResult(items: [SearchResultModel]) case setupSearchResultHeader(item: SearchResultHeaderView.Input) case setupSearchResultTotalPageCount(count: Int32) @@ -59,8 +59,8 @@ public final class PopupSearchReactor: Reactor { } public struct State { - var recentSearchItems: [TagCollectionViewCell.Input] = [] - var categoryItems: [TagCollectionViewCell.Input] = [] + var recentSearchItems: [TagModel] = [] + var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] var searchResultHeader: SearchResultHeaderView.Input? = nil var searchResultEmptyCase: SearchResultEmptyCollectionViewCell.EmptyCase? @@ -351,12 +351,12 @@ private extension PopupSearchReactor { return searchKeywords[indexPath.item] } - func makeRecentSearchItems() -> [TagCollectionViewCell.Input] { + func makeRecentSearchItems() -> [TagModel] { let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword) ?? [] - return searchKeywords.map { TagCollectionViewCell.Input(title: $0) } + return searchKeywords.map { TagModel(title: $0) } } - func makeCategoryItems() -> [TagCollectionViewCell.Input] { + func makeCategoryItems() -> [TagModel] { return Category.shared.getCancelableCategoryItems() } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 67090d36..a424ad49 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -73,7 +73,9 @@ extension CategorySelectViewController { .bind(to: mainView.collectionView.rx.items( cellIdentifier: TagCollectionViewCell.identifiers, cellType: TagCollectionViewCell.self - )) { _, item, cell in cell.injection(with: item) } + )) { _, item, cell in + cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) + } .disposed(by: disposeBag) reactor.state.distinctUntilChanged(\.saveButtonIsEnable) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index d861b285..a456283a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -18,8 +18,8 @@ final class PopupSearchView: UIView { /// Section에 들어갈 Item을 정의한 변수 enum SectionItem: Hashable { - case recentSearchItem(TagCollectionViewCell.Input) - case categoryItem(TagCollectionViewCell.Input) + case recentSearchItem(TagModel) + case categoryItem(TagModel) case searchResultItem(SearchResultModel) case searchResultEmptyItem(SearchResultEmptyCollectionViewCell.EmptyCase) } @@ -143,12 +143,12 @@ extension PopupSearchView { collectionView: collectionView ) { (collectionView, indexPath, item) -> UICollectionViewCell? in switch item { - case .recentSearchItem(let recentRearchItem): + case .recentSearchItem(let item): let cell = collectionView.dequeueReusableCell( withReuseIdentifier: TagCollectionViewCell.identifiers, for: indexPath ) as! TagCollectionViewCell - cell.injection(with: recentRearchItem) + cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) cell.cancelButton.rx.tap .compactMap { cell.titleLabel.text } @@ -157,15 +157,15 @@ extension PopupSearchView { return cell - case .categoryItem(let categoryItem): + case .categoryItem(let item): let cell = collectionView.dequeueReusableCell( withReuseIdentifier: TagCollectionViewCell.identifiers, for: indexPath ) as! TagCollectionViewCell - cell.injection(with: categoryItem) + cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) cell.cancelButton.rx.tap - .compactMap { categoryItem.id } + .compactMap { item.id } .bind(to: self.categoryTagRemoveButtonTapped) .disposed(by: cell.disposeBag) From e44ca645c59649b04ae829e592df6e72e9e9919e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 19:26:14 +0900 Subject: [PATCH 327/393] =?UTF-8?q?refactor/#131:=20SearchFeature=EC=97=90?= =?UTF-8?q?=EC=84=9C=20Inputable=20=EB=AA=A8=EB=91=90=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Inputable이 결국 DesignSystem과 결합도를 야기함 - Feature가 굳이 Input한 프로토콜을 의존하기 위해 DesignSystem을 의존할 이유가 없음 - 그래서 제거 --- .../Model/SearchResultHeaderModel.swift | 7 +++++++ .../Source/Model/SearchResultModel.swift | 5 +++++ .../Source/Reactor/PopupSearchReactor.swift | 14 +++++++------- .../Source/View/PopupSearchView.swift | 18 ++++++++---------- .../SearchResultEmptyCollectionViewCell.swift | 15 +++------------ .../Source/View/SearchResultHeaderView.swift | 19 ++++++------------- 6 files changed, 36 insertions(+), 42 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift new file mode 100644 index 00000000..313a5ed3 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift @@ -0,0 +1,7 @@ +import Foundation + +public struct SearchResultHeaderModel { + let title: String? + let count: Int? + let filterText: String? +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift index fc24bdae..c52314a0 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift @@ -12,4 +12,9 @@ public struct SearchResultModel: Hashable { var isLogin: Bool var isPopular: Bool = false var row: Int? + + enum EmptyCase { + case option + case keyword + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 3c1e19a7..54b8fe48 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -37,7 +37,7 @@ public final class PopupSearchReactor: Reactor { case setupRecentSearch(items: [TagModel]) case setupCategory(items: [TagModel]) case setupSearchResult(items: [SearchResultModel]) - case setupSearchResultHeader(item: SearchResultHeaderView.Input) + case setupSearchResultHeader(item: SearchResultHeaderModel) case setupSearchResultTotalPageCount(count: Int32) case appendSearchResult(items: [SearchResultModel]) @@ -62,8 +62,8 @@ public final class PopupSearchReactor: Reactor { var recentSearchItems: [TagModel] = [] var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] - var searchResultHeader: SearchResultHeaderView.Input? = nil - var searchResultEmptyCase: SearchResultEmptyCollectionViewCell.EmptyCase? + var searchResultHeader: SearchResultHeaderModel? = nil + var searchResultEmptyCase: SearchResultModel.EmptyCase? @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? @@ -379,15 +379,15 @@ private extension PopupSearchReactor { func makeSearchResultHeaderInput( keyword afterTitle: String? = nil, count: Int64, - filter filterTitle: String? = Filter.shared.title) -> SearchResultHeaderView.Input { - return SearchResultHeaderView.Input( + filter filterTitle: String? = Filter.shared.title) -> SearchResultHeaderModel { + return SearchResultHeaderModel( title: afterTitle, count: Int(count), - filterStatusText: filterTitle + filterText: filterTitle ) } - func makeSearchResultEmptyCase(state: State) -> SearchResultEmptyCollectionViewCell.EmptyCase? { + func makeSearchResultEmptyCase(state: State) -> SearchResultModel.EmptyCase? { if !currentState.searchResultItems.isEmpty { return nil } else if currentState.isSearching { return .keyword } else { return .option } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index a456283a..b7abee67 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -21,7 +21,7 @@ final class PopupSearchView: UIView { case recentSearchItem(TagModel) case categoryItem(TagModel) case searchResultItem(SearchResultModel) - case searchResultEmptyItem(SearchResultEmptyCollectionViewCell.EmptyCase) + case searchResultEmptyItem(SearchResultModel.EmptyCase) } /// Section의 헤더를 구분하기 위한 변수 @@ -88,7 +88,7 @@ final class PopupSearchView: UIView { } private var dataSource: UICollectionViewDiffableDataSource? - private var searchResultHeaderInput: SearchResultHeaderView.Input? + private var searchResultHeaderInput: SearchResultHeaderModel? // MARK: - init init() { @@ -184,7 +184,7 @@ extension PopupSearchView { withReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers, for: indexPath ) as! SearchResultEmptyCollectionViewCell - cell.injection(with: SearchResultEmptyCollectionViewCell.Input(emptyCase: emptyCase)) + cell.configureCell(with: emptyCase) return cell } } @@ -228,11 +228,9 @@ extension PopupSearchView { ) as? SearchResultHeaderView else { fatalError("\(#file), \(#function) Error") } if let input = self.searchResultHeaderInput { - header.injection(with: input) - } else { header.injection(with: SearchResultHeaderView.Input( - title: nil, - count: nil, filterStatusText: nil - )) + header.configureHeader(title: input.title, count: input.count, filterText: input.filterText) + } else { + header.configureHeader(title: nil, count: nil, filterText: nil) } header.filterStatusButton.rx.tap @@ -248,8 +246,8 @@ extension PopupSearchView { recentSearchItems: [SectionItem], categoryItems: [SectionItem], searchResultItems: [SectionItem], - headerInput searchResultHeaderInput: SearchResultHeaderView.Input? = nil, - searchResultEmpty: SearchResultEmptyCollectionViewCell.EmptyCase? = nil + headerInput searchResultHeaderInput: SearchResultHeaderModel? = nil, + searchResultEmpty: SearchResultModel.EmptyCase? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift index 841eadef..254970c8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift @@ -52,18 +52,9 @@ private extension SearchResultEmptyCollectionViewCell { func configureUI() { } } -extension SearchResultEmptyCollectionViewCell: Inputable { - enum EmptyCase { - case option - case keyword - } - - struct Input: Hashable { - let emptyCase: EmptyCase - } - - func injection(with input: Input) { - switch input.emptyCase { +extension SearchResultEmptyCollectionViewCell { + func configureCell(with emptyCase: SearchResultModel.EmptyCase) { + switch emptyCase { case .option: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" case .keyword: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift index ca515a7a..392d4c56 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift @@ -98,16 +98,10 @@ private extension SearchResultHeaderView { } } -extension SearchResultHeaderView: Inputable { - public struct Input { - let title: String? - let count: Int? - let filterStatusText: String? - } - - public func injection(with input: Input) { - if let afterSearchTitle = input.title, - let count = input.count { +extension SearchResultHeaderView { + func configureHeader(title: String?, count: Int?, filterText: String?) { + if let afterSearchTitle = title, + let count = count { filterStatusButton.isHidden = true afterSearchTitleLabel.isHidden = false afterSearchTitleLabel.text = afterSearchTitle + " 포함된 팝업" @@ -121,12 +115,11 @@ extension SearchResultHeaderView: Inputable { } } - } else if let count = input.count, - let filterStatusTitle = input.filterStatusText { + } else if let count, let filterText { filterStatusButton.isHidden = false afterSearchTitleLabel.isHidden = true cellCountLabel.text = "총 \(count)개" - filterStatusLabel.text = filterStatusTitle + filterStatusLabel.text = filterText self.isHidden = false afterSearchTitleLabel.snp.updateConstraints { make in From 64cf9e44716a6dd670cee83d48cfb44127db82f8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 19:37:42 +0900 Subject: [PATCH 328/393] =?UTF-8?q?refactor/#131:=20View=EA=B0=80=20?= =?UTF-8?q?=EB=AA=A8=EB=8D=B8=EC=9D=98=20Case=EB=A5=BC=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8F=84=EB=A1=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 --- .../Source/Reactor/PopupSearchReactor.swift | 26 +++++++++---------- .../View/PopupSearchLayoutFactory.swift | 2 +- .../Source/View/PopupSearchView.swift | 22 ++++++++-------- .../View/PopupSearchViewController.swift | 2 +- .../SearchResultEmptyCollectionViewCell.swift | 13 ++++------ .../Source/View/TagCollectionHeaderView.swift | 2 +- 6 files changed, 32 insertions(+), 35 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 54b8fe48..f5c6b376 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -47,7 +47,7 @@ public final class PopupSearchReactor: Reactor { case updateClearButtonIsHidden(to: Bool) case updateCurrentPage(to: Int32) case updateSearchingState(to: Bool) - case updateSearchResultEmptyCase + case updateSearchResultEmptyTitle case updateDataSource case present(target: PresentTarget) @@ -63,7 +63,7 @@ public final class PopupSearchReactor: Reactor { var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] var searchResultHeader: SearchResultHeaderModel? = nil - var searchResultEmptyCase: SearchResultModel.EmptyCase? + var searchResultEmptyTitle: String? @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? @@ -110,7 +110,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateDataSource) ]) } @@ -133,7 +133,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), .just(.updateDataSource) @@ -159,7 +159,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: false)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateSearchBar(to: nil)), .just(.updateEditingState), .just(.updateDataSource) @@ -215,7 +215,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateCurrentPage(to: 0)), .just(.updateSearchBar(to: keyword)), .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), .just(.updateDataSource) @@ -236,7 +236,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateDataSource) ]) } @@ -258,7 +258,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyCase), + .just(.updateSearchResultEmptyTitle), .just(.updateDataSource) ]) } @@ -298,8 +298,8 @@ public final class PopupSearchReactor: Reactor { case .updateSearchingState(let isSearching): newState.isSearching = isSearching - case .updateSearchResultEmptyCase: - newState.searchResultEmptyCase = makeSearchResultEmptyCase(state: newState) + case .updateSearchResultEmptyTitle: + newState.searchResultEmptyTitle = makeSearchResultEmptyTitle(state: newState) case .updateDataSource: newState.updateDataSource = () @@ -387,10 +387,10 @@ private extension PopupSearchReactor { ) } - func makeSearchResultEmptyCase(state: State) -> SearchResultModel.EmptyCase? { + func makeSearchResultEmptyTitle(state: State) -> String? { if !currentState.searchResultItems.isEmpty { return nil } - else if currentState.isSearching { return .keyword } - else { return .option } + else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } + else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } } /// 받침에 따라 이/가 를 판단해서 붙여준다. diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift index 08e2c95a..23b08450 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift @@ -24,7 +24,7 @@ final class PopupSearchLayoutFactory { case .searchResult: let sectionSnapshot = dataSource.snapshot(for: sectionType) let hasEmptyItem = sectionSnapshot.items.contains { item in - if case .searchResultEmptyItem = item { return true } + if case .searchResultEmptyTitle = item { return true } return false } return makeSearchResultSectionLayout( diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index b7abee67..345815b3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -21,7 +21,7 @@ final class PopupSearchView: UIView { case recentSearchItem(TagModel) case categoryItem(TagModel) case searchResultItem(SearchResultModel) - case searchResultEmptyItem(SearchResultModel.EmptyCase) + case searchResultEmptyTitle(String) } /// Section의 헤더를 구분하기 위한 변수 @@ -78,8 +78,8 @@ final class PopupSearchView: UIView { ) $0.register( - SearchResultEmptyCollectionViewCell.self, - forCellWithReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers + SearchResultEmptyTitleCollectionViewCell.self, + forCellWithReuseIdentifier: SearchResultEmptyTitleCollectionViewCell.identifiers ) // UICollectionView 최 상/하단 빈 영역 @@ -179,12 +179,12 @@ extension PopupSearchView { cell.configureCell(imagePath: item.imagePath, id: item.id, category: item.category, title: item.title, address: item.address, startDate: item.startDate, endDate: item.endDate, isBookmark: item.isBookmark, isLogin: item.isLogin, isPopular: item.isPopular, row: item.row) return cell - case .searchResultEmptyItem(let emptyCase): + case .searchResultEmptyTitle(let title): let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers, + withReuseIdentifier: SearchResultEmptyTitleCollectionViewCell.identifiers, for: indexPath - ) as! SearchResultEmptyCollectionViewCell - cell.configureCell(with: emptyCase) + ) as! SearchResultEmptyTitleCollectionViewCell + cell.configureCell(title: title) return cell } } @@ -202,7 +202,7 @@ extension PopupSearchView { withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.rawValue, for: indexPath ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } - header.setupHeader(title: "최근 검색어", buttonTitle: "모두삭제") + header.configureHeader(title: "최근 검색어", buttonTitle: "모두삭제") header.removeAllButton.rx.tap .bind(to: self.recentSearchTagRemoveAllButtonTapped) @@ -216,7 +216,7 @@ extension PopupSearchView { withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue, for: indexPath ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } - header.setupHeader(title: "팝업스토어 찾기") + header.configureHeader(title: "팝업스토어 찾기") return header @@ -247,7 +247,7 @@ extension PopupSearchView { categoryItems: [SectionItem], searchResultItems: [SectionItem], headerInput searchResultHeaderInput: SearchResultHeaderModel? = nil, - searchResultEmpty: SearchResultModel.EmptyCase? = nil + searchResultEmpty: String? = nil ) { var snapshot = NSDiffableDataSourceSnapshot() @@ -265,7 +265,7 @@ extension PopupSearchView { self.searchResultHeaderInput = searchResultHeaderInput if let searchResultEmpty { - snapshot.appendItems([.searchResultEmptyItem(searchResultEmpty)], toSection: .searchResult) + snapshot.appendItems([.searchResultEmptyTitle(searchResultEmpty)], toSection: .searchResult) } else { snapshot.appendItems(searchResultItems, toSection: .searchResult) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index cbd03c65..82bd1798 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -187,7 +187,7 @@ extension PopupSearchViewController { categoryItems: state.categoryItems.map(PopupSearchView.SectionItem.categoryItem), searchResultItems: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), headerInput: state.searchResultHeader, - searchResultEmpty: state.searchResultEmptyCase + searchResultEmpty: state.searchResultEmptyTitle ) } .disposed(by: disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift index 254970c8..782189df 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift @@ -6,7 +6,7 @@ import Infrastructure import SnapKit import Then -final class SearchResultEmptyCollectionViewCell: UICollectionViewCell { +final class SearchResultEmptyTitleCollectionViewCell: UICollectionViewCell { // MARK: - Properties private let emptyLabel = PPLabel( @@ -34,7 +34,7 @@ final class SearchResultEmptyCollectionViewCell: UICollectionViewCell { } // MARK: - SetUp -private extension SearchResultEmptyCollectionViewCell { +private extension SearchResultEmptyTitleCollectionViewCell { func addViews() { [emptyLabel].forEach { self.addSubview($0) @@ -52,12 +52,9 @@ private extension SearchResultEmptyCollectionViewCell { func configureUI() { } } -extension SearchResultEmptyCollectionViewCell { - func configureCell(with emptyCase: SearchResultModel.EmptyCase) { - switch emptyCase { - case .option: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" - case .keyword: self.emptyLabel.text = "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" - } +extension SearchResultEmptyTitleCollectionViewCell { + func configureCell(title: String) { + self.emptyLabel.text = title } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift index 892c9faa..1fdad74a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift @@ -66,7 +66,7 @@ private extension TagCollectionHeaderView { } extension TagCollectionHeaderView { - func setupHeader(title: String, buttonTitle: String? = nil) { + func configureHeader(title: String, buttonTitle: String? = nil) { sectionTitleLabel.text = title if let buttonTitle = buttonTitle { removeAllButton.isHidden = false From 538431fbf1c276e71b41be95ca39cd5f257b5abf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 19:50:38 +0900 Subject: [PATCH 329/393] =?UTF-8?q?fix/#131:=20=ED=95=84=ED=84=B0=EA=B0=80?= =?UTF-8?q?=20=EB=B0=94=EB=80=8C=EB=A9=B4=20=EB=AC=B4=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=A0=80=EC=9E=A5=20=EB=B2=84=ED=8A=BC=EC=9D=B4=20=EB=9C=A8?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 25 ++++++------------- .../SearchFeature/Source/Model/Filter.swift | 2 +- .../Source/Reactor/FilterSelectReactor.swift | 1 + 3 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 2ff75c52..aea985b2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 058AE08D2DCCC27F009119B2 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 058AE08C2DCCC27F009119B2 /* Then */; }; 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBE82DCB90070051129F /* RxSwift */; }; @@ -29,7 +30,6 @@ 05EC234B2DC49AB400C761A5 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234A2DC49AB400C761A5 /* Then */; }; 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC234D2DC49AC100C761A5 /* SnapKit */; }; 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; - 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC28E32DC696E000C761A5 /* PanModal */; }; 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC2AE82DC7C07400C761A5 /* RxRelay */; }; /* End PBXBuildFile section */ @@ -109,7 +109,6 @@ 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, - 05EC28E42DC696E000C761A5 /* PanModal in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, @@ -125,6 +124,7 @@ 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, 05CFFBEA2DCB90210051129F /* Data.framework in Frameworks */, 05CFFBF32DCB906F0051129F /* RxCocoa in Frameworks */, + 058AE08D2DCCC27F009119B2 /* Then in Frameworks */, 05CFFBF02DCB90580051129F /* Infrastructure.framework in Frameworks */, 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */, 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */, @@ -203,7 +203,6 @@ 05EC23402DC49A8B00C761A5 /* ReactorKit */, 05EC234A2DC49AB400C761A5 /* Then */, 05EC234D2DC49AC100C761A5 /* SnapKit */, - 05EC28E32DC696E000C761A5 /* PanModal */, 05EC2AE82DC7C07400C761A5 /* RxRelay */, ); productName = SearchFeature; @@ -232,6 +231,7 @@ 05CFFBE82DCB90070051129F /* RxSwift */, 05CFFBF22DCB906F0051129F /* RxCocoa */, 05CFFBF62DCB90A10051129F /* SnapKit */, + 058AE08C2DCCC27F009119B2 /* Then */, ); productName = SearchFeatureDemo; productReference = 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */; @@ -270,7 +270,6 @@ 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */, 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */, 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */, - 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */, ); preferredProjectObjectVersion = 77; productRefGroup = 0516336E2DC457A900A6C0D1 /* Products */; @@ -671,17 +670,14 @@ minimumVersion = 5.7.1; }; }; - 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/slackhq/PanModal"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.2.7; - }; - }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 058AE08C2DCCC27F009119B2 /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; 05CFFBE82DCB90070051129F /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; @@ -722,11 +718,6 @@ package = 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - 05EC28E32DC696E000C761A5 /* PanModal */ = { - isa = XCSwiftPackageProductDependency; - package = 05EC28E22DC696E000C761A5 /* XCRemoteSwiftPackageReference "PanModal" */; - productName = PanModal; - }; 05EC2AE82DC7C07400C761A5 /* RxRelay */ = { isa = XCSwiftPackageProductDependency; package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift index 33cad9b7..535ff0ec 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift @@ -9,7 +9,7 @@ final class Filter: NSCopying, Equatable { ) } - static func == (lhs: Filter, rhs: Filter) -> Bool { return lhs === rhs } + static func == (lhs: Filter, rhs: Filter) -> Bool { return (lhs.status == rhs.status) && (lhs.sort == rhs.sort) } static let shared = Filter(status: .open, sort: .newest) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift index 196b1e5f..8077e8d1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift @@ -96,6 +96,7 @@ final class FilterSelectReactor: Reactor { newState.selectedFilter.sort = sort case .updateSaveButtonEnable: + print("DEBUG: status: \(Filter.shared.status), sort: \(Filter.shared.sort)", ) newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) case .saveCurrentFilter: From 79424c0e9059a917c4a1d2fd0936cacefff4a491 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 19:58:02 +0900 Subject: [PATCH 330/393] =?UTF-8?q?refactor/#131:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=EC=9D=98=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=88=9C?= =?UTF-8?q?=EC=84=9C=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 121 ++++++++++-------- ...hResultEmptyTitleCollectionViewCell.swift} | 0 2 files changed, 68 insertions(+), 53 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{SearchResultEmptyCollectionViewCell.swift => SearchResultEmptyTitleCollectionViewCell.swift} (100%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index f5c6b376..1d283356 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -146,6 +146,12 @@ public final class PopupSearchReactor: Reactor { .just(.updateEditingState) ]) + case .searchBarClearButtonTapped: + return Observable.concat([ + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateSearchBar(to: nil)) + ]) + case .searchBarCancelButtonTapped: if currentState.isSearching { return fetchSearchResult() @@ -168,36 +174,6 @@ public final class PopupSearchReactor: Reactor { } else { return .empty() } // TODO: 이전 화면으로 보내기 - case .recentSearchTagRemoveButtonTapped(let text): - self.removeRecentSearchItem(text: text) - return Observable.concat([ - .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateDataSource) - ]) - - case .recentSearchTagRemoveAllButtonTapped: - self.removeAllRecentSearchItems() - return .concat([ - .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateDataSource) - ]) - - - case .searchResultPrefetchItems(let indexPathList): - guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } - return fetchSearchResult(page: currentState.currentPage + 1) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return .concat([ - .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), - .just(.updateDataSource) - ]) - } - - case .categoryTagButtonTapped: - return .just(.present(target: .categorySelector)) - case .recentSearchTagButtonTapped(let indexPath): let keyword = self.makeRecentSearchItem(at: indexPath) return fetchSearchResult(keyword: keyword) @@ -222,10 +198,40 @@ public final class PopupSearchReactor: Reactor { ]) } - case .searchResultItemTapped: - return .empty() + case .recentSearchTagRemoveButtonTapped(let text): + self.removeRecentSearchItem(text: text) + return Observable.concat([ + .just(.setupRecentSearch(items: self.makeRecentSearchItems())), + .just(.updateDataSource) + ]) - case .searchResultFilterChangedBySelector, .categoryChangedBySelector: + case .recentSearchTagRemoveAllButtonTapped: + self.removeAllRecentSearchItems() + return .concat([ + .just(.setupRecentSearch(items: self.makeRecentSearchItems())), + .just(.updateDataSource) + ]) + + case .categoryTagRemoveButtonTapped(let categoryID): + self.removeCategoryItem(by: categoryID) + return fetchSearchResult() + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return Observable.concat([ + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), + .just(.updateSearchResultEmptyTitle), + .just(.updateDataSource) + ]) + } + + case .categoryTagButtonTapped: + return .just(.present(target: .categorySelector)) + + case .categoryChangedBySelector: return fetchSearchResult() .withUnretained(self) .flatMap { (owner, response) -> Observable in @@ -241,18 +247,15 @@ public final class PopupSearchReactor: Reactor { ]) } - case .searchBarClearButtonTapped: - return Observable.concat([ - .just(.updateClearButtonIsHidden(to: true)), - .just(.updateSearchBar(to: nil)) - ]) + case .searchResultFilterButtonTapped: + return .just(.present(target: .filterSelector)) - case .categoryTagRemoveButtonTapped(let categoryID): - self.removeCategoryItem(by: categoryID) + case .searchResultFilterChangedBySelector: return fetchSearchResult() .withUnretained(self) .flatMap { (owner, response) -> Observable in - return Observable.concat([ + return .concat([ + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), .just(.setupCategory(items: owner.makeCategoryItems())), .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), @@ -261,10 +264,22 @@ public final class PopupSearchReactor: Reactor { .just(.updateSearchResultEmptyTitle), .just(.updateDataSource) ]) - } + } - case .searchResultFilterButtonTapped: - return .just(.present(target: .filterSelector)) + case .searchResultItemTapped: + return .empty() + + case .searchResultPrefetchItems(let indexPathList): + guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } + return fetchSearchResult(page: currentState.currentPage + 1) + .withUnretained(self) + .flatMap { (owner, response) -> Observable in + return .concat([ + .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), + .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), + .just(.updateDataSource) + ]) + } } } @@ -280,18 +295,24 @@ public final class PopupSearchReactor: Reactor { case .setupSearchResult(let items): newState.searchResultItems = items + case .setupSearchResultHeader(let input): + newState.searchResultHeader = input + case .setupSearchResultTotalPageCount(let count): newState.totalPagesCount = count - case .setupSearchResultHeader(let input): - newState.searchResultHeader = input - case .appendSearchResult(let items): newState.searchResultItems += items + case .updateEditingState: + newState.endEditing = () + case .updateSearchBar(let text): newState.searchBarText = text + case .updateClearButtonIsHidden(let state): + newState.clearButtonIsHidden = state + case .updateCurrentPage(let currentPage): newState.currentPage = currentPage @@ -306,12 +327,6 @@ public final class PopupSearchReactor: Reactor { case .present(let target): newState.present = target - - case .updateClearButtonIsHidden(let state): - newState.clearButtonIsHidden = state - - case .updateEditingState: - newState.endEditing = () } return newState diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyCollectionViewCell.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift From f436601d62ece540a372da790006bdcfc1d6856e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 21:04:44 +0900 Subject: [PATCH 331/393] =?UTF-8?q?fix/#131:=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EA=B2=80=ED=86=A0=20=ED=9B=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PPTagCollectionViewCell/TagCollectionViewCell.swift | 7 ++++--- .../CategorySelectModal/CategorySelectViewController.swift | 2 +- .../View/SearchResultEmptyTitleCollectionViewCell.swift | 3 ++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift index 61a0980e..2bfdda5e 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift @@ -76,21 +76,22 @@ private extension TagCollectionViewCell { } extension TagCollectionViewCell { - public func configureCell(title: String? = nil, id: Int?, isSelected: Bool = false, isCancelable: Bool = true) { + public func configureCell(title: String? = nil, id: Int?, isSelected: Bool = false, isCancelable: Bool = true, fontSize: CGFloat = 11, cornerRadius: CGFloat = 15.5) { let xmarkImage = isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") cancelButton.setImage(xmarkImage, for: .normal) if isSelected { contentView.backgroundColor = .blu500 - titleLabel.setLineHeightText(text: title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: title, font: .korFont(style: .medium, size: fontSize), lineHeight: 1.15) titleLabel.textColor = .w100 contentView.layer.borderColor = UIColor.blu500.cgColor } else { contentView.backgroundColor = .clear - titleLabel.setLineHeightText(text: title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) + titleLabel.setLineHeightText(text: title, font: .korFont(style: .medium, size: fontSize), lineHeight: 1.15) titleLabel.textColor = .g400 contentView.layer.borderColor = UIColor.g200.cgColor } cancelButton.isHidden = !isCancelable + contentView.layer.cornerRadius = cornerRadius if isCancelable { contentStackView.snp.updateConstraints { make in diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index a424ad49..331c7d1f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -74,7 +74,7 @@ extension CategorySelectViewController { cellIdentifier: TagCollectionViewCell.identifiers, cellType: TagCollectionViewCell.self )) { _, item, cell in - cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) + cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable, fontSize: 13, cornerRadius: 18) } .disposed(by: disposeBag) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift index 782189df..2750af3b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift @@ -11,7 +11,8 @@ final class SearchResultEmptyTitleCollectionViewCell: UICollectionViewCell { // MARK: - Properties private let emptyLabel = PPLabel( style: .medium, - fontSize: 14 + fontSize: 14, + lineHeight: 1.5 ).then { $0.textAlignment = .center $0.numberOfLines = 2 From d8a8676a2a8137217071a460cb61555f792d40ee Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 23:21:48 +0900 Subject: [PATCH 332/393] =?UTF-8?q?feat/#131:=20=EC=BD=94=EB=94=94?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=ED=84=B0=20=EB=AA=A8=EB=93=88=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents.xcworkspacedata | 3 + .../CoordinatorKit.xcodeproj/project.pbxproj | 358 ++++++++++++++++++ .../Source/BaseCoordinator.swift | 11 + .../CoordinatorKit/Source/Coordinator.swift | 6 + 4 files changed, 378 insertions(+) create mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj create mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift create mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata index 67537ffe..61f625a3 100644 --- a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -21,6 +21,9 @@ + + diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj new file mode 100644 index 00000000..177d9ed1 --- /dev/null +++ b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj @@ -0,0 +1,358 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXFileReference section */ + 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 058AE09A2DCCE23B009119B2 /* CoordinatorKit */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = CoordinatorKit; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 058AE0952DCCE23B009119B2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058AE08E2DCCE23B009119B2 = { + isa = PBXGroup; + children = ( + 058AE09A2DCCE23B009119B2 /* CoordinatorKit */, + 058AE0992DCCE23B009119B2 /* Products */, + ); + sourceTree = ""; + }; + 058AE0992DCCE23B009119B2 /* Products */ = { + isa = PBXGroup; + children = ( + 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 058AE0932DCCE23B009119B2 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 058AE0972DCCE23B009119B2 /* CoordinatorKit */ = { + isa = PBXNativeTarget; + buildConfigurationList = 058AE09F2DCCE23B009119B2 /* Build configuration list for PBXNativeTarget "CoordinatorKit" */; + buildPhases = ( + 058AE0932DCCE23B009119B2 /* Headers */, + 058AE0942DCCE23B009119B2 /* Sources */, + 058AE0952DCCE23B009119B2 /* Frameworks */, + 058AE0962DCCE23B009119B2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 058AE09A2DCCE23B009119B2 /* CoordinatorKit */, + ); + name = CoordinatorKit; + packageProductDependencies = ( + ); + productName = CoordinatorKit; + productReference = 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 058AE08F2DCCE23B009119B2 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 058AE0972DCCE23B009119B2 = { + CreatedOnToolsVersion = 16.3; + LastSwiftMigration = 1630; + }; + }; + }; + buildConfigurationList = 058AE0922DCCE23B009119B2 /* Build configuration list for PBXProject "CoordinatorKit" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 058AE08E2DCCE23B009119B2; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 058AE0992DCCE23B009119B2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 058AE0972DCCE23B009119B2 /* CoordinatorKit */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 058AE0962DCCE23B009119B2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 058AE0942DCCE23B009119B2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 058AE09D2DCCE23B009119B2 /* 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.4; + 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; + }; + 058AE09E2DCCE23B009119B2 /* 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.4; + 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; + }; + 058AE0A02DCCE23B009119B2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + 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"; + 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.CoordinatorKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 058AE0A12DCCE23B009119B2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + 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"; + 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.CoordinatorKit; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_MODULE = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 058AE0922DCCE23B009119B2 /* Build configuration list for PBXProject "CoordinatorKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058AE09D2DCCE23B009119B2 /* Debug */, + 058AE09E2DCCE23B009119B2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 058AE09F2DCCE23B009119B2 /* Build configuration list for PBXNativeTarget "CoordinatorKit" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 058AE0A02DCCE23B009119B2 /* Debug */, + 058AE0A12DCCE23B009119B2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 058AE08F2DCCE23B009119B2 /* Project object */; +} diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift new file mode 100644 index 00000000..f732625c --- /dev/null +++ b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift @@ -0,0 +1,11 @@ +import Foundation + +open class BaseCoordinator: Coordinator { + public var children = [Coordinator]() + open func start() { fatalError("\(#file), \(#function) Error") } + + public init() { } + + func add(child: Coordinator) { children.append(child) } + func remove(child: Coordinator) { children.removeAll { $0 === child} } +} diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift new file mode 100644 index 00000000..2e6d45d9 --- /dev/null +++ b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift @@ -0,0 +1,6 @@ +import Foundation + +public protocol Coordinator: AnyObject { + var children: [Coordinator] { get set } + func start() +} From 069bbfd808afd6fd7c9117612a56a4dca74b0b73 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 8 May 2025 23:22:09 +0900 Subject: [PATCH 333/393] =?UTF-8?q?feat/#131:=20SearchFeature=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EB=82=B4=EB=B6=80=20=EC=A0=84=ED=99=98=EC=9D=84=20?= =?UTF-8?q?=EC=BD=94=EB=94=94=EB=84=A4=EC=9D=B4=ED=84=B0=EC=97=90=EA=B2=8C?= =?UTF-8?q?=20=EC=A0=84=EB=8B=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 10 +++ .../SearchFeatureCoordinator.swift | 78 +++++++++++++++++++ .../View/PopupSearchViewController.swift | 25 +----- .../SearchFeatureDemo/App/SceneDelegate.swift | 15 ++-- 4 files changed, 100 insertions(+), 28 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index aea985b2..02ea68e3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -8,6 +8,9 @@ /* Begin PBXBuildFile section */ 058AE08D2DCCC27F009119B2 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 058AE08C2DCCC27F009119B2 /* Then */; }; + 058AE0AE2DCCE3E1009119B2 /* CoordinatorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */; }; + 058AE0B22DCCE3E9009119B2 /* CoordinatorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */; }; + 058AE0B32DCCE3E9009119B2 /* CoordinatorKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBE82DCB90070051129F /* RxSwift */; }; @@ -50,6 +53,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 058AE0B32DCCE3E9009119B2 /* CoordinatorKit.framework in Embed Frameworks */, 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */, 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */, 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */, @@ -65,6 +69,8 @@ /* Begin PBXFileReference section */ 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23462DC49AA800C761A5 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC28642DC5FDF800C761A5 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -110,6 +116,7 @@ 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, + 058AE0AE2DCCE3E1009119B2 /* CoordinatorKit.framework in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); @@ -119,6 +126,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 058AE0B22DCCE3E9009119B2 /* CoordinatorKit.framework in Frameworks */, 05CFFBEE2DCB90460051129F /* DomainInterface.framework in Frameworks */, 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */, 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, @@ -157,6 +165,8 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */, + 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */, 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */, 05EC286A2DC5FE5B00C761A5 /* Data.framework */, 05EC28642DC5FDF800C761A5 /* Domain.framework */, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift new file mode 100644 index 00000000..17c69193 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift @@ -0,0 +1,78 @@ +import UIKit + +import CoordinatorKit +import DomainInterface +import DesignSystem // PPModalPresent 사용 용도 +import Infrastructure + +import ReactorKit +import RxSwift + +public final class SearchFeatureCoordinator: BaseCoordinator { + private let navigationController: UINavigationController + private let disposeBag = DisposeBag() + + public init(navigationController: UINavigationController) { + self.navigationController = navigationController + super.init() + } + + deinit { + print("DEBUG: \(#file) deinitialized") + } + + public override func start() { + let reactor = PopupSearchReactor( + popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) + ) + + let viewController = PopupSearchViewController() + + viewController.reactor = reactor + viewController.coordinator = self + print("DEBUG: start and coordinator is \(viewController.coordinator == nil)") + + navigationController.setNavigationBarHidden(true, animated: false) + navigationController.pushViewController(viewController, animated: true) + } + + func presentCategorySelector( + from parentViewController: UIViewController, + parentReactor: PopupSearchReactor + ) { + let reactor = CategorySelectReactor( + fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) + ) + + let viewController = CategorySelectViewController() + + viewController.reactor = reactor + + reactor.state.distinctUntilChanged(\.selectedCategoryChanged) + .filter { $0.selectedCategoryChanged == true } + .map { _ in PopupSearchReactor.Action.categoryChangedBySelector } + .bind(to: parentReactor.action) + .disposed(by: disposeBag) + + parentViewController.PPPresent(viewController) + } + + func presentFilterSelector( + from parentViewController: UIViewController, + parentReactor: PopupSearchReactor + ) { + let reactor = FilterSelectReactor() + + let viewController = FilterSelectViewController() + + viewController.reactor = reactor + + reactor.pulse(\.$saveButtonTapped) + .map { _ in PopupSearchReactor.Action.searchResultFilterChangedBySelector } + .bind(to: parentReactor.action) + .disposed(by: disposeBag) + + parentViewController.PPPresent(viewController) + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 82bd1798..704b7e4d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -14,6 +14,8 @@ public final class PopupSearchViewController: BaseViewController, View { public typealias Reactor = PopupSearchReactor // MARK: - Properties + public weak var coordinator: SearchFeatureCoordinator! + public var disposeBag = DisposeBag() private let mainView = PopupSearchView() @@ -144,29 +146,10 @@ extension PopupSearchViewController { .subscribe { owner, target in switch target { case .categorySelector: - let viewController = CategorySelectViewController() - viewController.reactor = CategorySelectReactor( - fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) - ) - - viewController.reactor?.state.distinctUntilChanged(\.selectedCategoryChanged) - .filter { $0.selectedCategoryChanged == true } - .map { _ in Reactor.Action.categoryChangedBySelector } - .bind(to: reactor.action) - .disposed(by: owner.disposeBag) - - owner.PPPresent(viewController) + owner.coordinator?.presentCategorySelector(from: owner, parentReactor: reactor) case .filterSelector: - let viewController = FilterSelectViewController() - viewController.reactor = FilterSelectReactor() - - viewController.reactor?.pulse(\.$saveButtonTapped) - .map { _ in Reactor.Action.searchResultFilterChangedBySelector } - .bind(to: reactor.action) - .disposed(by: owner.disposeBag) - - owner.PPPresent(viewController) + owner.coordinator?.presentFilterSelector(from: owner, parentReactor: reactor) default: break } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index 134a042d..5c294353 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -3,23 +3,24 @@ import UIKit import Domain import DomainInterface import Infrastructure - import SearchFeature class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? + private var coordinator: SearchFeatureCoordinator? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - let vc = PopupSearchViewController() - vc.reactor = PopupSearchReactor( - popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) - ) - window?.rootViewController = vc + + let navigationController = UINavigationController() + + coordinator = SearchFeatureCoordinator(navigationController: navigationController) + coordinator?.start() + + window?.rootViewController = navigationController window?.makeKeyAndVisible() } } From 74b42eb33167e54ddfbf611bf2b929f613071ee8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 00:36:58 +0900 Subject: [PATCH 334/393] =?UTF-8?q?refactor/#131:=20=ED=99=94=EB=A9=B4=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=EC=9D=84=20=EC=9C=84=ED=95=9C=20Coordinator?= =?UTF-8?q?=EB=A5=BC=20search=EC=97=90=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/{ => Coordinator}/BaseCoordinator.swift | 0 .../Source/{ => Coordinator}/Coordinator.swift | 0 .../Source/Reactor/PopupSearchReactor.swift | 11 ++++++++--- .../SearchFeature/Source/View/PopupSearchView.swift | 1 + .../Source/View/PopupSearchViewController.swift | 12 +++++++++--- 5 files changed, 18 insertions(+), 6 deletions(-) rename Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/{ => Coordinator}/BaseCoordinator.swift (100%) rename Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/{ => Coordinator}/Coordinator.swift (100%) diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift similarity index 100% rename from Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/BaseCoordinator.swift rename to Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift similarity index 100% rename from Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator.swift rename to Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 1d283356..9877f313 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -29,7 +29,7 @@ public final class PopupSearchReactor: Reactor { case searchResultFilterButtonTapped case searchResultFilterChangedBySelector - case searchResultItemTapped + case searchResultItemTapped(indexPath: IndexPath) case searchResultPrefetchItems(indexPathList: [IndexPath]) } @@ -56,6 +56,7 @@ public final class PopupSearchReactor: Reactor { public enum PresentTarget { case categorySelector case filterSelector + case popupDetail(popupID: Int) } public struct State { @@ -266,8 +267,8 @@ public final class PopupSearchReactor: Reactor { ]) } - case .searchResultItemTapped: - return .empty() + case .searchResultItemTapped(let indexPath): + return .just(.present(target: .popupDetail(popupID: self.findPopupStoreID(at: indexPath)))) case .searchResultPrefetchItems(let indexPathList): guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } @@ -391,6 +392,10 @@ private extension PopupSearchReactor { } } + func findPopupStoreID(at indexPath: IndexPath) -> Int { + return Int(currentState.searchResultItems[indexPath.item].id) + } + func makeSearchResultHeaderInput( keyword afterTitle: String? = nil, count: Int64, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 345815b3..b39a95aa 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -177,6 +177,7 @@ extension PopupSearchView { for: indexPath ) as! PPPopupGridCollectionViewCell cell.configureCell(imagePath: item.imagePath, id: item.id, category: item.category, title: item.title, address: item.address, startDate: item.startDate, endDate: item.endDate, isBookmark: item.isBookmark, isLogin: item.isLogin, isPopular: item.isPopular, row: item.row) + return cell case .searchResultEmptyTitle(let title): diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 704b7e4d..f1f3716e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -1,5 +1,6 @@ import UIKit +import CoordinatorKit import DesignSystem import DomainInterface import Infrastructure @@ -14,7 +15,7 @@ public final class PopupSearchViewController: BaseViewController, View { public typealias Reactor = PopupSearchReactor // MARK: - Properties - public weak var coordinator: SearchFeatureCoordinator! + public weak var coordinator: SearchFeatureCoordinator? public var disposeBag = DisposeBag() @@ -104,8 +105,10 @@ extension PopupSearchViewController { switch sections[indexPath.section] { case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped(indexPath: indexPath) - case .category: return Reactor.Action.categoryTagButtonTapped - case .searchResult: return Reactor.Action.searchResultItemTapped + case .category: + return Reactor.Action.categoryTagButtonTapped + case .searchResult: + return Reactor.Action.searchResultItemTapped(indexPath: indexPath) } } .bind(to: reactor.action) @@ -151,6 +154,9 @@ extension PopupSearchViewController { case .filterSelector: owner.coordinator?.presentFilterSelector(from: owner, parentReactor: reactor) + case .popupDetail(let popupID): + print("DEBUG: PopupStore ID is \(popupID)") + default: break } } From f6c8757ff830a97b553e018d1bf99fd4b9209a04 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 00:48:50 +0900 Subject: [PATCH 335/393] =?UTF-8?q?fix/#131:=20inset=EC=9D=84=20=EB=91=90?= =?UTF-8?q?=EB=B2=88=20=EC=9E=A1=EC=95=98=EB=8D=98=20=EB=B6=80=EB=B6=84=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 --- .../SearchFeature/Source/View/PopupSearchView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index b39a95aa..50aee0a1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -118,7 +118,7 @@ private extension PopupSearchView { func setupConstraints() { searchBar.snp.makeConstraints { make in - make.top.equalTo(self.safeAreaLayoutGuide).inset(12) + make.top.equalTo(self.safeAreaLayoutGuide) make.horizontalEdges.equalToSuperview() make.height.equalTo(56) } From 578377773cc9ff9785e5ba3c5edb252fc9a37b6d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 17:19:41 +0900 Subject: [PATCH 336/393] =?UTF-8?q?refactor/#131:=20=EC=BD=94=EB=94=94?= =?UTF-8?q?=EB=84=A4=EC=9D=B4=ED=84=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contents.xcworkspacedata | 3 - .../CoordinatorKit.xcodeproj/project.pbxproj | 358 ------------------ .../Source/Coordinator/BaseCoordinator.swift | 11 - .../Source/Coordinator/Coordinator.swift | 6 - .../SearchFeature.xcodeproj/project.pbxproj | 6 - .../SearchFeatureCoordinator.swift | 78 ---- 6 files changed, 462 deletions(-) delete mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj delete mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift delete mode 100644 Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift delete mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift diff --git a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata index 61f625a3..67537ffe 100644 --- a/Poppool/Poppool.xcworkspace/contents.xcworkspacedata +++ b/Poppool/Poppool.xcworkspace/contents.xcworkspacedata @@ -21,9 +21,6 @@ - - diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj deleted file mode 100644 index 177d9ed1..00000000 --- a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit.xcodeproj/project.pbxproj +++ /dev/null @@ -1,358 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXFileReference section */ - 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 058AE09A2DCCE23B009119B2 /* CoordinatorKit */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = CoordinatorKit; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 058AE0952DCCE23B009119B2 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 058AE08E2DCCE23B009119B2 = { - isa = PBXGroup; - children = ( - 058AE09A2DCCE23B009119B2 /* CoordinatorKit */, - 058AE0992DCCE23B009119B2 /* Products */, - ); - sourceTree = ""; - }; - 058AE0992DCCE23B009119B2 /* Products */ = { - isa = PBXGroup; - children = ( - 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */, - ); - name = Products; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - 058AE0932DCCE23B009119B2 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - 058AE0972DCCE23B009119B2 /* CoordinatorKit */ = { - isa = PBXNativeTarget; - buildConfigurationList = 058AE09F2DCCE23B009119B2 /* Build configuration list for PBXNativeTarget "CoordinatorKit" */; - buildPhases = ( - 058AE0932DCCE23B009119B2 /* Headers */, - 058AE0942DCCE23B009119B2 /* Sources */, - 058AE0952DCCE23B009119B2 /* Frameworks */, - 058AE0962DCCE23B009119B2 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 058AE09A2DCCE23B009119B2 /* CoordinatorKit */, - ); - name = CoordinatorKit; - packageProductDependencies = ( - ); - productName = CoordinatorKit; - productReference = 058AE0982DCCE23B009119B2 /* CoordinatorKit.framework */; - productType = "com.apple.product-type.framework"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 058AE08F2DCCE23B009119B2 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 1630; - LastUpgradeCheck = 1630; - TargetAttributes = { - 058AE0972DCCE23B009119B2 = { - CreatedOnToolsVersion = 16.3; - LastSwiftMigration = 1630; - }; - }; - }; - buildConfigurationList = 058AE0922DCCE23B009119B2 /* Build configuration list for PBXProject "CoordinatorKit" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 058AE08E2DCCE23B009119B2; - minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 77; - productRefGroup = 058AE0992DCCE23B009119B2 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 058AE0972DCCE23B009119B2 /* CoordinatorKit */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 058AE0962DCCE23B009119B2 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 058AE0942DCCE23B009119B2 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 058AE09D2DCCE23B009119B2 /* 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.4; - 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; - }; - 058AE09E2DCCE23B009119B2 /* 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.4; - 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; - }; - 058AE0A02DCCE23B009119B2 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - 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"; - 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.CoordinatorKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_MODULE = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 058AE0A12DCCE23B009119B2 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - BUILD_LIBRARY_FOR_DISTRIBUTION = YES; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - 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"; - 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.CoordinatorKit; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SKIP_INSTALL = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_MODULE = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 058AE0922DCCE23B009119B2 /* Build configuration list for PBXProject "CoordinatorKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 058AE09D2DCCE23B009119B2 /* Debug */, - 058AE09E2DCCE23B009119B2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 058AE09F2DCCE23B009119B2 /* Build configuration list for PBXNativeTarget "CoordinatorKit" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 058AE0A02DCCE23B009119B2 /* Debug */, - 058AE0A12DCCE23B009119B2 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 058AE08F2DCCE23B009119B2 /* Project object */; -} diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift deleted file mode 100644 index f732625c..00000000 --- a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/BaseCoordinator.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -open class BaseCoordinator: Coordinator { - public var children = [Coordinator]() - open func start() { fatalError("\(#file), \(#function) Error") } - - public init() { } - - func add(child: Coordinator) { children.append(child) } - func remove(child: Coordinator) { children.removeAll { $0 === child} } -} diff --git a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift b/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift deleted file mode 100644 index 2e6d45d9..00000000 --- a/Poppool/PresentationLayer/CoordinatorKit/CoordinatorKit/Source/Coordinator/Coordinator.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -public protocol Coordinator: AnyObject { - var children: [Coordinator] { get set } - func start() -} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 02ea68e3..624c76c3 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -8,9 +8,6 @@ /* Begin PBXBuildFile section */ 058AE08D2DCCC27F009119B2 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 058AE08C2DCCC27F009119B2 /* Then */; }; - 058AE0AE2DCCE3E1009119B2 /* CoordinatorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */; }; - 058AE0B22DCCE3E9009119B2 /* CoordinatorKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */; }; - 058AE0B32DCCE3E9009119B2 /* CoordinatorKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBE82DCB90070051129F /* RxSwift */; }; @@ -53,7 +50,6 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( - 058AE0B32DCCE3E9009119B2 /* CoordinatorKit.framework in Embed Frameworks */, 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */, 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */, 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */, @@ -116,7 +112,6 @@ 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, - 058AE0AE2DCCE3E1009119B2 /* CoordinatorKit.framework in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); @@ -126,7 +121,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 058AE0B22DCCE3E9009119B2 /* CoordinatorKit.framework in Frameworks */, 05CFFBEE2DCB90460051129F /* DomainInterface.framework in Frameworks */, 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */, 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift deleted file mode 100644 index 17c69193..00000000 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Coordinator/SearchFeatureCoordinator.swift +++ /dev/null @@ -1,78 +0,0 @@ -import UIKit - -import CoordinatorKit -import DomainInterface -import DesignSystem // PPModalPresent 사용 용도 -import Infrastructure - -import ReactorKit -import RxSwift - -public final class SearchFeatureCoordinator: BaseCoordinator { - private let navigationController: UINavigationController - private let disposeBag = DisposeBag() - - public init(navigationController: UINavigationController) { - self.navigationController = navigationController - super.init() - } - - deinit { - print("DEBUG: \(#file) deinitialized") - } - - public override func start() { - let reactor = PopupSearchReactor( - popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), - fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) - ) - - let viewController = PopupSearchViewController() - - viewController.reactor = reactor - viewController.coordinator = self - print("DEBUG: start and coordinator is \(viewController.coordinator == nil)") - - navigationController.setNavigationBarHidden(true, animated: false) - navigationController.pushViewController(viewController, animated: true) - } - - func presentCategorySelector( - from parentViewController: UIViewController, - parentReactor: PopupSearchReactor - ) { - let reactor = CategorySelectReactor( - fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) - ) - - let viewController = CategorySelectViewController() - - viewController.reactor = reactor - - reactor.state.distinctUntilChanged(\.selectedCategoryChanged) - .filter { $0.selectedCategoryChanged == true } - .map { _ in PopupSearchReactor.Action.categoryChangedBySelector } - .bind(to: parentReactor.action) - .disposed(by: disposeBag) - - parentViewController.PPPresent(viewController) - } - - func presentFilterSelector( - from parentViewController: UIViewController, - parentReactor: PopupSearchReactor - ) { - let reactor = FilterSelectReactor() - - let viewController = FilterSelectViewController() - - viewController.reactor = reactor - - reactor.pulse(\.$saveButtonTapped) - .map { _ in PopupSearchReactor.Action.searchResultFilterChangedBySelector } - .bind(to: parentReactor.action) - .disposed(by: disposeBag) - - parentViewController.PPPresent(viewController) - } -} From c58af0610f41b4b5ffb6998ce1c4f8ed1f4d288f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 17:27:11 +0900 Subject: [PATCH 337/393] =?UTF-8?q?reafactor/#131:=20FactoryPattern?= =?UTF-8?q?=EC=9D=84=20=EC=9D=B4=EC=9A=A9=ED=95=B4=EC=84=9C=20View?= =?UTF-8?q?=EB=A5=BC=20=EB=A7=8C=EB=93=A4=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 - view간 이벤트 전달을 위해 별도의 shared subject를 이용 - ViewController마다 factory를 만들어서 사용 - DIContainer에서 Factory의 Impl을 등록 및 resolve해서 사용 --- .../Extension/UIViewController+.swift | 3 +- .../SearchFeature.xcodeproj/project.pbxproj | 193 ++++++++++++++++++ .../CategorySelectorFactoryImpl.swift | 19 ++ .../FilterSelectorFactoryImpl.swift | 15 ++ .../FactoryImpl/PopupSearchFactoryImpl.swift | 19 ++ .../SearchFeature/Source/Model/Category.swift | 11 +- .../SearchFeature/Source/Model/Filter.swift | 3 + .../Reactor/CategorySelectReactor.swift | 5 +- .../Source/Reactor/FilterSelectReactor.swift | 5 +- .../CategorySelectViewController.swift | 5 + .../FilterSelectViewController.swift | 5 + .../View/PopupSearchViewController.swift | 25 ++- .../SearchFeatureDemo/App/AppDelegate.swift | 32 +-- .../SearchFeatureDemo/App/SceneDelegate.swift | 10 +- .../Factory/CategorySelectorFactory.swift | 7 + .../Factory/FilterSelectorFactory.swift | 7 + .../Source/Factory/PopupSearchFactory.swift | 7 + 17 files changed, 329 insertions(+), 42 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/CategorySelectorFactory.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/FilterSelectorFactory.swift create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/PopupSearchFactory.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift index e8e5387b..034c7b7e 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIViewController+.swift @@ -1,5 +1,6 @@ import UIKit -import ObjectiveC + +import Infrastructure private struct PPModalConstants { static let animationDuration: TimeInterval = 0.2 diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 624c76c3..02183c9e 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,6 +7,10 @@ objects = { /* Begin PBXBuildFile section */ + 05734C082DCDA7D20093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; + 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; }; + 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 058AE08D2DCCC27F009119B2 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 058AE08C2DCCC27F009119B2 /* Then */; }; 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -41,9 +45,38 @@ remoteGlobalIDString = 0516336C2DC457A900A6C0D1; remoteInfo = SearchFeature; }; + 05734C0A2DCDA7D20093825D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 051633642DC457A900A6C0D1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05734BF42DCDA6B90093825D; + remoteInfo = SearchFeatureInterface; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ + 05734C0C2DCDA7D20093825D /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; + 05734C2D2DCDD6390093825D /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 05CFFBE72DCB8F6C0051129F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -65,6 +98,9 @@ /* Begin PBXFileReference section */ 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734C232DCDD4CF0093825D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734C2A2DCDD6380093825D /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -99,6 +135,11 @@ path = SearchFeatureDemo; sourceTree = ""; }; + 05734BF62DCDA6B90093825D /* SearchFeatureInterface */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = SearchFeatureInterface; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -112,6 +153,7 @@ 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */, 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, + 05734C082DCDA7D20093825D /* SearchFeatureInterface.framework in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); @@ -134,6 +176,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 05734BF22DCDA6B90093825D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -142,6 +192,7 @@ children = ( 0516336F2DC457A900A6C0D1 /* SearchFeature */, 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */, + 05734BF62DCDA6B90093825D /* SearchFeatureInterface */, 0516364A2DC45E6300A6C0D1 /* Frameworks */, 0516336E2DC457A900A6C0D1 /* Products */, ); @@ -152,6 +203,7 @@ children = ( 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */, 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */, + 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */, ); name = Products; sourceTree = ""; @@ -159,6 +211,8 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 05734C2A2DCDD6380093825D /* Infrastructure.framework */, + 05734C232DCDD4CF0093825D /* DesignSystem.framework */, 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */, 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */, 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */, @@ -181,6 +235,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 05734BF02DCDA6B90093825D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ @@ -192,10 +253,12 @@ 051633692DC457A900A6C0D1 /* Sources */, 0516336A2DC457A900A6C0D1 /* Frameworks */, 0516336B2DC457A900A6C0D1 /* Resources */, + 05734C0C2DCDA7D20093825D /* Embed Frameworks */, ); buildRules = ( ); dependencies = ( + 05734C0B2DCDA7D20093825D /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 0516336F2DC457A900A6C0D1 /* SearchFeature */, @@ -241,6 +304,30 @@ productReference = 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */; productType = "com.apple.product-type.application"; }; + 05734BF42DCDA6B90093825D /* SearchFeatureInterface */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05734BF92DCDA6B90093825D /* Build configuration list for PBXNativeTarget "SearchFeatureInterface" */; + buildPhases = ( + 05734BF02DCDA6B90093825D /* Headers */, + 05734BF12DCDA6B90093825D /* Sources */, + 05734BF22DCDA6B90093825D /* Frameworks */, + 05734BF32DCDA6B90093825D /* Resources */, + 05734C2D2DCDD6390093825D /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 05734BF62DCDA6B90093825D /* SearchFeatureInterface */, + ); + name = SearchFeatureInterface; + packageProductDependencies = ( + ); + productName = SearchFeatureInterface; + productReference = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; + productType = "com.apple.product-type.framework"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -258,6 +345,9 @@ 0516362E2DC457DE00A6C0D1 = { CreatedOnToolsVersion = 16.3; }; + 05734BF42DCDA6B90093825D = { + CreatedOnToolsVersion = 16.3; + }; }; }; buildConfigurationList = 051633672DC457A900A6C0D1 /* Build configuration list for PBXProject "SearchFeature" */; @@ -281,6 +371,7 @@ projectRoot = ""; targets = ( 0516336C2DC457A900A6C0D1 /* SearchFeature */, + 05734BF42DCDA6B90093825D /* SearchFeatureInterface */, 0516362E2DC457DE00A6C0D1 /* SearchFeatureDemo */, ); }; @@ -301,6 +392,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 05734BF32DCDA6B90093825D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -318,6 +416,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 05734BF12DCDA6B90093825D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -326,6 +431,11 @@ target = 0516336C2DC457A900A6C0D1 /* SearchFeature */; targetProxy = 0516364D2DC45E6300A6C0D1 /* PBXContainerItemProxy */; }; + 05734C0B2DCDA7D20093825D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05734BF42DCDA6B90093825D /* SearchFeatureInterface */; + targetProxy = 05734C0A2DCDA7D20093825D /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -609,6 +719,80 @@ }; name = Release; }; + 05734BFA2DCDA6B90093825D /* 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.SearchFeatureInterface; + 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; + }; + 05734BFB2DCDA6B90093825D /* 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.SearchFeatureInterface; + 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; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -639,6 +823,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 05734BF92DCDA6B90093825D /* Build configuration list for PBXNativeTarget "SearchFeatureInterface" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05734BFA2DCDA6B90093825D /* Debug */, + 05734BFB2DCDA6B90093825D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift new file mode 100644 index 00000000..960f86bd --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift @@ -0,0 +1,19 @@ +import DesignSystem +import DomainInterface +import Infrastructure +import SearchFeatureInterface + +public final class CategorySelectorFactoryImpl: CategorySelectorFactory { + public init() { } + + public func make() -> BaseViewController & PPModalPresentable { + let reactor = CategorySelectReactor( + fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) + ) + let viewController = CategorySelectViewController() + + viewController.reactor = reactor + + return viewController + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift new file mode 100644 index 00000000..a526e33f --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift @@ -0,0 +1,15 @@ +import DesignSystem +import Infrastructure +import SearchFeatureInterface + +public final class FilterSelectorFactoryImpl: FilterSelectorFactory { + public init() { } + + public func make() -> BaseViewController & PPModalPresentable { + let reactor = FilterSelectReactor() + let viewController = FilterSelectViewController() + viewController.reactor = reactor + + return viewController + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift new file mode 100644 index 00000000..18fddd85 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift @@ -0,0 +1,19 @@ +import DesignSystem +import DomainInterface +import Infrastructure +import SearchFeatureInterface + +public final class PopupSearchFactoryImpl: PopupSearchFactory { + public init() { } + + public func make() -> BaseViewController { + let viewController = PopupSearchViewController() + + viewController.reactor = PopupSearchReactor( + popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) + ) + + return viewController + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift index d46f1da6..84ea400b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift @@ -1,17 +1,20 @@ import Foundation -public final class Category: NSCopying, Equatable { - public func copy(with zone: NSZone? = nil) -> Any { +import RxSwift + +final class Category: NSCopying, Equatable { + func copy(with zone: NSZone? = nil) -> Any { return Category(items: self.items) } - public static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } + static func == (lhs: Category, rhs: Category) -> Bool { return lhs === rhs } static let shared = Category() + static let valueChanged = PublishSubject() /// 선택된 아이템들만 들어가는 인스턴스 private var _items: [TagModel] - public var items: [TagModel] { + var items: [TagModel] { get { _items } set { _items = newValue.isEmpty ? [Category.defaultItem] : newValue } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift index 535ff0ec..a24591c1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift @@ -1,5 +1,7 @@ import Foundation +import RxSwift + /// 필터 옵션 상태를 공유하기 위한 싱글톤 객체 final class Filter: NSCopying, Equatable { func copy(with zone: NSZone? = nil) -> Any { @@ -12,6 +14,7 @@ final class Filter: NSCopying, Equatable { static func == (lhs: Filter, rhs: Filter) -> Bool { return (lhs.status == rhs.status) && (lhs.sort == rhs.sort) } static let shared = Filter(status: .open, sort: .newest) + static let valueChanged = PublishSubject() var status: PopupStatus = .open var sort: PopupSort = .newest diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift index a68d0ebe..802f05de 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift @@ -32,6 +32,7 @@ final class CategorySelectReactor: Reactor { var saveButtonIsEnable: Bool = false var selectedCategoryChanged: Bool? + @Pulse var categoryChanged: Void? @Pulse var dismiss: Void? } @@ -106,11 +107,11 @@ final class CategorySelectReactor: Reactor { case .resetCategory: Category.shared.resetItems() - newState.selectedCategoryChanged = true + newState.categoryChanged = () case .saveCategory: Category.shared.items = newState.categoryItems.filter { $0.isSelected == true } - newState.selectedCategoryChanged = true + newState.categoryChanged = () case .updateCategoryTagSelection(let categoryID): newState.categoryItems = state.categoryItems.map { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift index 8077e8d1..b0deb372 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift @@ -26,7 +26,7 @@ final class FilterSelectReactor: Reactor { var selectedFilter: Filter var saveButtonIsEnable: Bool = false - @Pulse var saveButtonTapped: Void? + @Pulse var filterChanged: Void? @Pulse var dismiss: Void? } @@ -96,13 +96,12 @@ final class FilterSelectReactor: Reactor { newState.selectedFilter.sort = sort case .updateSaveButtonEnable: - print("DEBUG: status: \(Filter.shared.status), sort: \(Filter.shared.sort)", ) newState.saveButtonIsEnable = (newState.selectedFilter != Filter.shared) case .saveCurrentFilter: Filter.shared.status = newState.selectedFilter.status Filter.shared.sort = newState.selectedFilter.sort - newState.saveButtonTapped = () + newState.filterChanged = () } return newState diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 331c7d1f..67d73d5b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -1,6 +1,7 @@ import UIKit import DesignSystem +import Infrastructure import ReactorKit import RxCocoa @@ -82,6 +83,10 @@ extension CategorySelectViewController { .withUnretained(self) .subscribe { (owner, state) in owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } .disposed(by: disposeBag) + + reactor.pulse(\.$categoryChanged) + .subscribe { _ in Category.valueChanged.onNext(()) } + .disposed(by: disposeBag) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index 0ce28688..a0369ade 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -1,6 +1,7 @@ import UIKit import DesignSystem +import Infrastructure import ReactorKit import RxCocoa @@ -79,6 +80,10 @@ extension FilterSelectViewController { .withUnretained(self) .subscribe { (owner, _) in owner.dismissModal() } .disposed(by: disposeBag) + + reactor.pulse(\.$filterChanged) + .subscribe { _ in Filter.valueChanged.onNext(()) } + .disposed(by: disposeBag) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index f1f3716e..1c96e5c5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -1,9 +1,9 @@ import UIKit -import CoordinatorKit import DesignSystem import DomainInterface import Infrastructure +import SearchFeatureInterface import ReactorKit import RxSwift @@ -15,8 +15,6 @@ public final class PopupSearchViewController: BaseViewController, View { public typealias Reactor = PopupSearchReactor // MARK: - Properties - public weak var coordinator: SearchFeatureCoordinator? - public var disposeBag = DisposeBag() private let mainView = PopupSearchView() @@ -119,11 +117,21 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + Category.valueChanged + .map { _ in Reactor.Action.categoryChangedBySelector } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.filterStatusButtonTapped .map { Reactor.Action.searchResultFilterButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) + Filter.valueChanged + .map { _ in Reactor.Action.searchResultFilterChangedBySelector } + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.collectionView.rx.prefetchItems .throttle(.milliseconds(100), latest: false, scheduler: MainScheduler.asyncInstance) .map(Reactor.Action.searchResultPrefetchItems) @@ -149,10 +157,17 @@ extension PopupSearchViewController { .subscribe { owner, target in switch target { case .categorySelector: - owner.coordinator?.presentCategorySelector(from: owner, parentReactor: reactor) + @Dependency var factory: CategorySelectorFactory + let viewController = factory.make() + + owner.PPPresent(viewController) case .filterSelector: - owner.coordinator?.presentFilterSelector(from: owner, parentReactor: reactor) + let viewController = FilterSelectViewController() + let filterReactor = FilterSelectReactor() + + viewController.reactor = filterReactor + owner.PPPresent(viewController) case .popupDetail(let popupID): print("DEBUG: PopupStore ID is \(popupID)") diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index b162e314..d2afba12 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -4,6 +4,8 @@ import Data import Domain import DomainInterface import Infrastructure +import SearchFeature +import SearchFeatureInterface @main class AppDelegate: UIResponder, UIApplicationDelegate { @@ -11,6 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { self.registerDependencies() + self.registerFactory() return true } @@ -28,58 +31,41 @@ extension AppDelegate { private func registerDependencies() { // MARK: Register Service DIContainer.register(Provider.self) { return ProviderImpl() } - DIContainer.register(KeyChainService.self) { return KeyChainService() } DIContainer.register(UserDefaultService.self) { return UserDefaultService() } + DIContainer.register(KeyChainService.self) { return KeyChainService() } // MARK: Resolve service @Dependency var provider: Provider @Dependency var userDefaultService: UserDefaultService // MARK: Register repository - DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } - DIContainer.register(AdminRepository.self) { return AdminRepositoryImpl(provider: provider) } DIContainer.register(UserAPIRepository.self) { return UserAPIRepositoryImpl(provider: provider) } DIContainer.register(PopUpAPIRepository.self) { return PopUpAPIRepositoryImpl(provider: provider) } DIContainer.register(CommentAPIRepository.self) { return CommentAPIRepositoryImpl(provider: provider) } - DIContainer.register(HomeAPIRepository.self) { return HomeAPIRepositoryImpl(provider: provider) } - DIContainer.register(AuthAPIRepository.self) { return AuthAPIRepositoryImpl(provider: provider) } - DIContainer.register(SignUpRepository.self) { return SignUpRepositoryImpl(provider: provider) } - DIContainer.register(MapDirectionRepository.self) { return MapDirectionRepositoryImpl(provider: provider) } DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } - DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } - DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } DIContainer.register(CategoryRepository.self) { return CategoryRepositoryImpl(provider: provider) } DIContainer.register(SearchAPIRepository.self) { return SearchAPIRepositoryImpl(provider: provider, userDefaultService: userDefaultService) } // MARK: Resolve repository - @Dependency var mapRepository: MapRepository - @Dependency var adminRepository: AdminRepository @Dependency var userAPIRepository: UserAPIRepository @Dependency var popUpAPIRepository: PopUpAPIRepository @Dependency var commentAPIRepository: CommentAPIRepository - @Dependency var homeAPIRepository: HomeAPIRepository - @Dependency var authAPIRepository: AuthAPIRepository - @Dependency var signUpRepository: SignUpRepository @Dependency var preSignedRepository: PreSignedRepository - @Dependency var kakaoLoginRepository: KakaoLoginRepository - @Dependency var appleLoginRepository: AppleLoginRepository @Dependency var categoryRepository: CategoryRepository @Dependency var searchAPIRepository: SearchAPIRepository // MARK: Register UseCase - DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } - DIContainer.register(AdminUseCase.self) { return AdminUseCaseImpl(repository: adminRepository) } DIContainer.register(UserAPIUseCase.self) { return UserAPIUseCaseImpl(repository: userAPIRepository) } DIContainer.register(PopUpAPIUseCase.self) { return PopUpAPIUseCaseImpl(repository: popUpAPIRepository) } DIContainer.register(CommentAPIUseCase.self) { return CommentAPIUseCaseImpl(repository: commentAPIRepository) } - DIContainer.register(HomeAPIUseCase.self) { return HomeAPIUseCaseImpl(repository: homeAPIRepository) } - DIContainer.register(AuthAPIUseCase.self) { return AuthAPIUseCaseImpl(repository: authAPIRepository) } - DIContainer.register(SignUpAPIUseCase.self) { return SignUpAPIUseCaseImpl(repository: signUpRepository) } DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } - DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } - DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } DIContainer.register(FetchCategoryListUseCase.self) { return FetchCategoryListUseCaseImpl(repository: categoryRepository) } DIContainer.register(FetchKeywordBasePopupListUseCase.self) { return FetchKeywordBasePopupListUseCaseImpl(repository: searchAPIRepository) } } + + private func registerFactory() { + DIContainer.register(PopupSearchFactory.self) { return PopupSearchFactoryImpl() } + DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } + } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index 5c294353..1d9f2290 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -3,12 +3,11 @@ import UIKit import Domain import DomainInterface import Infrastructure -import SearchFeature +import SearchFeatureInterface class SceneDelegate: UIResponder, UIWindowSceneDelegate { var window: UIWindow? - private var coordinator: SearchFeatureCoordinator? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } @@ -16,9 +15,12 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window = UIWindow(windowScene: windowScene) let navigationController = UINavigationController() + @Dependency var popupSearchFactory: PopupSearchFactory - coordinator = SearchFeatureCoordinator(navigationController: navigationController) - coordinator?.start() + navigationController.pushViewController( + popupSearchFactory.make(), + animated: false + ) window?.rootViewController = navigationController window?.makeKeyAndVisible() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/CategorySelectorFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/CategorySelectorFactory.swift new file mode 100644 index 00000000..5e4a1a14 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/CategorySelectorFactory.swift @@ -0,0 +1,7 @@ +import UIKit + +import DesignSystem + +public protocol CategorySelectorFactory { + func make() -> BaseViewController & PPModalPresentable +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/FilterSelectorFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/FilterSelectorFactory.swift new file mode 100644 index 00000000..415c8b00 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/FilterSelectorFactory.swift @@ -0,0 +1,7 @@ +import UIKit + +import DesignSystem + +public protocol FilterSelectorFactory { + func make() -> BaseViewController & PPModalPresentable +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/PopupSearchFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/PopupSearchFactory.swift new file mode 100644 index 00000000..0de8d2cf --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureInterface/Source/Factory/PopupSearchFactory.swift @@ -0,0 +1,7 @@ +import UIKit + +import DesignSystem + +public protocol PopupSearchFactory { + func make() -> BaseViewController +} From 2fd04213fd4531ae4cf8e9ac35c1f2c8cf14381f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 17:38:51 +0900 Subject: [PATCH 338/393] =?UTF-8?q?refactor/#131:=20=ED=95=84=ED=84=B0=20?= =?UTF-8?q?=EC=84=A0=ED=83=9D=20=ED=99=94=EB=A9=B4=20Factory=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 --- .../Source/View/PopupSearchViewController.swift | 11 +++-------- .../SearchFeatureDemo/App/AppDelegate.swift | 1 + 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 1c96e5c5..14a4570d 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -158,16 +158,11 @@ extension PopupSearchViewController { switch target { case .categorySelector: @Dependency var factory: CategorySelectorFactory - let viewController = factory.make() - - owner.PPPresent(viewController) + owner.PPPresent(factory.make()) case .filterSelector: - let viewController = FilterSelectViewController() - let filterReactor = FilterSelectReactor() - - viewController.reactor = filterReactor - owner.PPPresent(viewController) + @Dependency var factory: FilterSelectorFactory + owner.PPPresent(factory.make()) case .popupDetail(let popupID): print("DEBUG: PopupStore ID is \(popupID)") diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index d2afba12..f243a3e0 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -66,6 +66,7 @@ extension AppDelegate { private func registerFactory() { DIContainer.register(PopupSearchFactory.self) { return PopupSearchFactoryImpl() } DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } + DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } } } From 4662ada646003555b04dc48ad5c75aedea4609ee Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 17:49:11 +0900 Subject: [PATCH 339/393] =?UTF-8?q?feat/#131:=20=ED=8C=9D=EC=97=85=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=ED=8C=A9=ED=86=A0=EB=A6=AC=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 --- .../Presentation.xcodeproj/project.pbxproj | 192 ++++++++++++++++++ .../FactoryImpl/DetailFactoryImpl.swift | 24 +++ .../Factory/DetailFactory.swift | 5 + .../SearchFeature.xcodeproj/project.pbxproj | 18 ++ .../SearchFeatureDemo/App/AppDelegate.swift | 3 + 5 files changed, 242 insertions(+) create mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift create mode 100644 Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/DetailFactory.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 9de8fcd1..26bf2f88 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -12,6 +12,12 @@ 051631302DC3D1FD00A6C0D1 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; }; 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1E02DB67C8300B141FF /* RxSwift */; }; + 05734C3C2DCDF6FE0093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; }; + 05734C3D2DCDF6FE0093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C412DCDF7190093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; }; + 05734C422DCDF7190093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C492DCDF7960093825D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; }; + 05734C4A2DCDF7960093825D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CB2DB6756500C1E192 /* SnapKit */; }; 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; @@ -26,13 +32,36 @@ 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 08B2A3692DB66BF200E57EFA /* RxGesture */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 05734C3E2DCDF6FE0093825D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 058CC8FB2DB537960084221A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05734C342DCDF6EC0093825D; + remoteInfo = PresentationInterface; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ + 05734C4B2DCDF7960093825D /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 05734C4A2DCDF7960093825D /* DesignSystem.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; 05BDD5C22DB6744000C1E192 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( + 05734C422DCDF7190093825D /* SearchFeatureInterface.framework in Embed Frameworks */, + 05734C3D2DCDF6FE0093825D /* PresentationInterface.framework in Embed Frameworks */, 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -42,12 +71,20 @@ /* Begin PBXFileReference section */ 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.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; }; 058CC9042DB537960084221A /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedRootGroup section */ + 05734C362DCDF6EC0093825D /* PresentationInterface */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = PresentationInterface; + sourceTree = ""; + }; 058CC9062DB537960084221A /* Presentation */ = { isa = PBXFileSystemSynchronizedRootGroup; path = Presentation; @@ -56,6 +93,14 @@ /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ + 05734C322DCDF6EC0093825D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 05734C492DCDF7960093825D /* DesignSystem.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 058CC9012DB537960084221A /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -65,12 +110,14 @@ 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */, 08B2A36A2DB66BF200E57EFA /* RxGesture in Frameworks */, 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */, + 05734C3C2DCDF6FE0093825D /* PresentationInterface.framework in Frameworks */, 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */, 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */, 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */, 08B2A3612DB66BAB00E57EFA /* Then in Frameworks */, 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, + 05734C412DCDF7190093825D /* SearchFeatureInterface.framework in Frameworks */, 05125BA12DB6275C001342A2 /* PanModal in Frameworks */, 08B2A3642DB66BBC00E57EFA /* Tabman in Frameworks */, 05EC93D32DB6536200771CB3 /* RxCocoa in Frameworks */, @@ -86,6 +133,7 @@ isa = PBXGroup; children = ( 058CC9062DB537960084221A /* Presentation */, + 05734C362DCDF6EC0093825D /* PresentationInterface */, 05C1D6292DB53A8200508FFD /* Frameworks */, 058CC9052DB537960084221A /* Products */, ); @@ -95,6 +143,7 @@ isa = PBXGroup; children = ( 058CC9042DB537960084221A /* Presentation.framework */, + 05734C352DCDF6EC0093825D /* PresentationInterface.framework */, ); name = Products; sourceTree = ""; @@ -102,6 +151,8 @@ 05C1D6292DB53A8200508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 05734C482DCDF7960093825D /* DesignSystem.framework */, + 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */, 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */, 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */, 05C1D62B2DB53A8200508FFD /* Infrastructure.framework */, @@ -112,6 +163,13 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ + 05734C302DCDF6EC0093825D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 058CC8FF2DB537960084221A /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -122,6 +180,30 @@ /* End PBXHeadersBuildPhase section */ /* Begin PBXNativeTarget section */ + 05734C342DCDF6EC0093825D /* PresentationInterface */ = { + isa = PBXNativeTarget; + buildConfigurationList = 05734C3B2DCDF6EC0093825D /* Build configuration list for PBXNativeTarget "PresentationInterface" */; + buildPhases = ( + 05734C302DCDF6EC0093825D /* Headers */, + 05734C312DCDF6EC0093825D /* Sources */, + 05734C322DCDF6EC0093825D /* Frameworks */, + 05734C332DCDF6EC0093825D /* Resources */, + 05734C4B2DCDF7960093825D /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 05734C362DCDF6EC0093825D /* PresentationInterface */, + ); + name = PresentationInterface; + packageProductDependencies = ( + ); + productName = PresentationInterface; + productReference = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; + productType = "com.apple.product-type.framework"; + }; 058CC9032DB537960084221A /* Presentation */ = { isa = PBXNativeTarget; buildConfigurationList = 058CC90B2DB537960084221A /* Build configuration list for PBXNativeTarget "Presentation" */; @@ -135,6 +217,7 @@ buildRules = ( ); dependencies = ( + 05734C3F2DCDF6FE0093825D /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 058CC9062DB537960084221A /* Presentation */, @@ -169,6 +252,9 @@ LastSwiftUpdateCheck = 1630; LastUpgradeCheck = 1630; TargetAttributes = { + 05734C342DCDF6EC0093825D = { + CreatedOnToolsVersion = 16.3; + }; 058CC9032DB537960084221A = { CreatedOnToolsVersion = 16.3; }; @@ -203,11 +289,19 @@ projectRoot = ""; targets = ( 058CC9032DB537960084221A /* Presentation */, + 05734C342DCDF6EC0093825D /* PresentationInterface */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 05734C332DCDF6EC0093825D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 058CC9022DB537960084221A /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -218,6 +312,13 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 05734C312DCDF6EC0093825D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 058CC9002DB537960084221A /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -227,7 +328,89 @@ }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 05734C3F2DCDF6FE0093825D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05734C342DCDF6EC0093825D /* PresentationInterface */; + targetProxy = 05734C3E2DCDF6FE0093825D /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + /* Begin XCBuildConfiguration section */ + 05734C392DCDF6EC0093825D /* 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.PresentationInterface; + 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; + }; + 05734C3A2DCDF6EC0093825D /* 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.PresentationInterface; + 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; + }; 058CC9092DB537960084221A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -430,6 +613,15 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 05734C3B2DCDF6EC0093825D /* Build configuration list for PBXNativeTarget "PresentationInterface" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 05734C392DCDF6EC0093825D /* Debug */, + 05734C3A2DCDF6EC0093825D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 058CC8FE2DB537960084221A /* Build configuration list for PBXProject "Presentation" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift new file mode 100644 index 00000000..50139c53 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift @@ -0,0 +1,24 @@ +import DesignSystem +import DomainInterface +import Infrastructure +import PresentationInterface + + +public final class DetailFactoryImpl: DetailFactory { + public init() { } + + public func make(popupID: Int) -> BaseViewController { + let viewController = DetailController() + let reactor = DetailReactor( + popUpID: Int64(popupID), + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), + popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), + preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) + ) + + viewController.reactor = reactor + + return viewController + } +} diff --git a/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/DetailFactory.swift b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/DetailFactory.swift new file mode 100644 index 00000000..c21eea95 --- /dev/null +++ b/Poppool/PresentationLayer/Presentation/PresentationInterface/Factory/DetailFactory.swift @@ -0,0 +1,5 @@ +import DesignSystem + +public protocol DetailFactory { + func make(popupID: Int) -> BaseViewController +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 02183c9e..427e6c8c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -11,6 +11,12 @@ 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; }; 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C442DCDF7240093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C432DCDF7240093825D /* PresentationInterface.framework */; }; + 05734C452DCDF7240093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C432DCDF7240093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C502DCDF8B40093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */; }; + 05734C512DCDF8B40093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C532DCDF8B80093825D /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C522DCDF8B80093825D /* Presentation.framework */; }; + 05734C542DCDF8B80093825D /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C522DCDF8B80093825D /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 058AE08D2DCCC27F009119B2 /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 058AE08C2DCCC27F009119B2 /* Then */; }; 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; }; 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -61,6 +67,7 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 05734C452DCDF7240093825D /* PresentationInterface.framework in Embed Frameworks */, 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -85,9 +92,11 @@ files = ( 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */, 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */, + 05734C512DCDF8B40093825D /* PresentationInterface.framework in Embed Frameworks */, 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */, 05CFFBF52DCB908B0051129F /* DesignSystem.framework in Embed Frameworks */, 05CFFBEF2DCB90460051129F /* DomainInterface.framework in Embed Frameworks */, + 05734C542DCDF8B80093825D /* Presentation.framework in Embed Frameworks */, 05CFFBE62DCB8F6C0051129F /* SearchFeature.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -101,6 +110,9 @@ 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C232DCDD4CF0093825D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C2A2DCDD6380093825D /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734C432DCDF7240093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 05734C522DCDF8B80093825D /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -147,6 +159,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05734C442DCDF7240093825D /* PresentationInterface.framework in Frameworks */, 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */, 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */, 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, @@ -165,6 +178,7 @@ files = ( 05CFFBEE2DCB90460051129F /* DomainInterface.framework in Frameworks */, 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */, + 05734C532DCDF8B80093825D /* Presentation.framework in Frameworks */, 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, 05CFFBEA2DCB90210051129F /* Data.framework in Frameworks */, 05CFFBF32DCB906F0051129F /* RxCocoa in Frameworks */, @@ -172,6 +186,7 @@ 05CFFBF02DCB90580051129F /* Infrastructure.framework in Frameworks */, 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */, 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */, + 05734C502DCDF8B40093825D /* PresentationInterface.framework in Frameworks */, 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -211,6 +226,9 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 05734C522DCDF8B80093825D /* Presentation.framework */, + 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */, + 05734C432DCDF7240093825D /* PresentationInterface.framework */, 05734C2A2DCDD6380093825D /* Infrastructure.framework */, 05734C232DCDD4CF0093825D /* DesignSystem.framework */, 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index f243a3e0..85babba4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -4,6 +4,8 @@ import Data import Domain import DomainInterface import Infrastructure +import Presentation +import PresentationInterface import SearchFeature import SearchFeatureInterface @@ -67,6 +69,7 @@ extension AppDelegate { DIContainer.register(PopupSearchFactory.self) { return PopupSearchFactoryImpl() } DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } + DIContainer.register(DetailFactory.self) { return DetailFactoryImpl() } } } From 559e17f98ce8f891e648521d496d76d685a3f26d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 17:54:56 +0900 Subject: [PATCH 340/393] =?UTF-8?q?feat/#131:=20SearchFeature=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=8C=9D=EC=97=85=20=EB=94=94=ED=85=8C=EC=9D=BC?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EB=8A=94=20=EC=9D=98=EC=A1=B4=EC=84=B1=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Presentation.xcodeproj/project.pbxproj | 17 +++++++++++++++++ .../Source/View/PopupSearchViewController.swift | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index 26bf2f88..f64f8ae2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -18,6 +18,7 @@ 05734C422DCDF7190093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C492DCDF7960093825D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; }; 05734C4A2DCDF7960093825D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C572DCDF9E80093825D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C562DCDF9E80093825D /* NMapsMap */; }; 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CB2DB6756500C1E192 /* SnapKit */; }; 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; 05C1D62C2DB53A8200508FFD /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D62A2DB53A8200508FFD /* DomainInterface.framework */; }; @@ -115,6 +116,7 @@ 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */, 08B2A35B2DB66B5A00E57EFA /* FloatingPanel in Frameworks */, 08B2A3612DB66BAB00E57EFA /* Then in Frameworks */, + 05734C572DCDF9E80093825D /* NMapsMap in Frameworks */, 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */, 05C1D62E2DB53A8200508FFD /* Infrastructure.framework in Frameworks */, 05734C412DCDF7190093825D /* SearchFeatureInterface.framework in Frameworks */, @@ -237,6 +239,7 @@ 05BDD5CB2DB6756500C1E192 /* SnapKit */, 05BDD5CE2DB6770300C1E192 /* Lottie */, 0522C1E02DB67C8300B141FF /* RxSwift */, + 05734C562DCDF9E80093825D /* NMapsMap */, ); productName = Presentation; productReference = 058CC9042DB537960084221A /* Presentation.framework */; @@ -282,6 +285,7 @@ 08B2A3652DB66BD400E57EFA /* XCRemoteSwiftPackageReference "Pageboy" */, 08B2A3682DB66BF200E57EFA /* XCRemoteSwiftPackageReference "RxGesture" */, 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */, + 05734C552DCDF9E80093825D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, ); preferredProjectObjectVersion = 77; productRefGroup = 058CC9052DB537960084221A /* Products */; @@ -675,6 +679,14 @@ minimumVersion = 1.2.7; }; }; + 05734C552DCDF9E80093825D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.21.0; + }; + }; 05BDD5CD2DB6770300C1E192 /* XCRemoteSwiftPackageReference "lottie-ios" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/airbnb/lottie-ios"; @@ -757,6 +769,11 @@ package = 05125B932DB62295001342A2 /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; + 05734C562DCDF9E80093825D /* NMapsMap */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C552DCDF9E80093825D /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; + productName = NMapsMap; + }; 05BDD5CB2DB6756500C1E192 /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = 05125B992DB626ED001342A2 /* XCRemoteSwiftPackageReference "SnapKit" */; diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 14a4570d..88ae84e6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -3,6 +3,7 @@ import UIKit import DesignSystem import DomainInterface import Infrastructure +import PresentationInterface import SearchFeatureInterface import ReactorKit @@ -165,7 +166,11 @@ extension PopupSearchViewController { owner.PPPresent(factory.make()) case .popupDetail(let popupID): - print("DEBUG: PopupStore ID is \(popupID)") + @Dependency var factory: DetailFactory + owner.navigationController?.pushViewController( + factory.make(popupID: popupID), + animated: true + ) default: break } From f83725ae817f3c94b01d64134d40cf7dc669d3c2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 18:51:42 +0900 Subject: [PATCH 341/393] =?UTF-8?q?refactor/#131:=20=ED=99=88=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=EC=97=90=EC=84=9C=20SearchBar=EB=A1=9C=20=EC=8B=A0?= =?UTF-8?q?=EA=B7=9C=20=EB=AA=A8=EB=93=88=EB=A1=9C=20=EC=A0=91=EA=B7=BC?= =?UTF-8?q?=ED=95=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 --- Poppool/.swiftlint.yml | 1 + Poppool/Poppool.xcodeproj/project.pbxproj | 111 ++++++++++++++++++ Poppool/Poppool/Application/AppDelegate.swift | 20 ++++ .../icon_bookmark.imageset/Contents.json | 13 +- .../icon_bookmark_fill.png | Bin 6098 -> 0 bytes .../icon_bookmark.imageset/solid.svg | 17 +++ .../icon_bookmark_fill.imageset/Contents.json | 13 +- .../icon_bookmark_fill.imageset/solid-1.svg | 17 +++ .../icon_bookmark_fill.imageset/solid.png | Bin 6207 -> 0 bytes .../icon_clearButton.imageset/Contents.json | 21 ---- .../icon_clearButton.png | Bin 1687 -> 0 bytes .../icon_clear_button.imageset/Contents.json | 12 ++ .../Icon/icon_clear_button.imageset/solid.svg | 5 + .../Icon/icon_dropdown.imageset/Contents.json | 13 +- .../Icon/icon_dropdown.imageset/solid.png | Bin 759 -> 0 bytes .../Icon/icon_dropdown.imageset/solid.svg | 3 + .../icon_search_gray.imageset/Contents.json | 13 +- .../icon_search_gray.imageset/line.png | Bin 1264 -> 0 bytes .../icon_search_gray.imageset/line.svg | 4 + .../icon_xmark.imageset/Contents.json | 13 +- .../icon_xmark.imageset/icon_xmark.png | Bin 755 -> 0 bytes .../icon_xmark/icon_xmark.imageset/line.svg | 4 + .../icon_xmark_gray.imageset/Contents.json | 13 +- .../icon_xmark_gray.png | Bin 583 -> 0 bytes .../icon_xmark_gray.imageset/line.svg | 4 + .../icon_xmark_white.imageset/Contents.json | 13 +- .../icon_xmark_white.png | Bin 407 -> 0 bytes .../icon_xmark_white.imageset/line.svg | 4 + .../Scene/Home/Main/HomeReactor.swift | 11 +- .../Main/View/ProfileEditView.swift | 2 +- .../Scene/Search/Main/SearchMainView.swift | 2 +- .../Scene/SignUp/Step2/SignUpStep2View.swift | 2 +- .../Source/Reactor/PopupSearchReactor.swift | 5 +- .../View/PopupSearchViewController.swift | 26 ++-- 34 files changed, 238 insertions(+), 124 deletions(-) delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/icon_bookmark_fill.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/solid.svg create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid-1.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid.png delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/Contents.json delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/icon_clearButton.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clear_button.imageset/Contents.json create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clear_button.imageset/solid.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/solid.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/solid.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/line.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/line.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/icon_xmark.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/line.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/icon_xmark_gray.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/line.svg delete mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/icon_xmark_white.png create mode 100644 Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/line.svg diff --git a/Poppool/.swiftlint.yml b/Poppool/.swiftlint.yml index 9e132e30..4718cfd2 100644 --- a/Poppool/.swiftlint.yml +++ b/Poppool/.swiftlint.yml @@ -10,6 +10,7 @@ disabled_rules: - identifier_name - cyclomatic_complexity - redundant_optional_initialization + - function_parameter_count # 기본(default) 룰이 아닌 룰들을 활성화 opt_in_rules: diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 94d3a29d..949d12f5 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -9,6 +9,18 @@ /* Begin PBXBuildFile section */ 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D82DB67C5900B141FF /* RxSwift */; }; 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C3C52DB67D7800B141FF /* RxGesture */; }; + 05734C5A2DCDFAC20093825D /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; }; + 05734C5B2DCDFAC20093825D /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C5C2DCDFAC20093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; }; + 05734C5D2DCDFAC20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C5F2DCE04CE0093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; }; + 05734C602DCE04CE0093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 05734C632DCE04FA0093825D /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C622DCE04FA0093825D /* Pageboy */; }; + 05734C662DCE05070093825D /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C652DCE05070093825D /* PanModal */; }; + 05734C682DCE05240093825D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C672DCE05240093825D /* SnapKit */; }; + 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 */; }; 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */; }; 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -34,11 +46,14 @@ dstPath = ""; dstSubfolderSpec = 10; files = ( + 05734C5B2DCDFAC20093825D /* SearchFeature.framework in Embed Frameworks */, 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */, 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */, 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */, + 05734C602DCE04CE0093825D /* PresentationInterface.framework in Embed Frameworks */, 05CFFC442DCC83290051129F /* DesignSystem.framework in Embed Frameworks */, 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */, + 05734C5D2DCDFAC20093825D /* SearchFeatureInterface.framework in Embed Frameworks */, 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -48,6 +63,9 @@ /* Begin PBXFileReference section */ 05229DD02D99519200D88E73 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; }; + 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; }; 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; }; @@ -88,17 +106,26 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 05734C662DCE05070093825D /* PanModal in Frameworks */, 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */, 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */, 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, + 05734C6E2DCE05680093825D /* Tabman in Frameworks */, 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */, 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */, + 05734C5C2DCDFAC20093825D /* SearchFeatureInterface.framework in Frameworks */, 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, + 05734C712DCE059D0093825D /* Then in Frameworks */, 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */, + 05734C6B2DCE05550093825D /* ReactorKit in Frameworks */, + 05734C5A2DCDFAC20093825D /* SearchFeature.framework in Frameworks */, + 05734C5F2DCE04CE0093825D /* PresentationInterface.framework in Frameworks */, 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */, 05CFFC432DCC83290051129F /* DesignSystem.framework in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, + 05734C682DCE05240093825D /* SnapKit in Frameworks */, + 05734C632DCE04FA0093825D /* Pageboy in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -109,6 +136,9 @@ 05C1D6062DB53A4900508FFD /* Frameworks */ = { isa = PBXGroup; children = ( + 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */, + 05734C582DCDFAC20093825D /* SearchFeature.framework */, + 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */, 05CFFC422DCC83290051129F /* DesignSystem.framework */, 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */, 05C1D6072DB53A4900508FFD /* Data.framework */, @@ -164,6 +194,12 @@ 0522C1D82DB67C5900B141FF /* RxSwift */, 0522C3C52DB67D7800B141FF /* RxGesture */, 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */, + 05734C622DCE04FA0093825D /* Pageboy */, + 05734C652DCE05070093825D /* PanModal */, + 05734C672DCE05240093825D /* SnapKit */, + 05734C6A2DCE05550093825D /* ReactorKit */, + 05734C6D2DCE05680093825D /* Tabman */, + 05734C702DCE059D0093825D /* Then */, ); productName = Poppool; productReference = BDCA41BD2CF35AC0005EECF6 /* Poppool.app */; @@ -198,6 +234,11 @@ 05BDD5C32DB674E500C1E192 /* XCRemoteSwiftPackageReference "SnapKit" */, 05BDD5D62DB6783C00C1E192 /* XCRemoteSwiftPackageReference "RxSwift" */, 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */, + 05734C612DCE04FA0093825D /* XCRemoteSwiftPackageReference "Pageboy" */, + 05734C642DCE05070093825D /* XCRemoteSwiftPackageReference "PanModal" */, + 05734C692DCE05550093825D /* XCRemoteSwiftPackageReference "ReactorKit" */, + 05734C6C2DCE05680093825D /* XCRemoteSwiftPackageReference "Tabman" */, + 05734C6F2DCE059D0093825D /* XCRemoteSwiftPackageReference "Then" */, ); preferredProjectObjectVersion = 56; productRefGroup = BDCA41BE2CF35AC0005EECF6 /* Products */; @@ -498,6 +539,46 @@ minimumVersion = 4.0.4; }; }; + 05734C612DCE04FA0093825D /* XCRemoteSwiftPackageReference "Pageboy" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Pageboy"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 4.2.0; + }; + }; + 05734C642DCE05070093825D /* XCRemoteSwiftPackageReference "PanModal" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/slackhq/PanModal"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.2.7; + }; + }; + 05734C692DCE05550093825D /* XCRemoteSwiftPackageReference "ReactorKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/ReactorKit/ReactorKit"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 05734C6C2DCE05680093825D /* XCRemoteSwiftPackageReference "Tabman" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Tabman"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 05734C6F2DCE059D0093825D /* XCRemoteSwiftPackageReference "Then" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/devxoul/Then"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; 05BDD5C32DB674E500C1E192 /* XCRemoteSwiftPackageReference "SnapKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SnapKit/SnapKit"; @@ -551,6 +632,36 @@ package = 0522C3C42DB67D7800B141FF /* XCRemoteSwiftPackageReference "RxGesture" */; productName = RxGesture; }; + 05734C622DCE04FA0093825D /* Pageboy */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C612DCE04FA0093825D /* XCRemoteSwiftPackageReference "Pageboy" */; + productName = Pageboy; + }; + 05734C652DCE05070093825D /* PanModal */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C642DCE05070093825D /* XCRemoteSwiftPackageReference "PanModal" */; + productName = PanModal; + }; + 05734C672DCE05240093825D /* SnapKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05BDD5C32DB674E500C1E192 /* XCRemoteSwiftPackageReference "SnapKit" */; + productName = SnapKit; + }; + 05734C6A2DCE05550093825D /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C692DCE05550093825D /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 05734C6D2DCE05680093825D /* Tabman */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C6C2DCE05680093825D /* XCRemoteSwiftPackageReference "Tabman" */; + productName = Tabman; + }; + 05734C702DCE059D0093825D /* Then */ = { + isa = XCSwiftPackageProductDependency; + package = 05734C6F2DCE059D0093825D /* XCRemoteSwiftPackageReference "Then" */; + productName = Then; + }; 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */ = { isa = XCSwiftPackageProductDependency; package = 08F403312D884F4D00BFA61A /* XCRemoteSwiftPackageReference "kakao-ios-sdk" */; diff --git a/Poppool/Poppool/Application/AppDelegate.swift b/Poppool/Poppool/Application/AppDelegate.swift index 46c60253..82d5d5a8 100644 --- a/Poppool/Poppool/Application/AppDelegate.swift +++ b/Poppool/Poppool/Application/AppDelegate.swift @@ -5,6 +5,10 @@ import Data import Domain import DomainInterface import Infrastructure +import Presentation +import PresentationInterface +import SearchFeature +import SearchFeatureInterface import KakaoSDKCommon import NMapsMap @@ -20,6 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { locationManager.requestWhenInUseAuthorization() self.registerDependencies() + self.registerFactory() return true } @@ -36,10 +41,12 @@ 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 @Dependency var provider: Provider + @Dependency var userDefaultService: UserDefaultService // MARK: Register repository DIContainer.register(MapRepository.self) { return MapRepositoryImpl(provider: provider) } @@ -54,6 +61,8 @@ extension AppDelegate { DIContainer.register(PreSignedRepository.self) { return PreSignedRepositoryImpl() } DIContainer.register(KakaoLoginRepository.self) { return KakaoLoginRepositoryImpl() } DIContainer.register(AppleLoginRepository.self) { return AppleLoginRepositoryImpl() } + DIContainer.register(CategoryRepository.self) { return CategoryRepositoryImpl(provider: provider) } + DIContainer.register(SearchAPIRepository.self) { return SearchAPIRepositoryImpl(provider: provider, userDefaultService: userDefaultService) } // MARK: Resolve repository @Dependency var mapRepository: MapRepository @@ -67,6 +76,8 @@ extension AppDelegate { @Dependency var preSignedRepository: PreSignedRepository @Dependency var kakaoLoginRepository: KakaoLoginRepository @Dependency var appleLoginRepository: AppleLoginRepository + @Dependency var categoryRepository: CategoryRepository + @Dependency var searchAPIRepository: SearchAPIRepository // MARK: Register UseCase DIContainer.register(MapUseCase.self) { return MapUseCaseImpl(repository: mapRepository) } @@ -80,5 +91,14 @@ extension AppDelegate { DIContainer.register(PreSignedUseCase.self) { return PreSignedUseCaseImpl(repository: preSignedRepository) } DIContainer.register(KakaoLoginUseCase.self) { return KakaoLoginUseCaseImpl(repository: kakaoLoginRepository) } DIContainer.register(AppleLoginUseCase.self) { return AppleLoginUseCaseImpl(repository: appleLoginRepository) } + DIContainer.register(FetchCategoryListUseCase.self) { return FetchCategoryListUseCaseImpl(repository: categoryRepository) } + DIContainer.register(FetchKeywordBasePopupListUseCase.self) { return FetchKeywordBasePopupListUseCaseImpl(repository: searchAPIRepository) } + } + + private func registerFactory() { + DIContainer.register(PopupSearchFactory.self) { return PopupSearchFactoryImpl() } + DIContainer.register(DetailFactory.self) { return DetailFactoryImpl() } + DIContainer.register(CategorySelectorFactory.self) { return CategorySelectorFactoryImpl() } + DIContainer.register(FilterSelectorFactory.self) { return FilterSelectorFactoryImpl() } } } diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/Contents.json index c203dc7e..f64986bb 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "icon_bookmark_fill.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "solid.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/icon_bookmark_fill.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/icon_bookmark_fill.png deleted file mode 100644 index d52d7e286194a1990a26a916fa1dc19d8fe6e3b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6098 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 Y7Z^f+VH&KGv;Y7A07*qoM6N<$f)z1(j{pDw diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/solid.svg b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/solid.svg new file mode 100644 index 00000000..1ef76aed --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark.imageset/solid.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/Contents.json index fb8ebfdd..44808aa2 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "solid.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "solid-1.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid-1.svg b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid-1.svg new file mode 100644 index 00000000..6d836093 --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid-1.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_bookmark/icon_bookmark_fill.imageset/solid.png deleted file mode 100644 index 0dfdebb1b74d5356ea9a7c01beed7ee82a6aa4db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6207 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 diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/Contents.json deleted file mode 100644 index d67ddcf1..00000000 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/Contents.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "images" : [ - { - "filename" : "icon_clearButton.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/icon_clearButton.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_clearButton.imageset/icon_clearButton.png deleted file mode 100644 index b8a1cd0d59174cbb5fb1231868cc3ed4b872a868..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1687 zcmV;I259+-P)@~0drDELIAGL9O(c600d`2O+f$vv5yPt?Bu4R2TmmH+4-O|WF|sBeFfpNMNbITLMLcLBdhrCDFd>GL zcradU4<;B73&WYmLFu-SPLE?r!)0?7o@zw%hQzymrenyPy62 z-u&Kg-YjsR`_#ig3rW~^7w)2qeiX>k==nbb7F>TckxpHAQqYK2A&CRFon1k~Gx5CP z(a0hifL>(EGvLyA+ce>%Za5AUp zA^R_ey(~^dCZRU2Vd9+0a&9;-LK3ME%_7Qi4FjW;QkTPsu@Div2khbOGB|t>k&F)L zhd%(@^N`%$D;5`KU8%`9bcE0xAu<_U;unMU1kW)hPBt4Yp(4beFjsJu4!#TPk6nJW z^oidWX560|eUh}0-TUm3tcTGSrAMQ18XbxC3H&+ZUTfqDQbJe;)di=~KG=;)X8MVG+Fqbv8`N~k-B^bgpBG_><18ia`L z5Q{XQ+{iyrGeS63Xc&PchhKUD4{zy&4#FIV7NR4Ggk4yO))YdGoTHFTm7(KD@xlA2 z@X3X<%B8$a;y?fNB6@pbkfbZSc`Xa7PO4!hQkF4ve6aBQ&%gYNH-}E*@1=hbH7w&p z63@)e&HKlhnVp9OY3m+#rczG2YJ^a9Bq1SVU~i&C2J_UDPbi6$Vi`CQ}YcC=H=|98pVAEyKryC=rs_XWL$gbSWn)DOzN#(orI$yH5-v;@)UjDH>(?lnUt) zT$u_)jI0#hGMEw}v0lOFV(F5v9?eQ=g$$-NLVCr=P)MUvS|x)i2uTdseDOK53AGkM zQXbsA8SkBXyW)8|WH1FGu^zFD&Mb62PT7jx=GS#De zwQJL$Wb;e2<#?;4lrHY#B2KU25F$o)ZPE%UB|^6Khz=v-YBlUaO}CU1Aq%svyM46< z5Xm;Fs8+2SrKBr*s%zs+suk@Ps$@H5t65|9Jy+cy67Lo%s#>{w)>M?- zx$_swKRs1T`0_~Mi`{&jS&56?937^Kcjy0b(^ZA|RcnuUh>mBYNs3>n>V8K4SbTw7 zRt7_jvGYHCw|bjN&2|HCBFQ{}-ym7azkcLZjDLAWOLK=)MfNzk=s1|)YgV`avU{I> zg6y5e#^%`6O=mR!OJGB)f57IAGd4CR4#_xJ{6Js}(Xw!i2JPK6)o`lh-dhQQLO8cP z53gW@LD!(F-b>Ta)^0A==(?|A7#kyXXkI)sJ>gWnO4;yUcaq|C$HgNg})2X`=x#|05C{o7SF@5~V2#~=@-BIPIPa8=IN|R!w z@3~_s+vDQ4Vfu55vc_~6sK`pw8S=&HqQWbPP?s{NZaHII_R^0I)*p^(VbAnrbKesX z*`-sybAnQ6ggz007Q#`H@o?tHN#`6Si0sa8zLPq>LyTv$Sfr>C(40oO^!Ui?#A{R6 z<-a{b&6gn@u*JO + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/Contents.json index fb8ebfdd..f64986bb 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "solid.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "solid.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/solid.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_dropdown.imageset/solid.png deleted file mode 100644 index 17de8bbf6295576a0e2b49efe155c1e1cc2d8879..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 759 zcmeAS@N?(olHy`uVBq!ia0vp^5kMTt!3HFEoqRkINO2Z;L>4nJ=y-!L<5JscD}jQH z#X;^)4C~IxyaaMs(j9#r85lP9bN@+XWnf_H@pN$vsbGA2+b~Z!P=M`$;WqBuyeIC- z9lEYlP2m!du%&n)9JWcCCc`=@%yvu{`q%J zI#uX)zW&~yO`Skn&_M~Whi9P3^((^72N%Sx4U6||Jk?OSW4{0L%cfsB_83~38&9qb2y1)p<#xq*N9O+f|7ZEAo&Paqk6Qab*XElqdC&N5u#oGYf9UjtIUD=f z673#rcv{)h^~% zpyHavN>{gCQ+wSYEaH^fmAlq=8c*IPt(G8=x_Jo;V>w!n&pMkXe}Yk*`I=tKq2$oe zr;Fa4mlJf_DBua8MJ9Wc+GAW@E=xwoX%yYr_whokONTMn!W>sWp6!B)w-zat zPZU0!>(X)fp+MZf?~4vO1G#@2qPOjN + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/Contents.json index 25625b1d..d86eb988 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "line.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "line.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/line.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_search/icon_search_gray.imageset/line.png deleted file mode 100644 index 6cee2193abec62a571006b90f6f7f03bb757ea6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1264 zcmV@~0drDELIAGL9O(c600d`2O+f$vv5yP-?De-5QFdJ z58a-eWPdM_Po7Bnd++w+Rsf2kD2k#eilQirq9}@TXF%wPYIM8}V4m~gV*u*&t!x8P zIrEh(zPpYUvI=NLBXhlv1wI3UKCDg+h+}|aSPfp*umNNd&TvLC2pu6RU^O}#fwwwY5H10FFS@Ws z5dpn}n4B)X&LKMtup(f6JLoKy9SiKha`5`t+s!Rvasi!qG&$qLods7O6VP{rt#3^a zee12sI-cjy40~?FBEG(aOb7`0<+SlZ-E^bDIw1**S@Mrl@!?*>*Ron-QUQmfqeJ$z zRqQ;U8w}qae1MiX#{}${DC(H3h)-C=D-foDMA(Olif7k2q}>$axobJ=Eq1*%2Pt{l zA3_F_3Ft$W04|%b){r)f@KdxG7m>Zcw^Uh@2xzBnJkB#>;f0@~R>WEFJqwCkVG;p} zU_WwVDD4rwh&-JE=GQw8qol2{wSd+8qy3=u@zkx}9aNA3XW>L2!)Il!u)PHq6bUIX zZ+ca1SqBxd!s9ggcj&Uv0w0$JvJGo}gI6bz5jT}1R%k0gAVfP0G^QUuf!!qc-U{b% zELK~1G>DnY)DmBYAaGt;R^7-`2v=q=*ljpd~nTrNE4~%WQf2+}anjKH>q3DxM!SwllnvX+Z!zf9cn zT(gqvOsGMa#KnZ(%lIBG*uIvZeh+zle7~W&SkN|i1>q$nITGDjR;UYT?nJE-@r0oG zx{Q^~bZG6Z&@yLx26v_VV}$wxKk>TPJ8fwGnY{zs7dR$ke(~l{?UR06yL0tB+kn0^ z-P2fo4b32;Scj_5M9kSzB1$4GqF6!IPtD>9AAST$tUyTd{use1z+XNCD^2NTVNXkj zhBqy93oTyA5;MOi;_}ty9YF{iq8N@jc@zDAF}e`3lQ)&7588KP+Foi};2`XS6YuwO zRb^3OAh(E}z3MfKU!Sgb{HL2e4O0Vb)$0VqIYrzE0VT*K;=L8nl0(FMEuckO#CtEG zMN-5E(U~v}>KmHT*XKc4;0-}b)G53g|A?qgWIeoylaZ}o=6D@UQ4~c{6h%=KMNt&x aFMj~;mNhu-sD|AD0000 + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/Contents.json index 0f28cb44..d86eb988 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "icon_xmark.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "line.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/icon_xmark.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/icon_xmark.png deleted file mode 100644 index 0125c1888332e9a47c45d935410a79675ee4a0ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 755 zcmeAS@N?(olHy`uVBq!ia0vp^2|%34!3HEXk1UxBq&N#aB8wRqbi6^BajEUJl|Vtp z;vjb?hIQv;UIIBR>5jgR3=A9lx&I`xGB7Z8c)B=-R4~51Pz7j<3IMb$Bm}mloYsc z`2OSEKH2#4JuQ!XBns%dv!A-0lrP32^ou)CYeE-nM-9t{{I&aA}4o~JMaeEI)C`K|&TdJHRIEf`8di!CK zgEkDW7imu8SjKoGe2t-t0r!K{oeA$a(pD=y`cS`X+eTg4gZmQZ@7nfV_H@X!`=TEs zMOdfV<=r~JdEM=omcns-?arcmv`%lzs!hDCH{r0|wtX8`a&2c|f0X#6H@HQs0}aIQ Z|D3t&-n%Q$E&$UggQu&X%Q~loCIBB_L`eVu diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/line.svg b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/line.svg new file mode 100644 index 00000000..c95be2c3 --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark.imageset/line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/Contents.json index 3184723c..d86eb988 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "icon_xmark_gray.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "line.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/icon_xmark_gray.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_gray.imageset/icon_xmark_gray.png deleted file mode 100644 index 37daacff524d0358f9251659c8f0dc902144344e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 583 zcmeAS@N?(olHy`uVBq!ia0vp^4nXY4!3HD~O&3)IDb50q$YKTt9d8h3TxvUQB~XyD zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFK)E}fE{-7;jBjV<`Y{;_v@L(p_{F8k<-gP+ z#%{dcfqwQFr^s(TqDbcj_i6$=F>;+xqsh<^40u zbWvd)ST_sce8*eNup8WTD;@-$ipZ1g=57c%(oViHz zz>lLvK8kzfO>``Tst%uDuC28of?=_yqp^*$Qp3dw3uNaB3Lo(B+Q4zXgDXKrC8D*z zbRv_gkj~-bvphOwIIlfSwq0^UQz)yYf9nJzXO$~l@~;(`f2my9bvjbBW=&&{ho?nX zV8t;X24jz=#EN4f441VJTG#~FC=_Yw?U+zgKC^Cl>q`!gy4(K`J_<-(Zf}(2@k3rm zb%sylzWbG@ou?+R=x5Kc<@6%qj(dNvJJ{CUv{hvc-|NkQ{35^pA?|qDhSI?+t SuZds;33 + + + diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/Contents.json b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/Contents.json index b3d1a689..d86eb988 100644 --- a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/Contents.json +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/Contents.json @@ -1,17 +1,8 @@ { "images" : [ { - "filename" : "icon_xmark_white.png", - "idiom" : "universal", - "scale" : "1x" - }, - { - "idiom" : "universal", - "scale" : "2x" - }, - { - "idiom" : "universal", - "scale" : "3x" + "filename" : "line.svg", + "idiom" : "universal" } ], "info" : { diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/icon_xmark_white.png b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/icon_xmark_white.png deleted file mode 100644 index 6de5ee2ff1951d3ad978e5fdae8304e3ffc64a66..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^4nXY4!3HD~O&3)IDb50q$YKTt9d8h3TxvUQB~XyD zILO_JVcj{Imp~3nx}&cn1H;CC?mvmFKsg;x7srqa#<#bk*Bvt8aSiMiG*mX4T=s04 zXvC%f&dgaB8!cad_d07EZKKt|0n`ry3z&@WpS;mw%%A)J#36ltqfPU_+7&OHc%@=n z*Mei8FL3;Jp3q;~U0@K`Q)!jMuD+UM=EqLAO&tbrD=r!z`tsPdpCj*B_cXyfz2dq` zJ7o3OxfIyPU2)+sj+@gdkhe#pQ=n|m30DsLxD=%$^7>rLDqU~fTU?IirSxn6e-{6Y z)!+Dc%E^$2KV}FTJr6^x~%sY2>v+qcI|JDI}h&N%WV;R ze06J$YldZ9>}B^BIUwhG--Y5mR>|i$fHINAt`FYF_o=;75l*}@eevq66{XM8Tc)}N p2?|LD3Wm*M{QucM1>$R)DyB#;pZ)rL>-U2MJYD@<);T3K0RWEip6&nu diff --git a/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/line.svg b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/line.svg new file mode 100644 index 00000000..b55257a1 --- /dev/null +++ b/Poppool/Poppool/Resource/Assets.xcassets/Icon/icon_xmark/icon_xmark_white.imageset/line.svg @@ -0,0 +1,4 @@ + + + + diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift index b3383fc2..6a58e109 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift @@ -1,8 +1,9 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem +import SearchFeatureInterface import ReactorKit import RxCocoa @@ -142,9 +143,11 @@ final class HomeReactor: Reactor { newState.isReloadView = false switch mutation { case .moveToSearchScene(let controller): - let nextController = SearchMainController() - nextController.reactor = SearchMainReactor() - controller.navigationController?.pushViewController(nextController, animated: true) + @Dependency var factory: PopupSearchFactory + controller.navigationController?.pushViewController(factory.make(), animated: true) +// let nextController = SearchMainController() +// nextController.reactor = SearchMainReactor() +// controller.navigationController?.pushViewController(nextController, animated: true) case .loadView: newState.isReloadView = true newState.sections = getSection() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift index 55d974ef..320ed56f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/View/ProfileEditView.swift @@ -69,7 +69,7 @@ final class ProfileEditView: UIView { }() let nickNameClearButton: UIButton = { let button = UIButton() - button.setImage(UIImage(named: "icon_clearButton"), for: .normal) + button.setImage(UIImage(named: "icon_clear_button"), for: .normal) button.isHidden = true return button }() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift index f71bf7ac..4946a606 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift @@ -40,7 +40,7 @@ final class SearchMainView: UIView { } let clearButton = UIButton().then { - $0.setImage(UIImage(named: "icon_clearButton"), for: .normal) + $0.setImage(UIImage(named: "icon_clear_button"), for: .normal) } private var headerStackView = UIStackView().then { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift index 3e1c98f7..9626eac2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step2/SignUpStep2View.swift @@ -46,7 +46,7 @@ final class SignUpStep2View: UIView { let clearButton: UIButton = { let button = UIButton() - button.setImage(UIImage(named: "icon_clearButton"), for: .normal) + button.setImage(UIImage(named: "icon_clear_button"), for: .normal) return button }() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 9877f313..23cb87c2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -53,10 +53,12 @@ public final class PopupSearchReactor: Reactor { case present(target: PresentTarget) } + @frozen public enum PresentTarget { case categorySelector case filterSelector case popupDetail(popupID: Int) + case before } public struct State { @@ -71,6 +73,7 @@ public final class PopupSearchReactor: Reactor { @Pulse var clearButtonIsHidden: Bool? @Pulse var endEditing: Void? @Pulse var updateDataSource: Void? + @Pulse var dismiss: Void? fileprivate var isSearching: Bool = false fileprivate var currentPage: Int32 = 0 @@ -173,7 +176,7 @@ public final class PopupSearchReactor: Reactor { ]) } } - else { return .empty() } // TODO: 이전 화면으로 보내기 + else { return .just(.present(target: .before)) } case .recentSearchTagButtonTapped(let indexPath): let keyword = self.makeRecentSearchItem(at: indexPath) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index 88ae84e6..b879e965 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -27,22 +27,11 @@ extension PopupSearchViewController { public override func loadView() { self.view = mainView } + + public override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) - public override func viewDidLoad() { - super.viewDidLoad() - } - - public override func present( - _ viewControllerToPresent: UIViewController, - animated flag: Bool, - completion: (() -> Void)? = nil - ) { - if let sheet = viewControllerToPresent.sheetPresentationController { - sheet.detents = [.medium()] - sheet.preferredCornerRadius = 20 - } - - super.present(viewControllerToPresent, animated: flag, completion: completion) + tabBarController?.tabBar.isHidden = true } } @@ -153,10 +142,10 @@ extension PopupSearchViewController { .disposed(by: disposeBag) reactor.pulse(\.$present) - .debug("DEBUG: present") .withUnretained(self) + .skip(1) .subscribe { owner, target in - switch target { + switch target! { case .categorySelector: @Dependency var factory: CategorySelectorFactory owner.PPPresent(factory.make()) @@ -172,7 +161,8 @@ extension PopupSearchViewController { animated: true ) - default: break + case .before: + owner.navigationController?.popViewController(animated: true) } } .disposed(by: disposeBag) From de168f868e8933a609706246b6c807a22e8bc836 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 19:52:22 +0900 Subject: [PATCH 342/393] =?UTF-8?q?refactor/#131:=20=EC=9D=BC=EB=B6=80=20S?= =?UTF-8?q?ection=EC=9D=84=20=EC=82=AC=EC=9A=A9=EC=9D=B4=20=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20View=EB=A1=9C=20=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/SimilarTitleSection}/SearchTitleSection.swift | 4 ++-- .../View/SimilarTitleSection/SimilarTitleSectionCell.swift} | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename Poppool/PresentationLayer/Presentation/Presentation/Scene/{Search/BeforeSearch/View/SearchTitleSection => Detail/View/SimilarTitleSection}/SearchTitleSection.swift (91%) rename Poppool/PresentationLayer/Presentation/Presentation/Scene/{Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift => Detail/View/SimilarTitleSection/SimilarTitleSectionCell.swift} (92%) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SearchTitleSection.swift similarity index 91% rename from Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SearchTitleSection.swift index d52d9334..ec700e25 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSection.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SearchTitleSection.swift @@ -4,11 +4,11 @@ import DesignSystem import RxSwift -struct SearchTitleSection: Sectionable { +struct SimilarTitleSection: Sectionable { var currentPage: PublishSubject = .init() - typealias CellType = SearchTitleSectionCell + typealias CellType = SimilarTitleSectionCell var inputDataList: [CellType.Input] diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SimilarTitleSectionCell.swift similarity index 92% rename from Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift rename to Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SimilarTitleSectionCell.swift index 98453aa0..8226579f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchTitleSection/SearchTitleSectionCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/View/SimilarTitleSection/SimilarTitleSectionCell.swift @@ -5,7 +5,7 @@ import DesignSystem import RxSwift import SnapKit -final class SearchTitleSectionCell: UICollectionViewCell { +final class SimilarTitleSectionCell: UICollectionViewCell { // MARK: - Components @@ -38,7 +38,7 @@ final class SearchTitleSectionCell: UICollectionViewCell { } // MARK: - SetUp -private extension SearchTitleSectionCell { +private extension SimilarTitleSectionCell { func setUpConstraints() { self.addSubview(sectionTitleLabel) sectionTitleLabel.snp.makeConstraints { make in @@ -56,7 +56,7 @@ private extension SearchTitleSectionCell { } } -extension SearchTitleSectionCell: Inputable { +extension SimilarTitleSectionCell: Inputable { struct Input { var title: String? var buttonTitle: String? From 87517f30f21830260cc3eb011d4c9524e111ade0 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 19:52:55 +0900 Subject: [PATCH 343/393] =?UTF-8?q?refactor/#131:=20=EA=B8=B0=EC=A1=B4?= =?UTF-8?q?=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8D=98=20SearchScene?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Detail/DetailController.swift | 2 +- .../Scene/Detail/DetailReactor.swift | 2 +- .../Scene/Map/MapView/MapViewController.swift | 6 +- .../AfterSearch/SearchResultController.swift | 124 ------ .../AfterSearch/SearchResultReactor.swift | 164 -------- .../SearchResultCountSection.swift | 39 -- .../SearchResultCountSectionCell.swift | 56 --- .../AfterSearch/View/SearchResultView.swift | 49 --- .../BeforeSearch/SearchController.swift | 227 ---------- .../Search/BeforeSearch/SearchReactor.swift | 393 ------------------ .../CancelableTagSection.swift | 38 -- .../CancelableTagSectionCell.swift | 105 ----- .../SearchCountTitleSection.swift | 39 -- .../SearchCountTitleSectionCell.swift | 93 ----- .../Search/BeforeSearch/View/SearchView.swift | 45 -- .../SearchCategoryController.swift | 138 ------ .../SearchCategoryReactor.swift | 120 ------ .../SearchCategoryView.swift | 84 ---- .../Search/Main/SearchMainController.swift | 188 --------- .../Scene/Search/Main/SearchMainReactor.swift | 52 --- .../Scene/Search/Main/SearchMainView.swift | 109 ----- .../SearchSortedController.swift | 102 ----- .../SearchSortedReactor.swift | 84 ---- .../SortedController/SearchSortedView.swift | 99 ----- 24 files changed, 5 insertions(+), 2353 deletions(-) delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift delete mode 100644 Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift index b10e56a6..d863da13 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailController.swift @@ -65,7 +65,7 @@ private extension DetailController { mainView.contentCollectionView.register(DetailCommentTitleSectionCell.self, forCellWithReuseIdentifier: DetailCommentTitleSectionCell.identifiers) mainView.contentCollectionView.register(DetailCommentSectionCell.self, forCellWithReuseIdentifier: DetailCommentSectionCell.identifiers) mainView.contentCollectionView.register(DetailEmptyCommetSectionCell.self, forCellWithReuseIdentifier: DetailEmptyCommetSectionCell.identifiers) - mainView.contentCollectionView.register(SearchTitleSectionCell.self, forCellWithReuseIdentifier: SearchTitleSectionCell.identifiers) + mainView.contentCollectionView.register(SimilarTitleSectionCell.self, forCellWithReuseIdentifier: SimilarTitleSectionCell.identifiers) mainView.contentCollectionView.register(DetailSimilarSectionCell.self, forCellWithReuseIdentifier: DetailSimilarSectionCell.identifiers) view.addSubview(mainView) mainView.snp.makeConstraints { make in diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 379eca54..8b23c977 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -86,7 +86,7 @@ final class DetailReactor: Reactor { private var commentTitleSection = DetailCommentTitleSection(inputDataList: []) private var commentSection = DetailCommentSection(inputDataList: []) private var commentEmptySection = DetailEmptyCommetSection(inputDataList: [.init()]) - private var similarTitleSecion = SearchTitleSection(inputDataList: [.init(title: "지금 보고있는 팝업과 비슷한 팝업")]) + private var similarTitleSecion = SimilarTitleSection(inputDataList: [.init(title: "지금 보고있는 팝업과 비슷한 팝업")]) private var similarSection = DetailSimilarSection(inputDataList: []) private var spacing70Section = SpacingSection(inputDataList: [.init(spacing: 70)]) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index 13533caf..d010c58d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -4,6 +4,7 @@ import UIKit import DomainInterface import Infrastructure import DesignSystem +import SearchFeatureInterface import FloatingPanel import NMapsMap @@ -488,9 +489,8 @@ class MapViewController: BaseViewController, View, CLLocationManagerDelegate, NM .throttle(.milliseconds(500), scheduler: MainScheduler.instance) .withUnretained(self) .subscribe(onNext: { owner, _ in - let searchMainVC = SearchMainController() - searchMainVC.reactor = SearchMainReactor() - owner.navigationController?.pushViewController(searchMainVC, animated: true) + @Dependency var factory: PopupSearchFactory + owner.navigationController?.pushViewController(factory.make(), animated: true) }) .disposed(by: disposeBag) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift deleted file mode 100644 index 840bd835..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultController.swift +++ /dev/null @@ -1,124 +0,0 @@ -import UIKit - -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class SearchResultController: BaseViewController, View { - - typealias Reactor = SearchResultReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SearchResultView() - private var sections: [any Sectionable] = [] - private let cellTapped: PublishSubject = .init() -} - -// MARK: - Life Cycle -extension SearchResultController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - tabBarController?.tabBar.isHidden = true - } -} - -// MARK: - SetUp -private extension SearchResultController { - func setUp() { - if let layout = reactor?.compositionalLayout { - mainView.contentCollectionView.collectionViewLayout = layout - } - mainView.contentCollectionView.delegate = self - mainView.contentCollectionView.dataSource = self - - mainView.contentCollectionView.register( - SearchTitleSectionCell.self, - forCellWithReuseIdentifier: SearchTitleSectionCell.identifiers - ) - mainView.contentCollectionView.register( - SpacingSectionCell.self, - forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) - mainView.contentCollectionView.register( - SearchResultCountSectionCell.self, - forCellWithReuseIdentifier: SearchResultCountSectionCell.identifiers - ) - mainView.contentCollectionView.register( - HomeCardSectionCell.self, - forCellWithReuseIdentifier: HomeCardSectionCell.identifiers - ) - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension SearchResultController { - func bind(reactor: Reactor) { - - cellTapped - .withUnretained(self) - .map({ (owner, indexPath) in - Reactor.Action.cellTapped(controller: owner, indexPath: indexPath) - }) - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .withUnretained(self) - .subscribe { (owner, state) in - if state.isEmptyResult { - owner.mainView.emptyLabel.isHidden = false - owner.mainView.contentCollectionView.isHidden = true - } else { - owner.mainView.emptyLabel.isHidden = true - owner.mainView.contentCollectionView.isHidden = false - owner.sections = state.sections - owner.mainView.contentCollectionView.reloadData() - } - } - .disposed(by: disposeBag) - } -} - -// MARK: - UICollectionViewDelegate, UICollectionViewDataSource -extension SearchResultController: UICollectionViewDelegate, UICollectionViewDataSource { - func numberOfSections(in collectionView: UICollectionView) -> Int { - return sections.count - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return sections[section].dataCount - } - - func collectionView( - _ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - guard let reactor = reactor else { return cell } - if let cell = cell as? HomeCardSectionCell { - cell.bookmarkButton.rx.tap - .map { Reactor.Action.bookmarkButtonTapped(indexPath: indexPath)} - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } - return cell - } - - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - cellTapped.onNext(indexPath) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift deleted file mode 100644 index e41dd648..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/SearchResultReactor.swift +++ /dev/null @@ -1,164 +0,0 @@ -import UIKit - -import DomainInterface -import Infrastructure -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift - -final class SearchResultReactor: Reactor { - - // MARK: - Reactor - enum Action { - case returnSearch(text: String) - case bookmarkButtonTapped(indexPath: IndexPath) - case cellTapped(controller: BaseViewController, indexPath: IndexPath) - } - - enum Mutation { - case loadView - case emptyView - case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) - } - - struct State { - var sections: [any Sectionable] = [] - var isEmptyResult: Bool = false - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - private var popUpAPIUseCase: PopUpAPIUseCase - private let userAPIUseCase: UserAPIUseCase - lazy var compositionalLayout: UICollectionViewCompositionalLayout = { - UICollectionViewCompositionalLayout { [weak self] section, env in - guard let self = self else { - return NSCollectionLayoutSection(group: NSCollectionLayoutGroup( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .fractionalHeight(1) - )) - ) - } - return getSection()[section].getSection(section: section, env: env) - } - }() - - private var titleSection = SearchTitleSection(inputDataList: [.init(title: "포함된 팝업", buttonTitle: nil)]) - private var searchCountSection = SearchResultCountSection(inputDataList: [.init(count: 65)]) - private var searchListSection = HomeCardGridSection(inputDataList: []) - private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) - private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) - - // MARK: - init - init( - userAPIUseCase: UserAPIUseCase, - popUpAPIUseCase: PopUpAPIUseCase - ) { - self.userAPIUseCase = userAPIUseCase - self.popUpAPIUseCase = popUpAPIUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .cellTapped(let controller, let indexPath): - return Observable.just(.moveToDetailScene(controller: controller, indexPath: indexPath)) - case .returnSearch(let text): - if hasFinalConsonant(text) { - titleSection.inputDataList = [.init(title: "\(text)이 포함된 팝업")] - } else { - titleSection.inputDataList = [.init(title: "\(text)가 포함된 팝업")] - } - return popUpAPIUseCase.getSearchPopUpList(query: text) - .withUnretained(self) - .map { (owner, response) in - owner.searchCountSection.inputDataList = [.init(count: response.popUpStoreList.count)] - let isLogin = response.loginYn - owner.searchListSection.inputDataList = response.popUpStoreList.map({ response in - return .init( - imagePath: response.mainImageUrl, - id: response.id, - category: response.category, - title: response.name, - address: response.address, - startDate: response.startDate, - endDate: response.endDate, - isBookmark: response.bookmarkYn, - isLogin: isLogin - ) - }) - return .loadView - } - .catch { _ in - return Observable.just(.emptyView) - } - case .bookmarkButtonTapped(let indexPath): - let data = searchListSection.inputDataList[indexPath.row] - let isBookmark = data.isBookmark - let id = data.id - searchListSection.inputDataList[indexPath.row].isBookmark.toggle() - ToastMaker.createBookMarkToast(isBookMark: !isBookmark) - if isBookmark { - return userAPIUseCase.deleteBookmarkPopUp(popUpID: id) - .andThen(Observable.just(.loadView)) - } else { - return userAPIUseCase.postBookmarkPopUp(popUpID: id) - .andThen(Observable.just(.loadView)) - } - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .loadView: - newState.isEmptyResult = searchListSection.isEmpty - newState.sections = getSection() - case .emptyView: - newState.isEmptyResult = true - case .moveToDetailScene(let controller, let indexPath): - let nextController = DetailController() - nextController.reactor = DetailReactor( - popUpID: searchListSection.inputDataList[indexPath.row].id, - userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), - preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) - ) - controller.navigationController?.pushViewController(nextController, animated: true) - } - return newState - } - - func getSection() -> [any Sectionable] { - return [ - spacing24Section, - titleSection, - searchCountSection, - spacing16Section, - searchListSection, - spacing64Section - ] - } - func hasFinalConsonant(_ text: String) -> Bool { - guard let lastCharacter = text.last else { return false } - - let unicodeValue = Int(lastCharacter.unicodeScalars.first!.value) - - // 한글 유니코드 범위 체크 - let base = 0xAC00 - let last = 0xD7A3 - guard base...last ~= unicodeValue else { return false } - - // 종성 인덱스 계산 (받침이 있으면 1 이상) - let finalConsonantIndex = (unicodeValue - base) % 28 - return finalConsonantIndex != 0 - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift deleted file mode 100644 index 267ac61e..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSection.swift +++ /dev/null @@ -1,39 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -struct SearchResultCountSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = SearchResultCountSectionCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .absolute(20) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) -// item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(20) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - - // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 5, leading: 20, bottom: 0, trailing: 20) - - return section - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift deleted file mode 100644 index 70a240b9..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultCountSection/SearchResultCountSectionCell.swift +++ /dev/null @@ -1,56 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class SearchResultCountSectionCell: UICollectionViewCell { - - // MARK: - Components - - var disposeBag = DisposeBag() - - private let countLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13) - label.textColor = .g600 - return label - }() - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } - - override func prepareForReuse() { - super.prepareForReuse() - disposeBag = DisposeBag() - } -} - -// MARK: - SetUp -private extension SearchResultCountSectionCell { - func setUpConstraints() { - contentView.addSubview(countLabel) - countLabel.snp.makeConstraints { make in - make.leading.equalToSuperview() - make.centerY.equalToSuperview() - } - } -} - -extension SearchResultCountSectionCell: Inputable { - struct Input { - var count: Int - } - - func injection(with input: Input) { - countLabel.text = "총 \(input.count)개를 찾았어요." - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift deleted file mode 100644 index 4f1aae0b..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/AfterSearch/View/SearchResultView.swift +++ /dev/null @@ -1,49 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class SearchResultView: UIView { - - // MARK: - Components - let contentCollectionView: UICollectionView = { - return UICollectionView(frame: .zero, collectionViewLayout: .init()) - }() - - let emptyLabel: PPLabel = { - let label = PPLabel(style: .medium, fontSize: 14, text: "검색 결과가 없어요:(\n다른 키워드로 검색해주세요") - label.textAlignment = .center - label.numberOfLines = 2 - label.textColor = .g400 - return label - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension SearchResultView { - - func setUpConstraints() { - self.addSubview(contentCollectionView) - contentCollectionView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(56) - make.leading.trailing.bottom.equalToSuperview() - } - - self.addSubview(emptyLabel) - emptyLabel.snp.makeConstraints { make in - make.top.equalTo(contentCollectionView.snp.top).inset(193) - make.leading.trailing.equalToSuperview() - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift deleted file mode 100644 index 0d4b80fe..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchController.swift +++ /dev/null @@ -1,227 +0,0 @@ -import UIKit - -import DesignSystem - -import ReactorKit -import RxCocoa -import RxGesture -import RxSwift -import SnapKit - -final class SearchController: BaseViewController, View { - - typealias Reactor = SearchReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SearchView() - private var sections: [any Sectionable] = [] - private let cellTapped: PublishSubject = .init() - private let loadNextPage = PublishSubject() -} - -// MARK: - Life Cycle -extension SearchController { - override func viewDidLoad() { - super.viewDidLoad() - - self.addViews() - self.setupContstraints() - self.configureUI() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - tabBarController?.tabBar.isHidden = true - } -} - -// MARK: - SetUp -private extension SearchController { - func addViews() { - [mainView].forEach { - self.view.addSubview($0) - } - } - - func setupContstraints() { - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - if let layout = reactor?.compositionalLayout { - mainView.contentCollectionView.collectionViewLayout = layout - } - - mainView.contentCollectionView.delegate = self - mainView.contentCollectionView.dataSource = self - - mainView.contentCollectionView.register( - SearchTitleSectionCell.self, - forCellWithReuseIdentifier: SearchTitleSectionCell.identifiers - ) - - mainView.contentCollectionView.register( - SpacingSectionCell.self, - forCellWithReuseIdentifier: SpacingSectionCell.identifiers - ) - - mainView.contentCollectionView.register( - CancelableTagSectionCell.self, - forCellWithReuseIdentifier: CancelableTagSectionCell.identifiers - ) - - mainView.contentCollectionView.register( - SearchCountTitleSectionCell.self, - forCellWithReuseIdentifier: SearchCountTitleSectionCell.identifiers - ) - - mainView.contentCollectionView.register( - HomeCardSectionCell.self, - forCellWithReuseIdentifier: HomeCardSectionCell.identifiers - ) - } -} - -// MARK: - Methods -extension SearchController { - func bind(reactor: Reactor) { - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - cellTapped - .withUnretained(self) - .map({ (owner, indexPath) in - Reactor.Action.cellTapped(indexPath: indexPath, controller: owner) - }) - .bind(to: reactor.action) - .disposed(by: disposeBag) - - loadNextPage - .throttle(.seconds(1), latest: false, scheduler: MainScheduler.asyncInstance) - .map { Reactor.Action.loadNextPage } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .filter { $0.newBottomSearchList.isEmpty && $0.bottomSearchListLastIndexPath == nil } - .withUnretained(self) - .subscribe { owner, state in - owner.sections = state.sections - owner.mainView.contentCollectionView.reloadData() - } - .disposed(by: disposeBag) - - reactor.state - .map { (sections: $0.sections, - newItems: $0.newBottomSearchList, - indexPath: $0.bottomSearchListLastIndexPath) } - .filter { !$0.newItems.isEmpty && $0.indexPath != nil } - .withUnretained(self) - .subscribe { (owner, subscribeResponse) in - let (updatedSections, newPopUpItems, popUpGridindexPath) = subscribeResponse - guard let popUpGridindexPath = popUpGridindexPath else { return } - - let start = popUpGridindexPath.item - let count = newPopUpItems.count - let section = popUpGridindexPath.section - let indexPaths = (start.. Int { - return sections.count - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return sections[section].dataCount - } - - func collectionView( - _ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - guard let userDefaultService = reactor?.userDefaultService else { return cell } - let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - guard let reactor = reactor else { return cell } - - if let cell = cell as? SearchTitleSectionCell { - cell.titleButton.rx.tap - .map { Reactor.Action.recentSearchListAllDeleteButtonTapped } - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } - - if let cell = cell as? CancelableTagSectionCell { - if searchList.isEmpty { - cell.cancelButton.rx.tap - .map { Reactor.Action.categoryDelteButtonTapped(indexPath: indexPath)} - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } else { - if indexPath.section == 3 { - cell.cancelButton.rx.tap - .map { Reactor.Action.recentSearchListDeleteButtonTapped(indexPath: indexPath)} - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } else { - cell.cancelButton.rx.tap - .map { Reactor.Action.categoryDelteButtonTapped(indexPath: indexPath)} - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } - } - } - - if let cell = cell as? SearchCountTitleSectionCell { - cell.sortedButton.rx.tap - .withUnretained(self) - .map({ (owner, _) in - Reactor.Action.sortedButtonTapped(controller: owner) - }) - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } - - if let cell = cell as? HomeCardSectionCell { - cell.bookmarkButton.rx.tap - .map { Reactor.Action.bookmarkButtonTapped(indexPath: indexPath)} - .bind(to: reactor.action) - .disposed(by: cell.disposeBag) - } - - return cell - } - - func scrollViewDidScroll(_ scrollView: UIScrollView) { - mainView.endEditing(true) - let contentHeight = scrollView.contentSize.height - let scrollViewHeight = scrollView.frame.size.height - let contentOffsetY = scrollView.contentOffset.y - if contentOffsetY + scrollViewHeight >= contentHeight { - loadNextPage.onNext(()) - } - } - - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - cellTapped.onNext(indexPath) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift deleted file mode 100644 index fa83cf82..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/SearchReactor.swift +++ /dev/null @@ -1,393 +0,0 @@ -import UIKit - -import DomainInterface -import Infrastructure -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift - -final class SearchReactor: Reactor { - - // MARK: - Reactor - enum Action { - case viewWillAppear - case returnSearchKeyword(text: String?) - case recentSearchListDeleteButtonTapped(indexPath: IndexPath) - case recentSearchListAllDeleteButtonTapped - case cellTapped(indexPath: IndexPath, controller: BaseViewController) - case sortedButtonTapped(controller: BaseViewController) - case changeSortedFilterIndex(filterIndex: Int, sortedIndex: Int) - case changeCategory(categoryList: [Int], categoryTitleList: [String?]) - case categoryDelteButtonTapped(indexPath: IndexPath) - case resetCategory - case loadNextPage - case bookmarkButtonTapped(indexPath: IndexPath) - case resetSearchKeyWord - } - - enum Mutation { - case loadView - case moveToCategoryScene(controller: BaseViewController) - case moveToSortedScene(controller: BaseViewController) - case moveToDetailScene(controller: BaseViewController, indexPath: IndexPath) - case setSearchKeyWord(text: String?) - case resetSearchKeyWord - case updateBottomSearchList(newItems: [HomeCardSectionCell.Input], IndexPath: IndexPath) - } - - struct State { - var sections: [any Sectionable] = [] - var searchKeyWord: String? - var newBottomSearchList: [HomeCardSectionCell.Input] = [] - var bottomSearchListLastIndexPath: IndexPath? - - mutating func resetPaginationState() { - self.newBottomSearchList = [] - self.bottomSearchListLastIndexPath = nil - } - - mutating func updateBottomGridSection(by newItems: [HomeCardSectionCell.Input]) { - sections = sections.map { section in - if var grid = section as? HomeCardGridSection { - grid.inputDataList.append(contentsOf: newItems) - return grid - } - return section - } - } - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - - private var sortedIndex: Int = 1 - private var filterIndex: Int = 0 - - private var currentPage: Int32 = 0 - private var lastAppendPage: Int32 = 0 - private var lastPage: Int32 = 0 - private var isLoading: Bool = false - - let userDefaultService = UserDefaultService() - private let popUpAPIUseCase: PopUpAPIUseCase - private let userAPIUseCase: UserAPIUseCase - - lazy var compositionalLayout: UICollectionViewCompositionalLayout = { - UICollectionViewCompositionalLayout { [weak self] section, env in - guard let self = self else { - return NSCollectionLayoutSection(group: NSCollectionLayoutGroup( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .fractionalHeight(1) - )) - ) - } - return getSection()[section].getSection(section: section, env: env) - } - }() - - private let recentKeywordTitleSection = SearchTitleSection(inputDataList: [.init(title: "최근 검색어", buttonTitle: "모두삭제")]) - private var recentKeywordSection = CancelableTagSection(inputDataList: []) - - private let searchTitleSection = SearchTitleSection(inputDataList: [.init(title: "팝업스토어 찾기")]) - private var searchCategorySection = CancelableTagSection(inputDataList: [ - .init(title: "카테고리", isSelected: false, isCancelAble: false) - ]) - private var searchListSection = HomeCardGridSection(inputDataList: []) - private var searchSortedSection = SearchCountTitleSection(inputDataList: []) - private let spacing24Section = SpacingSection(inputDataList: [.init(spacing: 24)]) - private let spacing16Section = SpacingSection(inputDataList: [.init(spacing: 16)]) - private let spacing18Section = SpacingSection(inputDataList: [.init(spacing: 18)]) - private let spacing48Section = SpacingSection(inputDataList: [.init(spacing: 48)]) - private let spacing64Section = SpacingSection(inputDataList: [.init(spacing: 64)]) - - // MARK: - init - init( - userAPIUseCase: UserAPIUseCase, - popUpAPIUseCase: PopUpAPIUseCase - ) { - self.userAPIUseCase = userAPIUseCase - self.popUpAPIUseCase = popUpAPIUseCase - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - let sort = sortedIndex == 0 ? "NEWEST" : "MOST_VIEWED,MOST_COMMENTED,MOST_BOOKMARKED" - switch action { - case .resetSearchKeyWord: - return Observable.just(.resetSearchKeyWord) - case .loadNextPage: - guard !isLoading, currentPage < lastPage else { return Observable.empty() } - isLoading = true - currentPage += 1 - return setBottomSearchList(sort: sort) - case .viewWillAppear: - setSearchList() - return setBottomSearchList(sort: sort) - case .returnSearchKeyword(let text): - appendSearchList(text: text) - return Observable.just(.loadView) - case .recentSearchListDeleteButtonTapped(let indexPath): - removeSearchList(indexPath: indexPath) - return Observable.just(.loadView) - case .recentSearchListAllDeleteButtonTapped: - resetSearchList() - return Observable.just(.loadView) - case .cellTapped(let indexPath, let controller): - let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - let section = searchList.isEmpty ? indexPath.section + 4 : indexPath.section - switch section { - case 3: - let text = recentKeywordSection.inputDataList[indexPath.row].title - appendSearchList(text: text) - return Observable.just(.setSearchKeyWord(text: text)) - case 7: - return Observable.just(.moveToCategoryScene(controller: controller)) - case 11: - return Observable.just(.moveToDetailScene(controller: controller, indexPath: indexPath)) - default: - return Observable.just(.loadView) - } - case .sortedButtonTapped(let controller): - return Observable.just(.moveToSortedScene(controller: controller)) - case .changeSortedFilterIndex(let filterIndex, let sortedIndex): - self.sortedIndex = sortedIndex - self.filterIndex = filterIndex - self.currentPage = 0 - self.lastAppendPage = 0 - return Observable.just(.loadView) - case .changeCategory(let categoryList, let categoryTitleList): - self.currentPage = 0 - self.lastAppendPage = 0 - let datas = zip(categoryList, categoryTitleList) - searchCategorySection.inputDataList = datas.map { return .init(title: $0.1, id: $0.0, isSelected: true, isCancelAble: true)} - return Observable.just(.loadView) - case .resetCategory: - self.currentPage = 0 - self.lastAppendPage = 0 - searchCategorySection.inputDataList = [.init(title: "카테고리", isSelected: false, isCancelAble: false)] - return Observable.just(.loadView) - case .categoryDelteButtonTapped(let indexPath): - self.currentPage = 0 - self.lastAppendPage = 0 - searchCategorySection.inputDataList.remove(at: indexPath.row) - if searchCategorySection.inputDataList.isEmpty { searchCategorySection.inputDataList = [.init(title: "카테고리", isSelected: false, isCancelAble: false)] } - return setBottomSearchList(sort: sort) - case .bookmarkButtonTapped(let indexPath): - let data = searchListSection.inputDataList[indexPath.row] - let isBookmark = data.isBookmark - let id = data.id - searchListSection.inputDataList[indexPath.row].isBookmark.toggle() - if isBookmark { - return userAPIUseCase.deleteBookmarkPopUp(popUpID: id) - .andThen(Observable.just(.loadView)) - } else { - return userAPIUseCase.postBookmarkPopUp(popUpID: id) - .andThen(Observable.just(.loadView)) - } - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .loadView: - newState.sections = getSection() - newState.resetPaginationState() - case .moveToCategoryScene(let controller): - let categoryIDList = searchCategorySection.inputDataList.compactMap { $0.id } - let nextController = SearchCategoryController() - nextController.reactor = SearchCategoryReactor( - originCategoryList: categoryIDList, - signUpAPIUseCase: DIContainer.resolve(SignUpAPIUseCase.self) - ) - controller.presentPanModal(nextController) - nextController.reactor?.state - .withUnretained(self) - .subscribe(onNext: { (owner, state) in - if state.isSave { - if state.categoryTitleList.isEmpty { - owner.searchCategorySection.inputDataList = [.init(title: "카테고리", isSelected: false, isCancelAble: false)] - } else { - owner.action.onNext(.changeCategory(categoryList: state.categoryIDList, categoryTitleList: state.categoryTitleList)) - } - - } - if state.isReset { owner.action.onNext(.resetCategory)} - }) - .disposed(by: nextController.disposeBag) - case .moveToSortedScene(let controller): - let nextController = SearchSortedController() - nextController.reactor = SearchSortedReactor(filterIndex: filterIndex, sortedIndex: sortedIndex) - controller.presentPanModal(nextController) - nextController.reactor?.state - .withUnretained(self) - .subscribe(onNext: { (owner, state) in - if state.isSave { - ToastMaker.createToast(message: "선택하신 옵션을 저장했어요") - owner.action.onNext(.changeSortedFilterIndex(filterIndex: state.filterIndex, sortedIndex: state.sortedIndex)) - } - }) - .disposed(by: nextController.disposeBag) - case .moveToDetailScene(let controller, let indexPath): - let nextController = DetailController() - nextController.reactor = DetailReactor( - popUpID: searchListSection.inputDataList[indexPath.row].id, - userAPIUseCase: userAPIUseCase, - popUpAPIUseCase: popUpAPIUseCase, - commentAPIUseCase: DIContainer.resolve(CommentAPIUseCase.self), - preSignedUseCase: DIContainer.resolve(PreSignedUseCase.self) - ) - controller.navigationController?.pushViewController(nextController, animated: true) - case .setSearchKeyWord(let text): - newState.searchKeyWord = text - case .resetSearchKeyWord: - newState.searchKeyWord = nil - newState.sections = getSection() - - case .updateBottomSearchList(let newItems, let indexPath): - newState.updateBottomGridSection(by: newItems) - newState.newBottomSearchList = newItems - newState.bottomSearchListLastIndexPath = indexPath - } - return newState - } - - func getSection() -> [any Sectionable] { - let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - if searchList.isEmpty { - return [ - spacing24Section, - searchTitleSection, - spacing16Section, - searchCategorySection, - spacing18Section, - searchSortedSection, - spacing16Section, - searchListSection, - spacing64Section - ] - } else { - return [ - spacing24Section, - recentKeywordTitleSection, - spacing16Section, - recentKeywordSection, - spacing48Section, - searchTitleSection, - spacing16Section, - searchCategorySection, - spacing18Section, - searchSortedSection, - spacing16Section, - searchListSection, - spacing64Section - ] - } - } - - func setSearchList() { - let searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - recentKeywordSection.inputDataList = searchList.map { return - CancelableTagSectionCell.Input(title: $0) } - } - - func appendSearchList(text: String?) { - if let text = text { - if !text.isEmpty { - var searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - if searchList.contains(text) { - let targetIndex = searchList.firstIndex(of: text)! - searchList.remove(at: targetIndex) - } - searchList = [text] + searchList - userDefaultService.save(key: "searchList", value: searchList) - recentKeywordSection.inputDataList = searchList.map { return .init(title: $0) } - } - } - } - - func removeSearchList(indexPath: IndexPath) { - var searchList = userDefaultService.fetchArray(key: "searchList") ?? [] - searchList.remove(at: indexPath.row) - userDefaultService.save(key: "searchList", value: searchList) - recentKeywordSection.inputDataList = searchList.map { return .init(title: $0) } - } - - func resetSearchList() { - userDefaultService.save(key: "searchList", value: []) - recentKeywordSection.inputDataList = [] - } - - func setBottomSearchList(sort: String?) -> Observable { - let isOpen = filterIndex == 0 - let categories = searchCategorySection.inputDataList.compactMap { $0.id } - - return popUpAPIUseCase.getSearchBottomPopUpList( - isOpen: isOpen, - categories: categories, - page: currentPage, - size: 10, - sort: sort - ) - .withUnretained(self) - .map { (owner, response) in - // 1) 새로 받아오기 전의 기존 아이템 개수 저장 - let previousCount = owner.searchListSection.inputDataList.count - - // 2) API 결과 매핑 - let newItems = response.popUpStoreList.map { - HomeCardSectionCell.Input( - imagePath: $0.mainImageUrl, - id: $0.id, - category: $0.category, - title: $0.name, - address: $0.address, - startDate: $0.startDate, - endDate: $0.endDate, - isBookmark: $0.bookmarkYn, - isLogin: response.loginYn - ) - } - - // 3) 첫 페이지 vs 이후 페이지 분기 - if owner.currentPage == 0 { - // 첫 페이지는 전체 reload - // SearchCountTitleSection 설정 - let isOpenString = isOpen ? "오픈・" : "종료・" - let sortedString = owner.sortedIndex == 0 ? "신규순" : "인기순" - let sortedTitle = isOpenString + sortedString - owner.searchSortedSection.inputDataList = [ - SearchCountTitleSectionCell.Input( - count: response.totalElements, - sortedTitle: sortedTitle - ) - ] - owner.searchListSection.inputDataList = newItems - owner.lastAppendPage = owner.currentPage - owner.lastPage = response.totalPages - owner.isLoading = false - return .loadView - } else { - // 다음 페이지는 append 후 부분 업데이트 - owner.lastAppendPage = owner.currentPage - owner.searchListSection.inputDataList.append(contentsOf: newItems) - owner.lastPage = response.totalPages - owner.isLoading = false - - // HomeCardGridSection이 컬렉션뷰에서 몇 번째 섹션인지 계산 - let sectionIndex = owner.getSection().enumerated() - .first { _, section in section is HomeCardGridSection }!.offset - - // append된 첫 아이템의 IndexPath - let firstIndexPath = IndexPath(item: previousCount, section: sectionIndex) - return .updateBottomSearchList(newItems: newItems, IndexPath: firstIndexPath) - } - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift deleted file mode 100644 index 675b947e..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSection.swift +++ /dev/null @@ -1,38 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -struct CancelableTagSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = CancelableTagSectionCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 0) - section.interGroupSpacing = 6 - section.orthogonalScrollingBehavior = .continuous - return section - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift deleted file mode 100644 index 5156ec89..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/CancelableTagSection/CancelableTagSectionCell.swift +++ /dev/null @@ -1,105 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class CancelableTagSectionCell: UICollectionViewCell { - - // MARK: - Components - - var disposeBag = DisposeBag() - - private let titleLabel: PPLabel = { - return PPLabel(style: .medium, fontSize: 11) - }() - - let cancelButton: UIButton = { - return UIButton() - }() - - private let contentStackView: UIStackView = { - let view = UIStackView() - view.alignment = .center - view.spacing = 2 - return view - }() - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } - - override func prepareForReuse() { - super.prepareForReuse() - disposeBag = DisposeBag() - } -} - -// MARK: - SetUp -private extension CancelableTagSectionCell { - func setUpConstraints() { - contentView.layer.cornerRadius = 15.5 - contentView.clipsToBounds = true - contentView.layer.borderWidth = 1 - - contentView.addSubview(contentStackView) - contentStackView.snp.makeConstraints { make in - make.top.bottom.equalToSuperview() - make.leading.equalToSuperview().inset(12) - make.trailing.equalToSuperview().inset(8) - } - contentStackView.addArrangedSubview(titleLabel) - contentStackView.addArrangedSubview(cancelButton) - - titleLabel.snp.makeConstraints { make in - make.height.equalTo(18) - } - cancelButton.snp.makeConstraints { make in - make.size.equalTo(16) - } - } -} - -extension CancelableTagSectionCell: Inputable { - struct Input { - var title: String? - var id: Int? = nil - var isSelected: Bool = false - var isCancelAble: Bool = true - } - - func injection(with input: Input) { - let xmarkImage = input.isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") - cancelButton.setImage(xmarkImage, for: .normal) - if input.isSelected { - contentView.backgroundColor = .blu500 - titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .bold, size: 11), lineHeight: 1.15) - titleLabel.textColor = .w100 - contentView.layer.borderColor = UIColor.blu500.cgColor - } else { - contentView.backgroundColor = .clear - titleLabel.setLineHeightText(text: input.title, font: .korFont(style: .medium, size: 11), lineHeight: 1.15) - titleLabel.textColor = .g400 - contentView.layer.borderColor = UIColor.g200.cgColor - } - cancelButton.isHidden = !input.isCancelAble - - if input.isCancelAble { - contentStackView.snp.updateConstraints { make in - make.trailing.equalToSuperview().inset(8) - } - } else { - contentStackView.snp.updateConstraints { make in - make.trailing.equalToSuperview().inset(12) - } - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift deleted file mode 100644 index e72a0dd7..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSection.swift +++ /dev/null @@ -1,39 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift - -struct SearchCountTitleSection: Sectionable { - - var currentPage: PublishSubject = .init() - - typealias CellType = SearchCountTitleSectionCell - - var inputDataList: [CellType.Input] - - var supplementaryItems: [any SectionSupplementaryItemable]? - - var decorationItems: [any SectionDecorationItemable]? - - func setSection(section: Int, env: any NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection { - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1), - heightDimension: .absolute(22) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) -// item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 10, bottom: 0, trailing: 10) - - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(22) - ) - let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item]) - - // 섹션 생성 - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = .init(top: 0, leading: 20, bottom: 0, trailing: 20) - - return section - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift deleted file mode 100644 index da021fbc..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchCountTitleSection/SearchCountTitleSectionCell.swift +++ /dev/null @@ -1,93 +0,0 @@ -import UIKit - -import DesignSystem - -import RxSwift -import SnapKit - -final class SearchCountTitleSectionCell: UICollectionViewCell { - - // MARK: - Components - - var disposeBag = DisposeBag() - - private let countLabel: PPLabel = { - let label = PPLabel(style: .regular, fontSize: 13) - label.textColor = .g400 - return label - }() - - private let sortedTitleLabel: PPLabel = { - return PPLabel(style: .regular, fontSize: 13) - }() - - private let downImageView: UIImageView = { - let view = UIImageView() - view.image = UIImage(named: "icon_dropdown") - view.isUserInteractionEnabled = false - return view - }() - - let sortedButton: UIButton = { - return UIButton() - }() - - // MARK: - init - - override init(frame: CGRect) { - super.init(frame: frame) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError() - } - - override func prepareForReuse() { - super.prepareForReuse() - disposeBag = DisposeBag() - } -} - -// MARK: - SetUp -private extension SearchCountTitleSectionCell { - func setUpConstraints() { - contentView.addSubview(countLabel) - countLabel.snp.makeConstraints { make in - make.leading.equalToSuperview() - make.centerY.equalToSuperview() - } - - contentView.addSubview(sortedButton) - sortedButton.snp.makeConstraints { make in - make.trailing.equalToSuperview() - make.centerY.equalToSuperview() - } - - sortedButton.addSubview(sortedTitleLabel) - sortedTitleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview() - make.centerY.equalToSuperview() - } - - sortedButton.addSubview(downImageView) - downImageView.snp.makeConstraints { make in - make.size.equalTo(22) - make.centerY.equalToSuperview() - make.leading.equalTo(sortedTitleLabel.snp.trailing).offset(6) - make.trailing.equalToSuperview() - } - } -} - -extension SearchCountTitleSectionCell: Inputable { - struct Input { - var count: Int64 - var sortedTitle: String? - } - - func injection(with input: Input) { - sortedTitleLabel.text = input.sortedTitle - countLabel.text = "총 \(input.count)개" - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift deleted file mode 100644 index 8ffc6413..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/BeforeSearch/View/SearchView.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// SearchView.swift -// Poppool -// -// Created by SeoJunYoung on 12/4/24. -// - -import UIKit - -import SnapKit - -final class SearchView: UIView { - - // MARK: - Components - let contentCollectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()) - - // MARK: - init - init() { - super.init(frame: .zero) - - self.addSubviews() - self.setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension SearchView { - - func addSubviews() { - [contentCollectionView].forEach { - self.addSubview($0) - } - } - - func setUpConstraints() { - contentCollectionView.snp.makeConstraints { make in - make.top.equalToSuperview().offset(56) - make.leading.trailing.bottom.equalToSuperview() - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift deleted file mode 100644 index f39fe557..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryController.swift +++ /dev/null @@ -1,138 +0,0 @@ -import UIKit - -import DesignSystem - -import PanModal -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class SearchCategoryController: BaseViewController, View { - - typealias Reactor = SearchCategoryReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SearchCategoryView() - private var sections: [any Sectionable] = [] - private let cellTapped: PublishSubject = .init() -} - -// MARK: - Life Cycle -extension SearchCategoryController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } -} - -// MARK: - SetUp -private extension SearchCategoryController { - func setUp() { - if let layout = reactor?.compositionalLayout { - mainView.contentCollectionView.collectionViewLayout = layout - } - mainView.contentCollectionView.delegate = self - mainView.contentCollectionView.dataSource = self - mainView.contentCollectionView.register( - TagSectionCell.self, - forCellWithReuseIdentifier: TagSectionCell.identifiers - ) - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension SearchCategoryController { - func bind(reactor: Reactor) { - rx.viewWillAppear - .map { Reactor.Action.viewWillAppear } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - cellTapped - .map { Reactor.Action.cellTapped(indexPath: $0)} - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.resetButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.resetButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.closeButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.closeButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.saveButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.saveButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .withUnretained(self) - .subscribe { (owner, state) in - owner.sections = state.sections - owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable - owner.mainView.contentCollectionView.reloadData() - } - .disposed(by: disposeBag) - } -} - -// MARK: - UICollectionViewDelegate, UICollectionViewDataSource -extension SearchCategoryController: UICollectionViewDelegate, UICollectionViewDataSource { - func numberOfSections(in collectionView: UICollectionView) -> Int { - return sections.count - } - - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return sections[section].dataCount - } - - func collectionView( - _ collectionView: UICollectionView, - cellForItemAt indexPath: IndexPath - ) -> UICollectionViewCell { - let cell = sections[indexPath.section].getCell(collectionView: collectionView, indexPath: indexPath) - guard let reactor = reactor else { return cell } - return cell - } - - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - cellTapped.onNext(indexPath) - } -} -// MARK: - PanModalPresentable -extension SearchCategoryController: PanModalPresentable { - var panScrollable: UIScrollView? { - return nil - } - var longFormHeight: PanModalHeight { - return .intrinsicHeight - } - var shortFormHeight: PanModalHeight { - return .intrinsicHeight - } - var showDragIndicator: Bool { - return false - } - var cornerRadius: CGFloat { - return 20 - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift deleted file mode 100644 index 911d59c5..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryReactor.swift +++ /dev/null @@ -1,120 +0,0 @@ -import UIKit - -import DomainInterface -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift - -final class SearchCategoryReactor: Reactor { - - // MARK: - Reactor - enum Action { - case viewWillAppear - case closeButtonTapped(controller: BaseViewController) - case saveButtonTapped(controller: BaseViewController) - case resetButtonTapped(controller: BaseViewController) - case cellTapped(indexPath: IndexPath) - } - - enum Mutation { - case moveToRecentScene(controller: BaseViewController) - case loadView - case save(controller: BaseViewController) - case reset(controller: BaseViewController) - } - - struct State { - var sections: [any Sectionable] = [] - var categoryIDList: [Int] = [] - var categoryTitleList: [String?] = [] - var saveButtonIsEnable: Bool = false - var isSave: Bool = false - var isReset: Bool = false - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - var originCategoryList: [Int] - private let signUpAPIUseCase: SignUpAPIUseCase - private var tagSection = TagSection(inputDataList: []) - lazy var compositionalLayout: UICollectionViewCompositionalLayout = { - UICollectionViewCompositionalLayout { [weak self] section, env in - guard let self = self else { - return NSCollectionLayoutSection(group: NSCollectionLayoutGroup( - layoutSize: .init( - widthDimension: .fractionalWidth(1), - heightDimension: .fractionalHeight(1) - )) - ) - } - return getSection()[section].getSection(section: section, env: env) - } - }() - - // MARK: - init - init( - originCategoryList: [Int], - signUpAPIUseCase: SignUpAPIUseCase - ) { - self.initialState = State() - self.originCategoryList = originCategoryList - self.signUpAPIUseCase = signUpAPIUseCase - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .viewWillAppear: - return signUpAPIUseCase.fetchCategoryList() - .withUnretained(self) - .map { (owner, response) in - owner.tagSection.inputDataList = response.map { - let isSelected = owner.originCategoryList.contains($0.categoryId) - return .init(title: $0.category, isSelected: isSelected, id: $0.categoryId) - } - return .loadView - } - case .closeButtonTapped(let controller): - return Observable.just(.moveToRecentScene(controller: controller)) - case .saveButtonTapped(let controller): - return Observable.just(.save(controller: controller)) - case .cellTapped(let indexPath): - tagSection.inputDataList[indexPath.row].isSelected.toggle() - return Observable.just(.loadView) - case .resetButtonTapped(let controller): - return Observable.just(.reset(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .moveToRecentScene(let controller): - controller.dismiss(animated: true) - case .loadView: - newState.sections = getSection() - let selectedList = tagSection.inputDataList.filter { $0.isSelected }.compactMap { $0.id }.sorted(by: <) - let originList = originCategoryList.sorted(by: <) - newState.saveButtonIsEnable = selectedList != originList - newState.categoryIDList = selectedList - newState.categoryTitleList = tagSection.inputDataList.filter { $0.isSelected }.map { $0.title } - case .save(let controller): - newState.isSave = true - controller.dismiss(animated: true) - case .reset(let controller): - newState.isReset = true - controller.dismiss(animated: true) - } - return newState - } - - func getSection() -> [any Sectionable] { - return [ - tagSection - ] - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift deleted file mode 100644 index d6ac716e..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/CategoryController/SearchCategoryView.swift +++ /dev/null @@ -1,84 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class SearchCategoryView: UIView { - - // MARK: - Components - private let titleLabel: PPLabel = { - return PPLabel(style: .bold, fontSize: 18, text: "카테고리를 선택해주세요") - }() - - let closeButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "icon_xmark"), for: .normal) - return button - }() - - let contentCollectionView: UICollectionView = { - let view = UICollectionView(frame: .zero, collectionViewLayout: .init()) - view.isScrollEnabled = false - return view - }() - - let buttonStackView: UIStackView = { - let view = UIStackView() - view.distribution = .fillEqually - view.spacing = 12 - return view - }() - - let resetButton: PPButton = { - return PPButton(style: .secondary, text: "초기화") - }() - - let saveButton: PPButton = { - return PPButton(style: .primary, text: "옵션저장", disabledText: "옵션저장") - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension SearchCategoryView { - - func setUpConstraints() { - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.top.equalToSuperview().inset(12) - } - - self.addSubview(closeButton) - closeButton.snp.makeConstraints { make in - make.size.equalTo(24) - make.trailing.equalToSuperview().inset(20) - make.centerY.equalTo(titleLabel) - } - self.addSubview(contentCollectionView) - contentCollectionView.snp.makeConstraints { make in - make.leading.trailing.equalToSuperview() - make.top.equalTo(titleLabel.snp.bottom).offset(10) - make.height.equalTo(195) - } - self.addSubview(buttonStackView) - buttonStackView.addArrangedSubview(resetButton) - buttonStackView.addArrangedSubview(saveButton) - buttonStackView.snp.makeConstraints { make in - make.top.equalTo(contentCollectionView.snp.bottom).offset(32) - make.leading.trailing.equalToSuperview().inset(20) - make.height.equalTo(50) - make.bottom.equalToSuperview() - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift deleted file mode 100644 index 9b2af457..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainController.swift +++ /dev/null @@ -1,188 +0,0 @@ -import UIKit - -import DomainInterface -import Infrastructure -import DesignSystem - -import Pageboy -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit -import Tabman -import Then - -final class SearchMainController: BaseTabmanController, View { - - typealias Reactor = SearchMainReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SearchMainView() - - var beforeController = SearchController().then { - $0.reactor = SearchReactor( - userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) - ) - } - - var afterController = SearchResultController().then { - $0.reactor = SearchResultReactor( - userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), - popUpAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self) - ) - } - - lazy var controllers = [ - beforeController, - afterController - ] - - var isResponseTextField: Bool = false -} - -// MARK: - Life Cycle -extension SearchMainController { - override func viewDidLoad() { - super.viewDidLoad() - - self.addViews() - self.setupConstraints() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - if !isResponseTextField { - mainView.searchTextField.becomeFirstResponder() - isResponseTextField = true - } - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - tabBarController?.tabBar.isHidden = true - } -} - -// MARK: - SetUp -private extension SearchMainController { - func addViews() { - [mainView] - .forEach { self.view.addSubview($0) } - } - - func setupConstraints() { - self.dataSource = self - self.isScrollEnabled = false - - mainView.snp.makeConstraints { make in - make.top.leading.trailing.equalTo(view.safeAreaLayoutGuide) - make.height.equalTo(56) - } - } -} - -// MARK: - Methods -extension SearchMainController { - func bind(reactor: Reactor) { - beforeController.reactor?.state - .withUnretained(self) - .subscribe(onNext: { (owner, state) in - owner.view.endEditing(true) - if let text = state.searchKeyWord { - if let index = owner.currentIndex { - if index == 0 { - owner.scrollToPage(.at(index: 1), animated: false) - reactor.action.onNext(.returnSearchKeyWord(text: text)) - owner.mainView.searchTextField.text = state.searchKeyWord - } - } - } - }) - .disposed(by: disposeBag) - - mainView.searchTextField.rx.controlEvent(.editingDidEndOnExit) - .withLatestFrom(mainView.searchTextField.rx.text.orEmpty) - .withUnretained(self) - .subscribe(onNext: { (owner, query) in - owner.view.endEditing(true) - // 텍스트가 비어있지 않으면 페이지 전환 - if !query.isEmpty { - owner.scrollToPage(.at(index: 1), animated: false) - } - owner.reactor?.action.onNext(.returnSearchKeyWord(text: query)) - owner.beforeController.reactor?.action.onNext(.returnSearchKeyword(text: query)) - }) - .disposed(by: disposeBag) - - mainView.searchTextField.rx.text - .withUnretained(self) - .subscribe(onNext: { (owner, text) in - if let text = text { - owner.mainView.clearButton.isHidden = text.isEmpty - } else { - owner.mainView.clearButton.isHidden = true - } - }) - .disposed(by: disposeBag) - - mainView.cancelButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - owner.view.endEditing(true) - if owner.currentIndex == 1 { - owner.mainView.searchTextField.text = nil - owner.beforeController.reactor?.action.onNext(.resetSearchKeyWord) - owner.scrollToPage(.at(index: 0), animated: false) - } else { - owner.navigationController?.popViewController(animated: true) - } - return Reactor.Action.returnSearchKeyWord(text: nil) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.clearButton.rx.tap - .withUnretained(self) - .subscribe { (owner, _) in - owner.mainView.searchTextField.text = nil - owner.mainView.clearButton.isHidden = true - } - .disposed(by: disposeBag) - - reactor.state - .withUnretained(self) - .subscribe { (owner, state) in - if let text = state.searchKeyword { - owner.afterController.reactor?.action.onNext(.returnSearch(text: text)) - } - } - .disposed(by: disposeBag) - } -} - -extension SearchMainController: PageboyViewControllerDataSource, TMBarDataSource { - func barItem(for bar: any Tabman.TMBar, at index: Int) -> any Tabman.TMBarItemable { - return TMBarItem(title: "") - } - - func numberOfViewControllers(in pageboyViewController: Pageboy.PageboyViewController) -> Int { - return controllers.count - } - - func viewController( - for pageboyViewController: Pageboy.PageboyViewController, - at index: Pageboy.PageboyViewController.PageIndex - ) -> UIViewController? { - return controllers[index] - } - - func defaultPage( - for pageboyViewController: Pageboy.PageboyViewController - ) -> Pageboy.PageboyViewController.Page? { - if let currentIndex = currentIndex { return .at(index: currentIndex) } - return .at(index: 0) - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift deleted file mode 100644 index 25c83527..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainReactor.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// SearchMainReactor.swift -// Poppool -// -// Created by SeoJunYoung on 12/7/24. -// - -import ReactorKit -import RxCocoa -import RxSwift - -final class SearchMainReactor: Reactor { - - // MARK: - Reactor - enum Action { - case returnSearchKeyWord(text: String?) - } - - enum Mutation { - case setSearchKeyWord(text: String?) - } - - struct State { - var searchKeyword: String? - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - // MARK: - init - init() { - self.initialState = State() - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .returnSearchKeyWord(let text): - return Observable.just(.setSearchKeyWord(text: text)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .setSearchKeyWord(let text): - newState.searchKeyword = text - } - return newState - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift deleted file mode 100644 index 4946a606..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/Main/SearchMainView.swift +++ /dev/null @@ -1,109 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit -import Then - -final class SearchMainView: UIView { - - // MARK: - Components - private let searchTrailingView = UIView().then { - $0.backgroundColor = .g50 - $0.layer.cornerRadius = 4 - $0.clipsToBounds = true - } - - private let searchIconImageView = UIImageView().then { - $0.image = UIImage(named: "icon_search_gray") - } - - private let searchStackView = UIStackView().then { - $0.spacing = 4 - $0.alignment = .center - } - - let cancelButton = UIButton(type: .system).then { - $0.setTitle("취소", for: .normal) - $0.setTitleColor(.g1000, for: .normal) - $0.titleLabel?.font = .korFont(style: .regular, size: 14) - $0.imageView?.contentMode = .scaleAspectFit - } - - let searchTextField = UITextField().then { - $0.font = .korFont(style: .regular, size: 14) - $0.setPlaceholder( - text: "팝업스토어명을 입력해보세요", - color: .g400, - font: .korFont(style: .regular, size: 14) - ) - } - - let clearButton = UIButton().then { - $0.setImage(UIImage(named: "icon_clear_button"), for: .normal) - } - - private var headerStackView = UIStackView().then { - $0.alignment = .center - $0.spacing = 16 - } - - // MARK: - init - init() { - super.init(frame: .zero) - - self.addViews() - self.setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("\(#file), \(#function) Error") - } -} - -// MARK: - SetUp -private extension SearchMainView { - - func addViews() { - [headerStackView] - .forEach { self.addSubview($0) } - - [searchTrailingView, cancelButton] - .forEach { headerStackView.addArrangedSubview($0) } - - [searchStackView] - .forEach { searchTrailingView.addSubview($0) } - - [searchIconImageView, searchTextField, clearButton] - .forEach { searchStackView.addArrangedSubview($0) } - } - - func setUpConstraints() { - searchTrailingView.snp.makeConstraints { make in - make.height.equalTo(37) - } - - headerStackView.snp.makeConstraints { make in - make.top.equalToSuperview().inset(7) - make.leading.equalToSuperview().inset(20) - make.trailing.equalToSuperview().inset(16) - } - - searchStackView.snp.makeConstraints { make in - make.top.bottom.equalToSuperview() - make.leading.trailing.equalToSuperview().inset(12) - } - - searchIconImageView.snp.makeConstraints { make in - make.size.equalTo(20) - } - - searchTextField.snp.makeConstraints { make in - make.height.equalTo(21) - } - - clearButton.snp.makeConstraints { make in - make.size.equalTo(16) - } - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift deleted file mode 100644 index 2501bb22..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedController.swift +++ /dev/null @@ -1,102 +0,0 @@ -import UIKit - -import DesignSystem - -import PanModal -import ReactorKit -import RxCocoa -import RxSwift -import SnapKit - -final class SearchSortedController: BaseViewController, View { - - typealias Reactor = SearchSortedReactor - - // MARK: - Properties - var disposeBag = DisposeBag() - - private var mainView = SearchSortedView() -} - -// MARK: - Life Cycle -extension SearchSortedController { - override func viewDidLoad() { - super.viewDidLoad() - setUp() - } -} - -// MARK: - SetUp -private extension SearchSortedController { - func setUp() { - view.addSubview(mainView) - mainView.snp.makeConstraints { make in - make.edges.equalTo(view.safeAreaLayoutGuide) - } - } -} - -// MARK: - Methods -extension SearchSortedController { - func bind(reactor: Reactor) { - mainView.closeButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.closeButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.filterSegmentControl.rx.controlEvent(.valueChanged) - .withUnretained(self) - .map({ (owner, _) in - Reactor.Action.changeFilterIndex(index: owner.mainView.filterSegmentControl.selectedSegmentIndex) - }) - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.sortedSegmentControl.rx.controlEvent(.valueChanged) - .withUnretained(self) - .map({ (owner, _) in - Reactor.Action.changeSortedIndex(index: owner.mainView.sortedSegmentControl.selectedSegmentIndex) - }) - .bind(to: reactor.action) - .disposed(by: disposeBag) - - mainView.saveButton.rx.tap - .withUnretained(self) - .map { (owner, _) in - Reactor.Action.saveButtonTapped(controller: owner) - } - .bind(to: reactor.action) - .disposed(by: disposeBag) - - reactor.state - .withUnretained(self) - .subscribe { (owner, state) in - owner.mainView.filterSegmentControl.selectedSegmentIndex = state.filterIndex - owner.mainView.sortedSegmentControl.selectedSegmentIndex = state.sortedIndex - owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable - } - .disposed(by: disposeBag) - } -} - -// MARK: - PanModalPresentable -extension SearchSortedController: PanModalPresentable { - var panScrollable: UIScrollView? { - return nil - } - var longFormHeight: PanModalHeight { - return .intrinsicHeight - } - var shortFormHeight: PanModalHeight { - return .intrinsicHeight - } - var showDragIndicator: Bool { - return false - } - var cornerRadius: CGFloat { - return 20 - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift deleted file mode 100644 index e2bc6d42..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedReactor.swift +++ /dev/null @@ -1,84 +0,0 @@ -import DesignSystem - -import ReactorKit -import RxCocoa -import RxSwift - -final class SearchSortedReactor: Reactor { - - // MARK: - Reactor - enum Action { - case closeButtonTapped(controller: BaseViewController) - case changeFilterIndex(index: Int) - case changeSortedIndex(index: Int) - case saveButtonTapped(controller: BaseViewController) - } - - enum Mutation { - case moveToRecentScene(controller: BaseViewController) - case loadView - case save(controller: BaseViewController) - } - - struct State { - var filterIndex: Int - var sortedIndex: Int - var saveButtonIsEnable: Bool = false - var isSave: Bool = false - } - - // MARK: - properties - - var initialState: State - var disposeBag = DisposeBag() - var originFilterIndex: Int - var originSortedIndex: Int - private var selectedFilterIndex: Int - private var selectedSortedIndex: Int - - // MARK: - init - init(filterIndex: Int, sortedIndex: Int) { - self.initialState = State(filterIndex: filterIndex, sortedIndex: sortedIndex) - self.originFilterIndex = filterIndex - self.originSortedIndex = sortedIndex - self.selectedFilterIndex = filterIndex - self.selectedSortedIndex = sortedIndex - } - - // MARK: - Reactor Methods - func mutate(action: Action) -> Observable { - switch action { - case .closeButtonTapped(let controller): - return Observable.just(.moveToRecentScene(controller: controller)) - case .changeFilterIndex(let index): - selectedFilterIndex = index - return Observable.just(.loadView) - case .changeSortedIndex(let index): - selectedSortedIndex = index - return Observable.just(.loadView) - case .saveButtonTapped(let controller): - return Observable.just(.save(controller: controller)) - } - } - - func reduce(state: State, mutation: Mutation) -> State { - var newState = state - switch mutation { - case .moveToRecentScene(let controller): - controller.dismiss(animated: true) - case .loadView: - newState.filterIndex = selectedFilterIndex - newState.sortedIndex = selectedSortedIndex - - if selectedFilterIndex != originFilterIndex || selectedSortedIndex != originSortedIndex { - newState.saveButtonIsEnable = true - } else { - newState.saveButtonIsEnable = false - } - case .save(let controller): - newState.isSave = true - controller.dismiss(animated: true) - } - return newState - } -} diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift deleted file mode 100644 index 01f5471e..00000000 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Search/SortedController/SearchSortedView.swift +++ /dev/null @@ -1,99 +0,0 @@ -import UIKit - -import DesignSystem - -import SnapKit - -final class SearchSortedView: UIView { - - // MARK: - Components - private let titleLabel: PPLabel = { - return PPLabel(style: .bold, fontSize: 18, text: "노출 순서를 선택해주세요") - }() - - let closeButton: UIButton = { - let button = UIButton() - button.setImage(UIImage(named: "icon_xmark"), for: .normal) - return button - }() - - private let filterTitleLabel: PPLabel = { - return PPLabel(style: .regular, fontSize: 13, text: "노출 조건") - }() - - let filterSegmentControl: PPSegmentedControl = { - return PPSegmentedControl(type: .base, segments: ["오픈", "종료"], selectedSegmentIndex: 0) - }() - - private let sortedTitleLabel: PPLabel = { - return PPLabel(style: .regular, fontSize: 13, text: "팝업순서") - }() - - let sortedSegmentControl: PPSegmentedControl = { - return PPSegmentedControl(type: .base, segments: ["신규순", "인기순"], selectedSegmentIndex: 0) - }() - - let saveButton: PPButton = { - return PPButton(style: .primary, text: "저장", disabledText: "저장") - }() - - // MARK: - init - init() { - super.init(frame: .zero) - setUpConstraints() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - SetUp -private extension SearchSortedView { - - func setUpConstraints() { - self.addSubview(titleLabel) - titleLabel.snp.makeConstraints { make in - make.leading.equalToSuperview().inset(20) - make.top.equalToSuperview().inset(32) - } - - self.addSubview(closeButton) - closeButton.snp.makeConstraints { make in - make.size.equalTo(24) - make.trailing.equalToSuperview().inset(20) - make.centerY.equalTo(titleLabel) - } - - self.addSubview(filterTitleLabel) - filterTitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(36) - make.leading.equalToSuperview().inset(20) - } - - self.addSubview(filterSegmentControl) - filterSegmentControl.snp.makeConstraints { make in - make.top.equalTo(filterTitleLabel.snp.bottom).offset(8) - make.leading.trailing.equalToSuperview().inset(20) - } - - self.addSubview(sortedTitleLabel) - sortedTitleLabel.snp.makeConstraints { make in - make.top.equalTo(filterSegmentControl.snp.bottom).offset(20) - make.leading.equalToSuperview().inset(20) - } - - self.addSubview(sortedSegmentControl) - sortedSegmentControl.snp.makeConstraints { make in - make.top.equalTo(sortedTitleLabel.snp.bottom).offset(8) - make.leading.trailing.equalToSuperview().inset(20) - } - - self.addSubview(saveButton) - saveButton.snp.makeConstraints { make in - make.top.equalTo(sortedSegmentControl.snp.bottom).offset(32) - make.leading.trailing.equalToSuperview().inset(20) - make.bottom.equalToSuperview() - } - } -} From 107ea1b6668ec4a65f793a1cbe8b0a27181a81b5 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 23:12:52 +0900 Subject: [PATCH 344/393] =?UTF-8?q?fix/#131:=20=EB=8D=B0=EB=AA=A8=EC=95=B1?= =?UTF-8?q?=20=EC=8B=A4=EA=B8=B0=EA=B8=B0=20@rPath=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 427e6c8c..589fef98 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,6 +7,11 @@ objects = { /* Begin PBXBuildFile section */ + 054A96202DCE38B500C0DD58 /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; + 054A96212DCE38B500C0DD58 /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 054A96262DCE38E900C0DD58 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 054A96252DCE38E900C0DD58 /* Tabman */; }; + 054A96282DCE390A00C0DD58 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 054A96272DCE390A00C0DD58 /* ReactorKit */; }; + 054A962B2DCE3A0300C0DD58 /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 054A962A2DCE3A0300C0DD58 /* NMapsMap */; }; 05734C082DCDA7D20093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; }; @@ -51,6 +56,13 @@ remoteGlobalIDString = 0516336C2DC457A900A6C0D1; remoteInfo = SearchFeature; }; + 054A96222DCE38B500C0DD58 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 051633642DC457A900A6C0D1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 05734BF42DCDA6B90093825D; + remoteInfo = SearchFeatureInterface; + }; 05734C0A2DCDA7D20093825D /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 051633642DC457A900A6C0D1 /* Project object */; @@ -93,6 +105,7 @@ 05CFFBED2DCB90440051129F /* Domain.framework in Embed Frameworks */, 05CFFBEB2DCB90210051129F /* Data.framework in Embed Frameworks */, 05734C512DCDF8B40093825D /* PresentationInterface.framework in Embed Frameworks */, + 054A96212DCE38B500C0DD58 /* SearchFeatureInterface.framework in Embed Frameworks */, 05CFFBF12DCB90580051129F /* Infrastructure.framework in Embed Frameworks */, 05CFFBF52DCB908B0051129F /* DesignSystem.framework in Embed Frameworks */, 05CFFBEF2DCB90460051129F /* DomainInterface.framework in Embed Frameworks */, @@ -181,12 +194,16 @@ 05734C532DCDF8B80093825D /* Presentation.framework in Frameworks */, 05CFFBEC2DCB90440051129F /* Domain.framework in Frameworks */, 05CFFBEA2DCB90210051129F /* Data.framework in Frameworks */, + 054A962B2DCE3A0300C0DD58 /* NMapsMap in Frameworks */, 05CFFBF32DCB906F0051129F /* RxCocoa in Frameworks */, 058AE08D2DCCC27F009119B2 /* Then in Frameworks */, 05CFFBF02DCB90580051129F /* Infrastructure.framework in Frameworks */, + 054A96202DCE38B500C0DD58 /* SearchFeatureInterface.framework in Frameworks */, + 054A96262DCE38E900C0DD58 /* Tabman in Frameworks */, 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */, 05CFFBE92DCB90070051129F /* RxSwift in Frameworks */, 05734C502DCDF8B40093825D /* PresentationInterface.framework in Frameworks */, + 054A96282DCE390A00C0DD58 /* ReactorKit in Frameworks */, 05CFFBE52DCB8F6C0051129F /* SearchFeature.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -307,6 +324,7 @@ ); dependencies = ( 0516364E2DC45E6300A6C0D1 /* PBXTargetDependency */, + 054A96232DCE38B500C0DD58 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( 051636302DC457DE00A6C0D1 /* SearchFeatureDemo */, @@ -317,6 +335,9 @@ 05CFFBF22DCB906F0051129F /* RxCocoa */, 05CFFBF62DCB90A10051129F /* SnapKit */, 058AE08C2DCCC27F009119B2 /* Then */, + 054A96252DCE38E900C0DD58 /* Tabman */, + 054A96272DCE390A00C0DD58 /* ReactorKit */, + 054A962A2DCE3A0300C0DD58 /* NMapsMap */, ); productName = SearchFeatureDemo; productReference = 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */; @@ -382,6 +403,8 @@ 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */, 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */, 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */, + 054A96242DCE38E900C0DD58 /* XCRemoteSwiftPackageReference "Tabman" */, + 054A96292DCE3A0300C0DD58 /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */, ); preferredProjectObjectVersion = 77; productRefGroup = 0516336E2DC457A900A6C0D1 /* Products */; @@ -449,6 +472,11 @@ target = 0516336C2DC457A900A6C0D1 /* SearchFeature */; targetProxy = 0516364D2DC45E6300A6C0D1 /* PBXContainerItemProxy */; }; + 054A96232DCE38B500C0DD58 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 05734BF42DCDA6B90093825D /* SearchFeatureInterface */; + targetProxy = 054A96222DCE38B500C0DD58 /* PBXContainerItemProxy */; + }; 05734C0B2DCDA7D20093825D /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 05734BF42DCDA6B90093825D /* SearchFeatureInterface */; @@ -853,6 +881,22 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ + 054A96242DCE38E900C0DD58 /* XCRemoteSwiftPackageReference "Tabman" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/uias/Tabman"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.2.0; + }; + }; + 054A96292DCE3A0300C0DD58 /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/navermaps/SPM-NMapsMap"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.21.0; + }; + }; 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/ReactiveX/RxSwift"; @@ -888,6 +932,21 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 054A96252DCE38E900C0DD58 /* Tabman */ = { + isa = XCSwiftPackageProductDependency; + package = 054A96242DCE38E900C0DD58 /* XCRemoteSwiftPackageReference "Tabman" */; + productName = Tabman; + }; + 054A96272DCE390A00C0DD58 /* ReactorKit */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233F2DC49A8B00C761A5 /* XCRemoteSwiftPackageReference "ReactorKit" */; + productName = ReactorKit; + }; + 054A962A2DCE3A0300C0DD58 /* NMapsMap */ = { + isa = XCSwiftPackageProductDependency; + package = 054A96292DCE3A0300C0DD58 /* XCRemoteSwiftPackageReference "SPM-NMapsMap" */; + productName = NMapsMap; + }; 058AE08C2DCCC27F009119B2 /* Then */ = { isa = XCSwiftPackageProductDependency; package = 05EC23492DC49AB400C761A5 /* XCRemoteSwiftPackageReference "Then" */; From 2af8b1615ed05ee76951d44f33b5bbd2df3c7544 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 23:14:27 +0900 Subject: [PATCH 345/393] =?UTF-8?q?fix/#131:=20=EA=B2=80=EC=83=89=EC=97=90?= =?UTF-8?q?=20=EC=84=B1=EA=B3=B5=ED=96=88=EC=9D=84=EB=95=8C=EB=A7=8C=20?= =?UTF-8?q?=ED=82=A4=EC=9B=8C=EB=93=9C=EB=A5=BC=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=ED=95=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 - 현재 API에서 한글자일때는 request에 항상 실패하는것 같음 - 키워드는 저장하고 결과는 받지 못하고 있어서 리액터에서 아이템과 키체인에 아이템이 달라짐 - 이로 인해 키워드를 탭했을 때 검색창에 다른 아이템이 전달되던 문제를 방지 - 별도의 요구사항이 필요한 부분으로 보임 - 사용자의 입장에서는 검색이 실패했을 때 어떤 이유인지 모른채 화면만 안넘어가는 상황이 됨 --- .../RepositoryImpl/Search/SearchAPIRepositoryImpl.swift | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift index 1526ed6b..cc6c9825 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift @@ -20,14 +20,15 @@ public final class SearchAPIRepositoryImpl: SearchAPIRepository { } public func fetchSearchResult(by query: String) -> Observable { - self.saveSearchKeyword(keyword: query) let request = GetSearchPopupStoreRequestDTO(query: query) let endPoint = SearchAPIEndPoint.getSearchPopUpList(request: request) - return provider.requestData( + return provider.requestData( // 실패했을때는 키워드 저장이 안되도록 수정 with: endPoint, interceptor: tokenInterceptor - ).map { $0.toDomain() } + ) + .map { $0.toDomain() } + .do { _ in self.saveSearchKeyword(keyword: query) } } } From c19b503220dc2e4c3e189d895aa11dfd9306542d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 23:14:44 +0900 Subject: [PATCH 346/393] =?UTF-8?q?refactor/#131:=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=EC=9D=98=20=EC=9D=B4=EB=A6=84=EC=9D=84=20=EB=8D=94=20?= =?UTF-8?q?=EC=A0=95=ED=99=95=ED=95=98=EA=B2=8C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Source/Reactor/PopupSearchReactor.swift | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 23cb87c2..3269d749 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -179,7 +179,7 @@ public final class PopupSearchReactor: Reactor { else { return .just(.present(target: .before)) } case .recentSearchTagButtonTapped(let indexPath): - let keyword = self.makeRecentSearchItem(at: indexPath) + let keyword = self.findRecentSearchKeyword(at: indexPath) return fetchSearchResult(keyword: keyword) .withUnretained(self) .flatMap { (owner, response) -> Observable in @@ -364,10 +364,11 @@ private extension PopupSearchReactor { // MARK: - Make Functions private extension PopupSearchReactor { - func makeRecentSearchItem(at indexPath: IndexPath) -> String? { - guard let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword), - searchKeywords.indices.contains(indexPath.item) else { return nil } - return searchKeywords[indexPath.item] + func findRecentSearchKeyword(at indexPath: IndexPath) -> String? { + guard currentState.recentSearchItems.indices.contains(indexPath.item) + else { return nil } + + return currentState.recentSearchItems[indexPath.item].title } func makeRecentSearchItems() -> [TagModel] { From 41436823a3c30d412c4a1ba6352db0237eb60c82 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 9 May 2025 23:15:04 +0900 Subject: [PATCH 347/393] =?UTF-8?q?refactor/#131:=20Cell=20reuse=EB=A5=BC?= =?UTF-8?q?=20=EC=9C=84=ED=95=B4=20=EA=B8=B0=EC=A1=B4=EC=9D=98=20cell?= =?UTF-8?q?=EC=9D=84=20=EB=B9=84=EC=9A=B0=EB=8F=84=EB=A1=9D=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TagCollectionViewCell.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift index 2bfdda5e..4a626ea7 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift @@ -36,6 +36,16 @@ public final class TagCollectionViewCell: UICollectionViewCell { public override func prepareForReuse() { super.prepareForReuse() + + configureCell( + title: nil, + id: nil, + isSelected: false, + isCancelable: false, + fontSize: 11, + cornerRadius: 15.5 + ) + disposeBag = DisposeBag() } } From 8c231c9b7e677738983d807574f4abe61e8989fc Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 10 May 2025 02:01:40 +0900 Subject: [PATCH 348/393] =?UTF-8?q?feat/#131:=20=EB=B6=81=EB=A7=88?= =?UTF-8?q?=ED=81=AC=20=EB=B2=84=ED=8A=BC=EC=9D=84=20=ED=83=AD=ED=96=88?= =?UTF-8?q?=EC=9D=84=EB=95=8C=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PPPopupGridCollectionViewCell.swift | 4 ++-- .../Scene/Home/Main/HomeReactor.swift | 3 --- .../FactoryImpl/PopupSearchFactoryImpl.swift | 1 + .../Source/Reactor/PopupSearchReactor.swift | 24 +++++++++++++++++++ .../Source/View/PopupSearchView.swift | 7 ++++++ .../View/PopupSearchViewController.swift | 5 ++++ 6 files changed, 39 insertions(+), 5 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift index 231f7e14..57497186 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift @@ -9,7 +9,7 @@ public final class PPPopupGridCollectionViewCell: UICollectionViewCell { // MARK: - Properties - var disposeBag = DisposeBag() + public var disposeBag = DisposeBag() private let imageView = UIImageView().then { $0.contentMode = .scaleAspectFill @@ -39,7 +39,7 @@ public final class PPPopupGridCollectionViewCell: UICollectionViewCell { $0.setLineHeightText(text: "date", font: .korFont(style: .medium, size: 11)) } - let bookmarkButton = UIButton() + public let bookmarkButton = UIButton() private let rankLabel = UILabel().then { $0.backgroundColor = .w10 diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift index 6a58e109..4a45bd65 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeReactor.swift @@ -145,9 +145,6 @@ final class HomeReactor: Reactor { case .moveToSearchScene(let controller): @Dependency var factory: PopupSearchFactory controller.navigationController?.pushViewController(factory.make(), animated: true) -// let nextController = SearchMainController() -// nextController.reactor = SearchMainReactor() -// controller.navigationController?.pushViewController(nextController, animated: true) case .loadView: newState.isReloadView = true newState.sections = getSection() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift index 18fddd85..a352ad86 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift @@ -11,6 +11,7 @@ public final class PopupSearchFactoryImpl: PopupSearchFactory { viewController.reactor = PopupSearchReactor( popupAPIUseCase: DIContainer.resolve(PopUpAPIUseCase.self), + userAPIUseCase: DIContainer.resolve(UserAPIUseCase.self), fetchKeywordBasePopupListUseCase: DIContainer.resolve(FetchKeywordBasePopupListUseCase.self) ) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 3269d749..471537bb 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -30,6 +30,7 @@ public final class PopupSearchReactor: Reactor { case searchResultFilterButtonTapped case searchResultFilterChangedBySelector case searchResultItemTapped(indexPath: IndexPath) + case searchResultBookmarkButtonTapped(indexPath: IndexPath) case searchResultPrefetchItems(indexPathList: [IndexPath]) } @@ -48,6 +49,7 @@ public final class PopupSearchReactor: Reactor { case updateCurrentPage(to: Int32) case updateSearchingState(to: Bool) case updateSearchResultEmptyTitle + case updateSearchResultBookmark(indexPath: IndexPath) case updateDataSource case present(target: PresentTarget) @@ -88,14 +90,17 @@ public final class PopupSearchReactor: Reactor { private let userDefaultService = UserDefaultService() private let popupAPIUseCase: PopUpAPIUseCase + private let userAPIUseCase: UserAPIUseCase private let fetchKeywordBasePopupListUseCase: FetchKeywordBasePopupListUseCase // MARK: - init public init( popupAPIUseCase: PopUpAPIUseCase, + userAPIUseCase: UserAPIUseCase, fetchKeywordBasePopupListUseCase: FetchKeywordBasePopupListUseCase ) { self.popupAPIUseCase = popupAPIUseCase + self.userAPIUseCase = userAPIUseCase self.fetchKeywordBasePopupListUseCase = fetchKeywordBasePopupListUseCase self.initialState = State() } @@ -273,6 +278,13 @@ public final class PopupSearchReactor: Reactor { case .searchResultItemTapped(let indexPath): return .just(.present(target: .popupDetail(popupID: self.findPopupStoreID(at: indexPath)))) + case .searchResultBookmarkButtonTapped(let indexPath): + return fetchSearchResultBookmark(at: indexPath) + .andThen(.concat([ + .just(.updateSearchResultBookmark(indexPath: indexPath)), + .just(.updateDataSource) + ])) + case .searchResultPrefetchItems(let indexPathList): guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } return fetchSearchResult(page: currentState.currentPage + 1) @@ -326,6 +338,9 @@ public final class PopupSearchReactor: Reactor { case .updateSearchResultEmptyTitle: newState.searchResultEmptyTitle = makeSearchResultEmptyTitle(state: newState) + case .updateSearchResultBookmark(let indexPath): + newState.searchResultItems[indexPath.item].isBookmark.toggle() + case .updateDataSource: newState.updateDataSource = () @@ -360,6 +375,15 @@ private extension PopupSearchReactor { guard let keyword else { return .empty() } return fetchKeywordBasePopupListUseCase.execute(keyword: keyword) } + + func fetchSearchResultBookmark(at indexPath: IndexPath) -> Completable { + let popupID = currentState.searchResultItems[indexPath.item].id + if currentState.searchResultItems[indexPath.item].isBookmark { + return userAPIUseCase.deleteBookmarkPopUp(popUpID: popupID) + } else { + return userAPIUseCase.postBookmarkPopUp(popUpID: popupID) + } + } } // MARK: - Make Functions diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift index 50aee0a1..206855e6 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift @@ -37,6 +37,7 @@ final class PopupSearchView: UIView { let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() let filterStatusButtonTapped = PublishRelay() + let bookmarkButtonTapped = PublishRelay() let tapGestureRecognizer = UITapGestureRecognizer().then { $0.cancelsTouchesInView = false @@ -178,6 +179,11 @@ extension PopupSearchView { ) as! PPPopupGridCollectionViewCell cell.configureCell(imagePath: item.imagePath, id: item.id, category: item.category, title: item.title, address: item.address, startDate: item.startDate, endDate: item.endDate, isBookmark: item.isBookmark, isLogin: item.isLogin, isPopular: item.isPopular, row: item.row) + cell.bookmarkButton.rx.tap + .map { indexPath } + .bind(to: self.bookmarkButtonTapped) + .disposed(by: cell.disposeBag) + return cell case .searchResultEmptyTitle(let title): @@ -186,6 +192,7 @@ extension PopupSearchView { for: indexPath ) as! SearchResultEmptyTitleCollectionViewCell cell.configureCell(title: title) + return cell } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift index b879e965..5eec2223 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift @@ -122,6 +122,11 @@ extension PopupSearchViewController { .bind(to: reactor.action) .disposed(by: disposeBag) + mainView.bookmarkButtonTapped + .map(Reactor.Action.searchResultBookmarkButtonTapped) + .bind(to: reactor.action) + .disposed(by: disposeBag) + mainView.collectionView.rx.prefetchItems .throttle(.milliseconds(100), latest: false, scheduler: MainScheduler.asyncInstance) .map(Reactor.Action.searchResultPrefetchItems) From a51fda6904e8e8ee8a722fe20d45339a48c50baf Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 10 May 2025 18:36:15 +0900 Subject: [PATCH 349/393] =?UTF-8?q?refactor/#131:=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EB=8B=A4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ => Cell}/SearchResultEmptyTitleCollectionViewCell.swift | 0 .../Source/View/{ => Header}/SearchResultHeaderView.swift | 0 .../Source/View/{ => Header}/TagCollectionHeaderView.swift | 0 .../Source/View/{ => Layout}/PopupSearchLayoutFactory.swift | 0 .../SearchFeature/Source/View/{ => Main}/PopupSearchView.swift | 0 .../Source/View/{ => Main}/PopupSearchViewController.swift | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Cell}/SearchResultEmptyTitleCollectionViewCell.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Header}/SearchResultHeaderView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Header}/TagCollectionHeaderView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Layout}/PopupSearchLayoutFactory.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Main}/PopupSearchView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{ => Main}/PopupSearchViewController.swift (100%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultEmptyTitleCollectionViewCell.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultEmptyTitleCollectionViewCell.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultEmptyTitleCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/SearchResultHeaderView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/SearchResultHeaderView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/SearchResultHeaderView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/TagCollectionHeaderView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/TagCollectionHeaderView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/TagCollectionHeaderView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchLayoutFactory.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/PopupSearchViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift From 3521057e7147629dd349683ec8003b87bb0b0c39 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 10 May 2025 21:49:14 +0900 Subject: [PATCH 350/393] =?UTF-8?q?refactor/#131:=20=EA=B0=80=EB=8F=85?= =?UTF-8?q?=EC=84=B1=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 6 ++ .../Source/View/Main/PopupSearchView.swift | 91 +++++++++++++------ 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 589fef98..339781c4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 052413092DCF7DA100C42E2D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; }; + 0524130A2DCF7DA100C42E2D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 054A96202DCE38B500C0DD58 /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; 054A96212DCE38B500C0DD58 /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 054A96262DCE38E900C0DD58 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 054A96252DCE38E900C0DD58 /* Tabman */; }; @@ -92,6 +94,7 @@ dstSubfolderSpec = 10; files = ( 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */, + 0524130A2DCF7DA100C42E2D /* DesignSystem.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -120,6 +123,7 @@ /* Begin PBXFileReference section */ 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeature.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 0516362F2DC457DE00A6C0D1 /* SearchFeatureDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SearchFeatureDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 052413082DCF7DA100C42E2D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SearchFeatureInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C232DCDD4CF0093825D /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C2A2DCDD6380093825D /* Infrastructure.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Infrastructure.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -213,6 +217,7 @@ buildActionMask = 2147483647; files = ( 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */, + 052413092DCF7DA100C42E2D /* DesignSystem.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -243,6 +248,7 @@ 0516364A2DC45E6300A6C0D1 /* Frameworks */ = { isa = PBXGroup; children = ( + 052413082DCF7DA100C42E2D /* DesignSystem.framework */, 05734C522DCDF8B80093825D /* Presentation.framework */, 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */, 05734C432DCDF7240093825D /* PresentationInterface.framework */, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift index 206855e6..7bf35993 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift @@ -4,35 +4,17 @@ import DesignSystem import SnapKit import Then -import RxSwift import RxCocoa +import RxSwift import RxRelay final class PopupSearchView: UIView { - /// View를 구성하는 section을 정의 - enum Section: CaseIterable, Hashable { - case recentSearch - case category - case searchResult - } - - /// Section에 들어갈 Item을 정의한 변수 - enum SectionItem: Hashable { - case recentSearchItem(TagModel) - case categoryItem(TagModel) - case searchResultItem(SearchResultModel) - case searchResultEmptyTitle(String) - } - - /// Section의 헤더를 구분하기 위한 변수 - enum SectionHeaderKind: String { - case recentSearch = "recentSearchElementKind" - case category = "categoryElementKind" - case searchResult = "searchResultElementKind" - } // MARK: - Properties + private var dataSource: UICollectionViewDiffableDataSource? private let layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() + private var searchResultHeaderInput: SearchResultHeaderModel? + let recentSearchTagRemoveButtonTapped = PublishRelay() let recentSearchTagRemoveAllButtonTapped = PublishRelay() let categoryTagRemoveButtonTapped = PublishRelay() @@ -88,9 +70,6 @@ final class PopupSearchView: UIView { $0.contentInsetAdjustmentBehavior = .never } - private var dataSource: UICollectionViewDiffableDataSource? - private var searchResultHeaderInput: SearchResultHeaderModel? - // MARK: - init init() { super.init(frame: .zero) @@ -140,16 +119,26 @@ private extension PopupSearchView { // MARK: - DataSource extension PopupSearchView { private func configurationDataSourceItem() { - self.dataSource = UICollectionViewDiffableDataSource( + self.dataSource = UICollectionViewDiffableDataSource< + PopupSearchView.Section, + PopupSearchView.SectionItem + >( collectionView: collectionView ) { (collectionView, indexPath, item) -> UICollectionViewCell? in + switch item { case .recentSearchItem(let item): let cell = collectionView.dequeueReusableCell( withReuseIdentifier: TagCollectionViewCell.identifiers, for: indexPath ) as! TagCollectionViewCell - cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) + + cell.configureCell( + title: item.title, + id: item.id, + isSelected: item.isSelected, + isCancelable: item.isCancelable + ) cell.cancelButton.rx.tap .compactMap { cell.titleLabel.text } @@ -163,7 +152,13 @@ extension PopupSearchView { withReuseIdentifier: TagCollectionViewCell.identifiers, for: indexPath ) as! TagCollectionViewCell - cell.configureCell(title: item.title, id: item.id, isSelected: item.isSelected, isCancelable: item.isCancelable) + + cell.configureCell( + title: item.title, + id: item.id, + isSelected: item.isSelected, + isCancelable: item.isCancelable + ) cell.cancelButton.rx.tap .compactMap { item.id } @@ -177,7 +172,20 @@ extension PopupSearchView { withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, for: indexPath ) as! PPPopupGridCollectionViewCell - cell.configureCell(imagePath: item.imagePath, id: item.id, category: item.category, title: item.title, address: item.address, startDate: item.startDate, endDate: item.endDate, isBookmark: item.isBookmark, isLogin: item.isLogin, isPopular: item.isPopular, row: item.row) + + cell.configureCell( + imagePath: item.imagePath, + id: item.id, + category: item.category, + title: item.title, + address: item.address, + startDate: item.startDate, + endDate: item.endDate, + isBookmark: item.isBookmark, + isLogin: item.isLogin, + isPopular: item.isPopular, + row: item.row + ) cell.bookmarkButton.rx.tap .map { indexPath } @@ -286,3 +294,28 @@ extension PopupSearchView { return dataSource?.snapshot().sectionIdentifiers ?? [] } } + +// MARK: - Section information +extension PopupSearchView { + /// View를 구성하는 section을 정의 + enum Section: CaseIterable, Hashable { + case recentSearch + case category + case searchResult + } + + /// Section에 들어갈 Item을 정의한 변수 + enum SectionItem: Hashable { + case recentSearchItem(TagModel) + case categoryItem(TagModel) + case searchResultItem(SearchResultModel) + case searchResultEmptyTitle(String) + } + + /// Section의 헤더를 구분하기 위한 변수 + enum SectionHeaderKind: String { + case recentSearch = "recentSearchElementKind" + case category = "categoryElementKind" + case searchResult = "searchResultElementKind" + } +} From 360a8586d7af278f674649772347bd4b417aba27 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 10 May 2025 22:24:51 +0900 Subject: [PATCH 351/393] =?UTF-8?q?fix/#131:=20=EB=AA=A8=EB=8B=AC=EC=9D=B4?= =?UTF-8?q?=20=EB=9C=B0=20=EB=95=8C=20=EB=92=B7=20=ED=99=94=EB=A9=B4?= =?UTF-8?q?=EC=9D=B4=20=EC=83=88=EB=A1=9C=EA=B3=A0=EC=B9=A8=20=EB=90=98?= =?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CategorySelectViewController.swift | 1 + .../FilterSelectViewController.swift | 1 + .../Source/View/Main/PopupSearchViewController.swift | 12 +++++++++--- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 67d73d5b..c3dceed5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -85,6 +85,7 @@ extension CategorySelectViewController { .disposed(by: disposeBag) reactor.pulse(\.$categoryChanged) + .skip(1) .subscribe { _ in Category.valueChanged.onNext(()) } .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index a0369ade..1894a04c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -82,6 +82,7 @@ extension FilterSelectViewController { .disposed(by: disposeBag) reactor.pulse(\.$filterChanged) + .skip(1) .subscribe { _ in Filter.valueChanged.onNext(()) } .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift index 5eec2223..d9fc1439 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift @@ -182,9 +182,15 @@ extension PopupSearchViewController { .withUnretained(self) .subscribe { (owner, state) in owner.mainView.updateSnapshot( - recentSearchItems: state.recentSearchItems.map(PopupSearchView.SectionItem.recentSearchItem), - categoryItems: state.categoryItems.map(PopupSearchView.SectionItem.categoryItem), - searchResultItems: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), + recentSearchItems: state.recentSearchItems.map( + PopupSearchView.SectionItem.recentSearchItem + ), + categoryItems: state.categoryItems.map( + PopupSearchView.SectionItem.categoryItem + ), + searchResultItems: state.searchResultItems.map( + PopupSearchView.SectionItem.searchResultItem + ), headerInput: state.searchResultHeader, searchResultEmpty: state.searchResultEmptyTitle ) From 9aca3d2f45f5c0b8eaec1b1c2e359912651d1f04 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 10 May 2025 22:24:51 +0900 Subject: [PATCH 352/393] =?UTF-8?q?fix/#131:=20Section=EB=A7=88=EB=8B=A4?= =?UTF-8?q?=20=EB=8B=A4=EB=A5=B4=EA=B2=8C=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=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 --- .../Model/SearchResultHeaderModel.swift | 16 ++- .../Source/Reactor/PopupSearchReactor.swift | 11 ++- .../CategorySelectViewController.swift | 1 + ...earchResultHeaderCollectionViewCell.swift} | 12 +-- .../FilterSelectViewController.swift | 2 +- .../Layout/PopupSearchLayoutFactory.swift | 55 ++++++----- .../Source/View/Main/PopupSearchView.swift | 98 ++++++++++--------- .../View/Main/PopupSearchViewController.swift | 45 +++++++-- 8 files changed, 143 insertions(+), 97 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/{Header/SearchResultHeaderView.swift => Cell/SearchResultHeaderCollectionViewCell.swift} (91%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift index 313a5ed3..290e6d6b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift @@ -1,7 +1,15 @@ import Foundation -public struct SearchResultHeaderModel { - let title: String? - let count: Int? - let filterText: String? +public struct SearchResultHeaderModel: Hashable { + public static func == (lhs: Self, rhs: Self) -> Bool { + return (lhs.title == rhs.title) && (lhs.filterText == rhs.filterText) + } + + public init(title: String? = nil, count: Int? = 0, filterText: String?) { + self.filterText = filterText + } + + let title: String? = nil + let count: Int? = 0 + var filterText: String? = Filter.shared.title } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 471537bb..9443260a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -4,8 +4,8 @@ import DomainInterface import Infrastructure import ReactorKit -import RxSwift import RxCocoa +import RxSwift public final class PopupSearchReactor: Reactor { @@ -67,14 +67,14 @@ public final class PopupSearchReactor: Reactor { var recentSearchItems: [TagModel] = [] var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] - var searchResultHeader: SearchResultHeaderModel? = nil + var searchResultHeader: SearchResultHeaderModel = SearchResultHeaderModel(filterText: Filter.shared.title) var searchResultEmptyTitle: String? @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? @Pulse var clearButtonIsHidden: Bool? @Pulse var endEditing: Void? - @Pulse var updateDataSource: Void? + @Pulse var updateSearchResult: Void? @Pulse var dismiss: Void? fileprivate var isSearching: Bool = false @@ -115,8 +115,8 @@ public final class PopupSearchReactor: Reactor { return Observable.concat([ .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchResultEmptyTitle), @@ -342,7 +342,7 @@ public final class PopupSearchReactor: Reactor { newState.searchResultItems[indexPath.item].isBookmark.toggle() case .updateDataSource: - newState.updateDataSource = () + newState.updateSearchResult = () case .present(let target): newState.present = target @@ -428,6 +428,7 @@ private extension PopupSearchReactor { keyword afterTitle: String? = nil, count: Int64, filter filterTitle: String? = Filter.shared.title) -> SearchResultHeaderModel { + print("DEBUG: count is \(count)") return SearchResultHeaderModel( title: afterTitle, count: Int(count), diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift index 67d73d5b..c3dceed5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift @@ -85,6 +85,7 @@ extension CategorySelectViewController { .disposed(by: disposeBag) reactor.pulse(\.$categoryChanged) + .skip(1) .subscribe { _ in Category.valueChanged.onNext(()) } .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/SearchResultHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultHeaderCollectionViewCell.swift similarity index 91% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/SearchResultHeaderView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultHeaderCollectionViewCell.swift index 392d4c56..8ee7bf51 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/SearchResultHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultHeaderCollectionViewCell.swift @@ -6,11 +6,7 @@ import SnapKit import RxSwift import Then -public final class SearchResultHeaderView: UICollectionReusableView { - - enum Identifier: String { - case searchResult = "PopupGridCollectionHeaderView.searchResult" - } +public final class SearchResultHeaderCollectionViewCell: UICollectionViewCell { // MARK: - Properties @@ -53,7 +49,7 @@ public final class SearchResultHeaderView: UICollectionReusableView { } // MARK: - SetUp -private extension SearchResultHeaderView { +private extension SearchResultHeaderCollectionViewCell { func addViews() { [afterSearchTitleLabel, cellCountLabel, filterStatusButton].forEach { addSubview($0) @@ -98,8 +94,8 @@ private extension SearchResultHeaderView { } } -extension SearchResultHeaderView { - func configureHeader(title: String?, count: Int?, filterText: String?) { +extension SearchResultHeaderCollectionViewCell { + func configureCell(title: String?, count: Int?, filterText: String?) { if let afterSearchTitle = title, let count = count { filterStatusButton.isHidden = true diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift index a0369ade..92a141dc 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift @@ -75,13 +75,13 @@ extension FilterSelectViewController { .subscribe { (owner, state) in owner.mainView.saveButton.isEnabled = state.saveButtonIsEnable } .disposed(by: disposeBag) - reactor.pulse(\.$dismiss) .withUnretained(self) .subscribe { (owner, _) in owner.dismissModal() } .disposed(by: disposeBag) reactor.pulse(\.$filterChanged) + .skip(1) .subscribe { _ in Filter.valueChanged.onNext(()) } .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift index 23b08450..ad65921f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift @@ -21,16 +21,16 @@ final class PopupSearchLayoutFactory { case .category: return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + case .searchResultHeader: + return makeSearchResultHeaderSectionLayout() + case .searchResult: let sectionSnapshot = dataSource.snapshot(for: sectionType) let hasEmptyItem = sectionSnapshot.items.contains { item in if case .searchResultEmptyTitle = item { return true } return false } - return makeSearchResultSectionLayout( - PopupSearchView.SectionHeaderKind.searchResult.rawValue, - hasEmptyItem: hasEmptyItem - ) + return makeSearchResultSectionLayout(hasEmptyItem: hasEmptyItem) } }) } @@ -70,10 +70,32 @@ final class PopupSearchLayoutFactory { return section } - func makeSearchResultSectionLayout( - _ headerKind: String, - hasEmptyItem: Bool - ) -> NSCollectionLayoutSection { + func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(22) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .estimated(22) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + + return section + } + + func makeSearchResultSectionLayout(hasEmptyItem: Bool) -> NSCollectionLayoutSection { let itemWidth: NSCollectionLayoutDimension = hasEmptyItem ? .fractionalWidth(1.0) : .fractionalWidth(0.5) // Item @@ -99,8 +121,6 @@ final class PopupSearchLayoutFactory { section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) section.interGroupSpacing = 24 - section.boundarySupplementaryItems = [makePopupGridCollectionHeaderLayout(headerKind)] - return section } @@ -118,19 +138,4 @@ final class PopupSearchLayoutFactory { return header } - - func makePopupGridCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(22) - ) - let header = NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) - - return header - } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift index 7bf35993..c8cc8e3c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift @@ -13,7 +13,6 @@ final class PopupSearchView: UIView { // MARK: - Properties private var dataSource: UICollectionViewDiffableDataSource? private let layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() - private var searchResultHeaderInput: SearchResultHeaderModel? let recentSearchTagRemoveButtonTapped = PublishRelay() let recentSearchTagRemoveAllButtonTapped = PublishRelay() @@ -50,9 +49,8 @@ final class PopupSearchView: UIView { ) $0.register( - SearchResultHeaderView.self, - forSupplementaryViewOfKind: SectionHeaderKind.searchResult.rawValue, - withReuseIdentifier: SearchResultHeaderView.Identifier.searchResult.rawValue + SearchResultHeaderCollectionViewCell.self, + forCellWithReuseIdentifier: SearchResultHeaderCollectionViewCell.identifiers ) $0.register( @@ -167,6 +165,20 @@ extension PopupSearchView { return cell + case .searchResultHeaderItem(let item): + let cell = collectionView.dequeueReusableCell( + withReuseIdentifier: SearchResultHeaderCollectionViewCell.identifiers, + for: indexPath + ) as! SearchResultHeaderCollectionViewCell + + cell.configureCell(title: item.title, count: item.count, filterText: item.filterText) + + cell.filterStatusButton.rx.tap + .bind(to: self.filterStatusButtonTapped) + .disposed(by: cell.disposeBag) + + return cell + case .searchResultItem(let item): let cell = collectionView.dequeueReusableCell( withReuseIdentifier: PPPopupGridCollectionViewCell.identifiers, @@ -199,6 +211,7 @@ extension PopupSearchView { withReuseIdentifier: SearchResultEmptyTitleCollectionViewCell.identifiers, for: indexPath ) as! SearchResultEmptyTitleCollectionViewCell + cell.configureCell(title: title) return cell @@ -235,61 +248,53 @@ extension PopupSearchView { header.configureHeader(title: "팝업스토어 찾기") return header + } + } + } - case .searchResult: - guard let header = collectionView.dequeueReusableSupplementaryView( - ofKind: elementKind, - withReuseIdentifier: SearchResultHeaderView.Identifier.searchResult.rawValue, - for: indexPath - ) as? SearchResultHeaderView else { fatalError("\(#file), \(#function) Error") } - - if let input = self.searchResultHeaderInput { - header.configureHeader(title: input.title, count: input.count, filterText: input.filterText) - } else { - header.configureHeader(title: nil, count: nil, filterText: nil) - } - - header.filterStatusButton.rx.tap - .bind(to: self.filterStatusButtonTapped) - .disposed(by: header.disposeBag) - - return header + func updateSectionSnapshot(at section: Section, with items: [SectionItem]) { + if items.isEmpty { + guard var snapshot = dataSource?.snapshot() else { return } + snapshot.deleteSections([section]) + dataSource?.apply(snapshot) + return + } else { + if var snapshot = dataSource?.snapshot(for: section) { + snapshot.deleteAll() + snapshot.append(items) + dataSource?.apply(snapshot, to: section) + } else { + guard var snapshot = dataSource?.snapshot() else { return } + snapshot.appendSections([section]) + snapshot.appendItems(items, toSection: section) + dataSource?.apply(snapshot) } } } - func updateSnapshot( - recentSearchItems: [SectionItem], - categoryItems: [SectionItem], - searchResultItems: [SectionItem], - headerInput searchResultHeaderInput: SearchResultHeaderModel? = nil, - searchResultEmpty: String? = nil + func updateSearchResultSectionSnapshot( + with items: [SectionItem], + header: SectionItem, + empty: SectionItem? = nil ) { - var snapshot = NSDiffableDataSourceSnapshot() - - if !recentSearchItems.isEmpty { - snapshot.appendSections([PopupSearchView.Section.recentSearch]) - snapshot.appendItems(recentSearchItems, toSection: .recentSearch) - } + guard var snapshot = dataSource?.snapshot() else { return } - if !categoryItems.isEmpty { - snapshot.appendSections([PopupSearchView.Section.category]) - snapshot.appendItems(categoryItems, toSection: .category) - } + snapshot.deleteSections([.searchResultHeader, .searchResult]) - snapshot.appendSections([PopupSearchView.Section.searchResult]) - self.searchResultHeaderInput = searchResultHeaderInput + snapshot.appendSections( [.searchResultHeader, .searchResult]) + snapshot.appendItems([header], toSection: .searchResultHeader) - if let searchResultEmpty { - snapshot.appendItems([.searchResultEmptyTitle(searchResultEmpty)], toSection: .searchResult) + if let empty { + snapshot.appendItems([empty], toSection: .searchResult) } else { - snapshot.appendItems(searchResultItems, toSection: .searchResult) + snapshot.appendItems(items, toSection: .searchResult) } - snapshot.reloadSections([.searchResult]) - dataSource?.apply(snapshot, animatingDifferences: true) + dataSource?.apply(snapshot) } + + func getSectionsFromDataSource() -> [Section] { return dataSource?.snapshot().sectionIdentifiers ?? [] } @@ -301,6 +306,7 @@ extension PopupSearchView { enum Section: CaseIterable, Hashable { case recentSearch case category + case searchResultHeader case searchResult } @@ -308,6 +314,7 @@ extension PopupSearchView { enum SectionItem: Hashable { case recentSearchItem(TagModel) case categoryItem(TagModel) + case searchResultHeaderItem(SearchResultHeaderModel) case searchResultItem(SearchResultModel) case searchResultEmptyTitle(String) } @@ -316,6 +323,5 @@ extension PopupSearchView { enum SectionHeaderKind: String { case recentSearch = "recentSearchElementKind" case category = "categoryElementKind" - case searchResult = "searchResultElementKind" } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift index 5eec2223..4c2c012b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift @@ -95,6 +95,8 @@ extension PopupSearchViewController { return Reactor.Action.recentSearchTagButtonTapped(indexPath: indexPath) case .category: return Reactor.Action.categoryTagButtonTapped + + case .searchResultHeader: return nil case .searchResult: return Reactor.Action.searchResultItemTapped(indexPath: indexPath) } @@ -177,17 +179,44 @@ extension PopupSearchViewController { .subscribe { (owner, text) in owner.mainView.searchBar.searchBar.text = text } .disposed(by: disposeBag) - reactor.pulse(\.$updateDataSource) + reactor.state.distinctUntilChanged(\.recentSearchItems) + .compactMap { $0.recentSearchItems } + .withUnretained(self) + .subscribe { (owner, items) in + owner.mainView.updateSectionSnapshot( + at: .recentSearch, + with: items.map(PopupSearchView.SectionItem.recentSearchItem) + ) + } + .disposed(by: disposeBag) + + reactor.state.distinctUntilChanged(\.categoryItems) + .compactMap { $0.categoryItems } + .withUnretained(self) + .subscribe { (owner, items) in + owner.mainView.updateSectionSnapshot( + at: .category, + with: items.map(PopupSearchView.SectionItem.categoryItem) + ) + } + .disposed(by: disposeBag) + + reactor.pulse(\.$updateSearchResult) .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in - owner.mainView.updateSnapshot( - recentSearchItems: state.recentSearchItems.map(PopupSearchView.SectionItem.recentSearchItem), - categoryItems: state.categoryItems.map(PopupSearchView.SectionItem.categoryItem), - searchResultItems: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), - headerInput: state.searchResultHeader, - searchResultEmpty: state.searchResultEmptyTitle - ) + if let emptyTitle = state.searchResultEmptyTitle { + owner.mainView.updateSearchResultSectionSnapshot( + with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), + header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader), + empty: PopupSearchView.SectionItem.searchResultEmptyTitle(emptyTitle) + ) + } else { + owner.mainView.updateSearchResultSectionSnapshot( + with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), + header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader) + ) + } } .disposed(by: disposeBag) } From 69b2944b30a01de52215124c3f04d6caea035b48 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 11 May 2025 17:08:48 +0900 Subject: [PATCH 353/393] =?UTF-8?q?fix/#131:=20=EC=84=B8=EA=B0=9C=EC=9D=98?= =?UTF-8?q?=20=EC=84=B9=EC=85=98=EC=9C=BC=EB=A1=9C=20=EB=B6=84=EB=A6=AC?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EA=B0=81=EA=B0=81=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=86=8C=EC=8A=A4=20=EC=A0=81=EC=9A=A9=ED=95=98?= =?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 --- .../Model/SearchResultHeaderModel.swift | 10 +++--- .../Source/Reactor/PopupSearchReactor.swift | 31 +++++++++---------- .../View/Main/PopupSearchViewController.swift | 2 +- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift index 290e6d6b..1a68a0ba 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift @@ -1,15 +1,13 @@ import Foundation public struct SearchResultHeaderModel: Hashable { - public static func == (lhs: Self, rhs: Self) -> Bool { - return (lhs.title == rhs.title) && (lhs.filterText == rhs.filterText) - } - public init(title: String? = nil, count: Int? = 0, filterText: String?) { + self.title = title + self.count = count self.filterText = filterText } - let title: String? = nil - let count: Int? = 0 + var title: String? = nil + var count: Int? = 0 var filterText: String? = Filter.shared.title } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 9443260a..31781fd8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -50,7 +50,7 @@ public final class PopupSearchReactor: Reactor { case updateSearchingState(to: Bool) case updateSearchResultEmptyTitle case updateSearchResultBookmark(indexPath: IndexPath) - case updateDataSource + case updateSearchResultDataSource case present(target: PresentTarget) } @@ -74,7 +74,7 @@ public final class PopupSearchReactor: Reactor { @Pulse var present: PresentTarget? @Pulse var clearButtonIsHidden: Bool? @Pulse var endEditing: Void? - @Pulse var updateSearchResult: Void? + @Pulse var updateSearchResultDataSource: Void? @Pulse var dismiss: Void? fileprivate var isSearching: Bool = false @@ -120,7 +120,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchResultEmptyTitle), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -145,7 +145,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateSearchResultEmptyTitle), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -177,7 +177,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateSearchResultEmptyTitle), .just(.updateSearchBar(to: nil)), .just(.updateEditingState), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } } @@ -203,7 +203,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateSearchResultEmptyTitle), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -211,14 +211,14 @@ public final class PopupSearchReactor: Reactor { self.removeRecentSearchItem(text: text) return Observable.concat([ .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) case .recentSearchTagRemoveAllButtonTapped: self.removeAllRecentSearchItems() return .concat([ .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) case .categoryTagRemoveButtonTapped(let categoryID): @@ -233,7 +233,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchResultEmptyTitle), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -252,7 +252,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchResultEmptyTitle), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -271,7 +271,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchResultEmptyTitle), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } @@ -282,7 +282,7 @@ public final class PopupSearchReactor: Reactor { return fetchSearchResultBookmark(at: indexPath) .andThen(.concat([ .just(.updateSearchResultBookmark(indexPath: indexPath)), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ])) case .searchResultPrefetchItems(let indexPathList): @@ -293,7 +293,7 @@ public final class PopupSearchReactor: Reactor { return .concat([ .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), - .just(.updateDataSource) + .just(.updateSearchResultDataSource) ]) } } @@ -341,8 +341,8 @@ public final class PopupSearchReactor: Reactor { case .updateSearchResultBookmark(let indexPath): newState.searchResultItems[indexPath.item].isBookmark.toggle() - case .updateDataSource: - newState.updateSearchResult = () + case .updateSearchResultDataSource: + newState.updateSearchResultDataSource = () case .present(let target): newState.present = target @@ -428,7 +428,6 @@ private extension PopupSearchReactor { keyword afterTitle: String? = nil, count: Int64, filter filterTitle: String? = Filter.shared.title) -> SearchResultHeaderModel { - print("DEBUG: count is \(count)") return SearchResultHeaderModel( title: afterTitle, count: Int(count), diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift index 4c2c012b..84037798 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift @@ -201,7 +201,7 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) - reactor.pulse(\.$updateSearchResult) + reactor.pulse(\.$updateSearchResultDataSource) .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in From 12f2e667b1cb73fd1a9a9f6e1f52f9f495d340da Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 11 May 2025 17:15:03 +0900 Subject: [PATCH 354/393] =?UTF-8?q?fix/#131:=20=EB=B6=81=EB=A7=88=ED=81=AC?= =?UTF-8?q?=EC=8B=9C=20=EC=85=80=20=EA=B9=9C=EB=B9=A1=EC=9E=84=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/Source/View/Main/PopupSearchView.swift | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift index c8cc8e3c..9bba4123 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift @@ -256,7 +256,7 @@ extension PopupSearchView { if items.isEmpty { guard var snapshot = dataSource?.snapshot() else { return } snapshot.deleteSections([section]) - dataSource?.apply(snapshot) + dataSource?.apply(snapshot, animatingDifferences: false) return } else { if var snapshot = dataSource?.snapshot(for: section) { @@ -290,11 +290,9 @@ extension PopupSearchView { snapshot.appendItems(items, toSection: .searchResult) } - dataSource?.apply(snapshot) + dataSource?.apply(snapshot, animatingDifferences: false) } - - func getSectionsFromDataSource() -> [Section] { return dataSource?.snapshot().sectionIdentifiers ?? [] } From 0eec33b14390cfc49da7966cef6b7b7260e8598e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 11 May 2025 17:25:05 +0900 Subject: [PATCH 355/393] =?UTF-8?q?feat/#131:=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=96=B4=20=EC=B5=9C=EB=8C=80=2010=EA=B0=9C=EA=B9=8C=EC=A7=80?= =?UTF-8?q?=EB=A7=8C=20=EB=B3=B4=EC=9D=B4=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 --- .../SearchFeature/Source/Reactor/PopupSearchReactor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift index 31781fd8..bce81eb5 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift @@ -397,7 +397,7 @@ private extension PopupSearchReactor { func makeRecentSearchItems() -> [TagModel] { let searchKeywords = userDefaultService.fetchArray(keyType: .searchKeyword) ?? [] - return searchKeywords.map { TagModel(title: $0) } + return searchKeywords.prefix(10).map { TagModel(title: $0) } } func makeCategoryItems() -> [TagModel] { From 43e5f2b58273e322aac1fe6ca131a6c5f88321a8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 11 May 2025 18:08:55 +0900 Subject: [PATCH 356/393] =?UTF-8?q?refactor/#131:=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EB=B0=8F=20=EB=A6=AC=EB=84=A4=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ectionViewCell.swift => PPTagCollectionViewCell.swift} | 8 +++----- .../Factory}/CategorySelectorFactoryImpl.swift | 0 .../Reactor/CategorySelectReactor.swift | 0 .../View}/CategorySelectView.swift | 0 .../View}/CategorySelectViewController.swift | 0 .../SearchFeature/{Source => Common}/Model/Category.swift | 0 .../SearchFeature/{Source => Common}/Model/Filter.swift | 0 .../Model/SearchResultHeaderModel.swift | 0 .../{Source => Common}/Model/SearchResultModel.swift | 0 .../SearchFeature/{Source => Common}/Model/TagModel.swift | 0 .../View/Component}/TagCollectionHeaderView.swift | 0 .../Factory}/FilterSelectorFactoryImpl.swift | 0 .../Reactor/FilterSelectReactor.swift | 0 .../View}/FilterSelectView.swift | 0 .../View}/FilterSelectViewController.swift | 0 .../Factory}/PopupSearchFactoryImpl.swift | 0 .../Factory}/PopupSearchLayoutFactory.swift | 0 .../Reactor/PopupSearchReactor.swift | 0 .../Cell/SearchResultEmptyTitleCollectionViewCell.swift | 0 .../Cell/SearchResultHeaderCollectionViewCell.swift | 0 .../View/Main => PopupSearch/View}/PopupSearchView.swift | 0 .../View}/PopupSearchViewController.swift | 0 22 files changed, 3 insertions(+), 5 deletions(-) rename Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/{TagCollectionViewCell.swift => PPTagCollectionViewCell.swift} (95%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/FactoryImpl => CatagorySelect/Factory}/CategorySelectorFactoryImpl.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => CatagorySelect}/Reactor/CategorySelectReactor.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/CategorySelectModal => CatagorySelect/View}/CategorySelectView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/CategorySelectModal => CatagorySelect/View}/CategorySelectViewController.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => Common}/Model/Category.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => Common}/Model/Filter.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => Common}/Model/SearchResultHeaderModel.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => Common}/Model/SearchResultModel.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => Common}/Model/TagModel.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/Header => Common/View/Component}/TagCollectionHeaderView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/FactoryImpl => FilterSelector/Factory}/FilterSelectorFactoryImpl.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => FilterSelector}/Reactor/FilterSelectReactor.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/FilterSelectModal => FilterSelector/View}/FilterSelectView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/FilterSelectModal => FilterSelector/View}/FilterSelectViewController.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/FactoryImpl => PopupSearch/Factory}/PopupSearchFactoryImpl.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/Layout => PopupSearch/Factory}/PopupSearchLayoutFactory.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source => PopupSearch}/Reactor/PopupSearchReactor.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View => PopupSearch/View/Component}/Cell/SearchResultEmptyTitleCollectionViewCell.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View => PopupSearch/View/Component}/Cell/SearchResultHeaderCollectionViewCell.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/Main => PopupSearch/View}/PopupSearchView.swift (100%) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/{Source/View/Main => PopupSearch/View}/PopupSearchViewController.swift (100%) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/PPTagCollectionViewCell.swift similarity index 95% rename from Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift rename to Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/PPTagCollectionViewCell.swift index 4a626ea7..304d3456 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/TagCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPTagCollectionViewCell/PPTagCollectionViewCell.swift @@ -1,11 +1,9 @@ import UIKit -import DesignSystem - import RxSwift import SnapKit -public final class TagCollectionViewCell: UICollectionViewCell { +public final class PPTagCollectionViewCell: UICollectionViewCell { // MARK: - Components @@ -51,7 +49,7 @@ public final class TagCollectionViewCell: UICollectionViewCell { } // MARK: - SetUp -private extension TagCollectionViewCell { +private extension PPTagCollectionViewCell { func addViews() { [contentStackView].forEach { self.contentView.addSubview($0) @@ -85,7 +83,7 @@ private extension TagCollectionViewCell { } } -extension TagCollectionViewCell { +extension PPTagCollectionViewCell { public func configureCell(title: String? = nil, id: Int?, isSelected: Bool = false, isCancelable: Bool = true, fontSize: CGFloat = 11, cornerRadius: CGFloat = 15.5) { let xmarkImage = isSelected ? UIImage(named: "icon_xmark_white") : UIImage(named: "icon_xmark_gray") cancelButton.setImage(xmarkImage, for: .normal) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/CategorySelectorFactoryImpl.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/CategorySelectReactor.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/CategorySelectModal/CategorySelectViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Category.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Filter.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/Filter.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Filter.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/SearchResultHeaderModel.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultHeaderModel.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/SearchResultHeaderModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/SearchResultModel.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/SearchResultModel.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/SearchResultModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/TagModel.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Model/TagModel.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/TagModel.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Header/TagCollectionHeaderView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/FilterSelectorFactoryImpl.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Reactor/FilterSelectReactor.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/FilterSelectReactor.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Reactor/FilterSelectReactor.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/FilterSelectModal/FilterSelectViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchFactoryImpl.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/FactoryImpl/PopupSearchFactoryImpl.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchFactoryImpl.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Layout/PopupSearchLayoutFactory.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/Reactor/PopupSearchReactor.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultEmptyTitleCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultEmptyTitleCollectionViewCell.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultHeaderCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Cell/SearchResultHeaderCollectionViewCell.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchView.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift similarity index 100% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/Source/View/Main/PopupSearchViewController.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift From a6070aedbbe985132b83164f1330dd6b2d64b206 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 11 May 2025 10:47:47 +0000 Subject: [PATCH 357/393] style/#131: Apply SwiftLint autocorrect --- .../UseCase/Search/FetchKeywordBasePopupStoreList.swift | 1 - .../PPPopupGridCollectionViewCell.swift | 1 - .../DesignSystem/DesignSystem/Extension/UIFont+.swift | 6 ++---- .../Admin/AdminBottomSheet/AdminBottomSheetView.swift | 2 +- .../AdminBottomSheet/AdminBottomSheetViewController.swift | 2 +- .../AdminRegister/PopUpStoreRegisterViewController.swift | 2 +- .../Presentation/Scene/Admin/AdminViewController.swift | 2 +- .../Comment/CommentDetail/CommentDetailReactor.swift | 2 +- .../Scene/Comment/CommentList/CommentListReactor.swift | 2 +- .../NormalCommentAdd/NormalCommentAddReactor.swift | 2 +- .../OtherUserComment/OtherUserCommentReactor.swift | 2 +- .../Presentation/Scene/Detail/DetailReactor.swift | 2 +- .../Scene/Detail/FactoryImpl/DetailFactoryImpl.swift | 3 +-- .../Presentation/Scene/Home/List/HomeListReactor.swift | 2 +- .../Presentation/Scene/Login/Main/LoginController.swift | 2 +- .../Presentation/Scene/Login/Main/LoginReactor.swift | 2 +- .../Presentation/Scene/Login/Sub/SubLoginReactor.swift | 2 +- .../MapGuideView/FullScreenMapViewController.swift | 2 +- .../Scene/Map/MapView/MapViewController.swift | 2 +- .../Scene/MyPage/Block/BlockUserManageReactor.swift | 2 +- .../MyPage/Bookmark/Main/MyPageBookmarkReactor.swift | 2 +- .../Presentation/Scene/MyPage/Main/MyPageReactor.swift | 2 +- .../Scene/MyPage/MyComment/Main/MyCommentReactor.swift | 2 +- .../MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift | 2 +- .../Scene/MyPage/Notice/List/MyPageNoticeReactor.swift | 2 +- .../CategoryEditModal/CategoryEditModalReactor.swift | 2 +- .../ProfileEdit/InfoEditModal/InfoEditModalReactor.swift | 2 +- .../MyPage/ProfileEdit/Main/ProfileEditController.swift | 2 +- .../MyPage/ProfileEdit/Main/ProfileEditReactor.swift | 2 +- .../Scene/MyPage/Recent/MyPageRecentReactor.swift | 2 +- .../Withdrawl/SelectedReason/WithdrawlReasonReactor.swift | 2 +- .../Scene/SignUp/Main/SignUpMainController.swift | 2 +- .../Scene/SignUp/Main/SignUpMainReactor.swift | 2 +- .../Scene/SignUp/Step3/SignUpStep3Reactor.swift | 2 +- .../Factory/CategorySelectorFactoryImpl.swift | 2 +- .../CatagorySelect/Reactor/CategorySelectReactor.swift | 8 +++----- .../CatagorySelect/View/CategorySelectView.swift | 2 +- .../View/CategorySelectViewController.swift | 4 ---- .../SearchFeature/Common/Model/Category.swift | 3 +-- .../Factory/FilterSelectorFactoryImpl.swift | 2 +- .../FilterSelector/View/FilterSelectViewController.swift | 6 +----- .../PopupSearch/Factory/PopupSearchLayoutFactory.swift | 6 ++---- .../PopupSearch/Reactor/PopupSearchReactor.swift | 7 ++----- .../Cell/SearchResultEmptyTitleCollectionViewCell.swift | 2 -- .../Cell/SearchResultHeaderCollectionViewCell.swift | 5 ++--- .../SearchFeature/PopupSearch/View/PopupSearchView.swift | 6 +++--- .../PopupSearch/View/PopupSearchViewController.swift | 4 ++-- .../SearchFeature/SearchFeatureDemo/App/AppDelegate.swift | 1 - .../SearchFeatureDemo/App/SceneDelegate.swift | 1 - 49 files changed, 52 insertions(+), 78 deletions(-) diff --git a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift index 754a07a6..0ecfceab 100644 --- a/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift +++ b/Poppool/DomainLayer/Domain/DomainInterface/UseCase/Search/FetchKeywordBasePopupStoreList.swift @@ -5,4 +5,3 @@ import RxSwift public protocol FetchKeywordBasePopupListUseCase { func execute(keyword: String) -> Observable } - diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift index 57497186..0e8d1944 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PopupGridCollectionViewCell/PPPopupGridCollectionViewCell.swift @@ -158,4 +158,3 @@ extension PPPopupGridCollectionViewCell { } } } - diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift index ff86a031..9723e545 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UIFont+.swift @@ -12,15 +12,13 @@ public extension UIFont { static func korFont(style: FontStyle, size: CGFloat) -> UIFont { let fontName = "GothicA1-\(style.rawValue)" - if let font = UIFont(name: fontName, size: size) { return font } - else { return registerAndGetFont(name: fontName, size: size) } + if let font = UIFont(name: fontName, size: size) { return font } else { return registerAndGetFont(name: fontName, size: size) } } static func engFont(style: FontStyle, size: CGFloat) -> UIFont { let fontName = "Poppins-\(style.rawValue)" - if let font = UIFont(name: fontName, size: size) { return font } - else { return registerAndGetFont(name: fontName, size: size) } + if let font = UIFont(name: fontName, size: size) { return font } else { return registerAndGetFont(name: fontName, size: size) } } private static func registerAndGetFont(name: String, size: CGFloat) -> UIFont { diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift index 3ac9a994..09248345 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetView.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DesignSystem +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift index 6ff008da..a02c00e7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminBottomSheet/AdminBottomSheetViewController.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DesignSystem +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift index 49287424..da9ccb95 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpStoreRegisterViewController.swift @@ -2,9 +2,9 @@ import CoreLocation import PhotosUI import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index d168a9a3..fc104dd6 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift index 6a1a6f39..c5a24de3 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentDetail/CommentDetailReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift index d2333562..d673ae97 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/CommentList/CommentListReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift index 0cf26bbd..a3c2b594 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/NormalCommentAdd/NormalCommentAddReactor.swift @@ -1,9 +1,9 @@ import PhotosUI import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift index b101ead1..69d98d5b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Comment/OtherUserComment/OtherUserCommentReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift index 67f68c44..e6626483 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/DetailReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import LinkPresentation import ReactorKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift index 50139c53..d4339120 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Detail/FactoryImpl/DetailFactoryImpl.swift @@ -3,10 +3,9 @@ import DomainInterface import Infrastructure import PresentationInterface - public final class DetailFactoryImpl: DetailFactory { public init() { } - + public func make(popupID: Int) -> BaseViewController { let viewController = DetailController() let reactor = DetailReactor( diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift index 8a6d666f..9dc1332a 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/List/HomeListReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift index 79797d54..b24a556f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginController.swift @@ -1,7 +1,7 @@ import UIKit -import Infrastructure import DesignSystem +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift index f83e8db7..56fcff23 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Main/LoginReactor.swift @@ -1,6 +1,6 @@ +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift index 3dee4356..ce0c960b 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Login/Sub/SubLoginReactor.swift @@ -1,6 +1,6 @@ +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index f6abc5e5..a719f426 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -1,9 +1,9 @@ import CoreLocation import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import NMapsMap import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift index b42c1b1d..f68f5ab9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapViewController.swift @@ -1,9 +1,9 @@ import CoreLocation import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import SearchFeatureInterface import FloatingPanel diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift index 797d71e4..b750673d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Block/BlockUserManageReactor.swift @@ -1,7 +1,7 @@ import UIKit -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift index 5a284efc..6a89a5ee 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Bookmark/Main/MyPageBookmarkReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift index ee231366..70fa8dbb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Main/MyPageReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift index efd34009..b036fcdf 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/MyComment/Main/MyCommentReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift index f2aa7f83..aa430f77 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/Detail/MyPageNoticeDetailReactor.swift @@ -1,5 +1,5 @@ -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift index f9458b92..7a920ceb 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Notice/List/MyPageNoticeReactor.swift @@ -1,7 +1,7 @@ import UIKit -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift index 8aab9568..e2bb0ff5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/CategoryEditModal/CategoryEditModalReactor.swift @@ -1,7 +1,7 @@ import UIKit -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift index 7114c25d..fc3b890d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/InfoEditModal/InfoEditModalReactor.swift @@ -1,7 +1,7 @@ import UIKit -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift index c11c06cc..4fddc408 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditController.swift @@ -1,8 +1,8 @@ import PhotosUI import UIKit -import Infrastructure import DesignSystem +import Infrastructure import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift index dfc95e4e..5dbfcba9 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/ProfileEdit/Main/ProfileEditReactor.swift @@ -1,9 +1,9 @@ import PhotosUI import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift index e31b64f3..021747b7 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Recent/MyPageRecentReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa 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 64ee7025..a27dc32c 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/MyPage/Withdrawl/SelectedReason/WithdrawlReasonReactor.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift index 101a9a0c..3086975f 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainController.swift @@ -1,8 +1,8 @@ import UIKit +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import Pageboy import ReactorKit diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift index d81a5b60..3c5bafe2 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Main/SignUpMainReactor.swift @@ -1,6 +1,6 @@ +import DesignSystem import DomainInterface import Infrastructure -import DesignSystem import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift index 906c8ee7..43ddd997 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/SignUp/Step3/SignUpStep3Reactor.swift @@ -1,7 +1,7 @@ import UIKit -import DomainInterface import DesignSystem +import DomainInterface import ReactorKit import RxCocoa diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift index 960f86bd..a1645730 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Factory/CategorySelectorFactoryImpl.swift @@ -5,7 +5,7 @@ import SearchFeatureInterface public final class CategorySelectorFactoryImpl: CategorySelectorFactory { public init() { } - + public func make() -> BaseViewController & PPModalPresentable { let reactor = CategorySelectReactor( fetchCategoryListUseCase: DIContainer.resolve(FetchCategoryListUseCase.self) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift index 802f05de..1682dbdf 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/Reactor/CategorySelectReactor.swift @@ -57,7 +57,7 @@ final class CategorySelectReactor: Reactor { case .viewWillAppear: return fetchCategoryListUseCase.execute() .withUnretained(self) - .map { (owner, response) in + .map { (_, response) in let items = response.map { return TagModel(title: $0.category, id: $0.categoryId, isCancelable: false) } @@ -96,8 +96,7 @@ final class CategorySelectReactor: Reactor { switch mutation { case .setupCategotyTag(let items): let fetchedItems = items.map { - if let id = $0.id, Category.shared.contains(id: id) { return $0.selectionToggledItem() } - else { return $0 } + if let id = $0.id, Category.shared.contains(id: id) { return $0.selectionToggledItem() } else { return $0 } } originCategoryItems = fetchedItems newState.categoryItems = fetchedItems @@ -115,8 +114,7 @@ final class CategorySelectReactor: Reactor { case .updateCategoryTagSelection(let categoryID): newState.categoryItems = state.categoryItems.map { - if $0.id == categoryID { return $0.selectionToggledItem() } - else { return $0 } + if $0.id == categoryID { return $0.selectionToggledItem() } else { return $0 } } case .updateSaveButtonEnable: diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift index 9adaa16c..0dcdd526 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectView.swift @@ -90,7 +90,7 @@ private extension CategorySelectView { private extension CategorySelectView { func makeLayout() -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout { sectionIndex, environment in + return UICollectionViewCompositionalLayout { _, _ in let itemSize = NSCollectionLayoutSize( widthDimension: .estimated(26), heightDimension: .absolute(36) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift index 468960b1..4d6a233a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/CatagorySelect/View/CategorySelectViewController.swift @@ -23,10 +23,6 @@ extension CategorySelectViewController { override func loadView() { self.view = mainView } - - override func viewDidLoad() { - super.viewDidLoad() - } } // MARK: - Methods diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift index 84ea400b..ff2fe9c4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/Model/Category.swift @@ -51,8 +51,7 @@ extension Category { } func getCancelableCategoryItems() -> [TagModel] { - if items == [Category.defaultItem] { return items } - else { return items.filter { $0.isSelected == true }.map { $0.cancelableItem() } } + if items == [Category.defaultItem] { return items } else { return items.filter { $0.isSelected == true }.map { $0.cancelableItem() } } } func removeItem(by categoryID: Int) { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift index a526e33f..95e8d2f2 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/Factory/FilterSelectorFactoryImpl.swift @@ -4,7 +4,7 @@ import SearchFeatureInterface public final class FilterSelectorFactoryImpl: FilterSelectorFactory { public init() { } - + public func make() -> BaseViewController & PPModalPresentable { let reactor = FilterSelectReactor() let viewController = FilterSelectViewController() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift index 92a141dc..5a50254b 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/FilterSelector/View/FilterSelectViewController.swift @@ -23,10 +23,6 @@ extension FilterSelectViewController { override func loadView() { self.view = mainView } - - override func viewDidLoad() { - super.viewDidLoad() - } } // MARK: - Methods @@ -56,7 +52,7 @@ extension FilterSelectViewController { mainView.saveButton.rx.tap .withUnretained(self) - .map { (owner, _) in Reactor.Action.saveButtonTapped } + .map { (_, _) in Reactor.Action.saveButtonTapped } .bind(to: reactor.action) .disposed(by: disposeBag) } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index ad65921f..77e6d8e7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -6,7 +6,7 @@ final class PopupSearchLayoutFactory { func makeCollectionViewLayout( dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? ) -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, environment -> NSCollectionLayoutSection? in + return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ -> NSCollectionLayoutSection? in guard let self = self, let dataSource = dataSourceProvider() else { return nil } @@ -130,12 +130,10 @@ final class PopupSearchLayoutFactory { widthDimension: .fractionalWidth(1.0), heightDimension: .absolute(24) ) - let header = NSCollectionLayoutBoundarySupplementaryItem( + return NSCollectionLayoutBoundarySupplementaryItem( layoutSize: headerSize, elementKind: elementKind, alignment: .top ) - - return header } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index bce81eb5..6d81b5e1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -180,8 +180,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateSearchResultDataSource) ]) } - } - else { return .just(.present(target: .before)) } + } else { return .just(.present(target: .before)) } case .recentSearchTagButtonTapped(let indexPath): let keyword = self.findRecentSearchKeyword(at: indexPath) @@ -436,9 +435,7 @@ private extension PopupSearchReactor { } func makeSearchResultEmptyTitle(state: State) -> String? { - if !currentState.searchResultItems.isEmpty { return nil } - else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } - else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } + if !currentState.searchResultItems.isEmpty { return nil } else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } } /// 받침에 따라 이/가 를 판단해서 붙여준다. diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift index 2750af3b..7d3786fa 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift @@ -19,7 +19,6 @@ final class SearchResultEmptyTitleCollectionViewCell: UICollectionViewCell { $0.textColor = .g400 } - // MARK: - init override init(frame: CGRect) { super.init(frame: frame) @@ -58,4 +57,3 @@ extension SearchResultEmptyTitleCollectionViewCell { self.emptyLabel.text = title } } - diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift index 8ee7bf51..518bac1a 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultHeaderCollectionViewCell.swift @@ -2,8 +2,8 @@ import UIKit import DesignSystem -import SnapKit import RxSwift +import SnapKit import Then public final class SearchResultHeaderCollectionViewCell: UICollectionViewCell { @@ -103,8 +103,7 @@ extension SearchResultHeaderCollectionViewCell { afterSearchTitleLabel.text = afterSearchTitle + " 포함된 팝업" cellCountLabel.text = "총 \(count)개를 찾았어요." - if count == 0 { self.isHidden = true } - else { + if count == 0 { self.isHidden = true } else { self.isHidden = false afterSearchTitleLabel.snp.updateConstraints { make in make.height.equalTo(24) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 8368f0b8..029c440f 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -2,11 +2,11 @@ import UIKit import DesignSystem -import SnapKit -import Then import RxCocoa -import RxSwift import RxRelay +import RxSwift +import SnapKit +import Then final class PopupSearchView: UIView { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift index 84037798..45262a60 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift @@ -7,8 +7,8 @@ import PresentationInterface import SearchFeatureInterface import ReactorKit -import RxSwift import RxCocoa +import RxSwift import Then public final class PopupSearchViewController: BaseViewController, View { @@ -27,7 +27,7 @@ extension PopupSearchViewController { public override func loadView() { self.view = mainView } - + public override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift index 85babba4..d26b08f7 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/AppDelegate.swift @@ -72,4 +72,3 @@ extension AppDelegate { DIContainer.register(DetailFactory.self) { return DetailFactoryImpl() } } } - diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift index 1d9f2290..ded75ea4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeatureDemo/App/SceneDelegate.swift @@ -26,4 +26,3 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { window?.makeKeyAndVisible() } } - From 900146713264564b192e6e95421100e5976daf0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 12 May 2025 19:13:13 +0900 Subject: [PATCH 358/393] =?UTF-8?q?fix/#139:=20=EC=96=B4=EB=93=9C=EB=AF=BC?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=88=98=EC=A0=95=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=9D=B4=EC=8A=88=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Admin/AdminViewController.swift | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index 6e31f7e6..8c049ba1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -178,24 +178,15 @@ final class AdminViewController: BaseViewController, View { .subscribe( onNext: { [weak self] storeDetail in guard let self = self else { return } - let updateParams = UpdateStoreParams( - id: storeDetail.id, - name: storeDetail.name, - categoryId: storeDetail.categoryId, - desc: storeDetail.description, - address: storeDetail.address, - startDate: storeDetail.startDate, - endDate: storeDetail.endDate, - mainImageUrl: storeDetail.mainImageUrl, - imageUrlList: storeDetail.images.map { $0.imageUrl }, - imagesToDelete: [], - latitude: storeDetail.latitude, - longitude: storeDetail.longitude, - markerTitle: storeDetail.markerTitle, - markerSnippet: storeDetail.markerSnippet, - startDateBeforeEndDate: true + let registerVC = PopUpStoreRegisterViewController( + nickname: self.nickname, + editingStore: store ) - let registerVC = PopUpStoreRegisterViewController(nickname: self.nickname) + + registerVC.completionHandler = { [weak self] in + self?.reactor?.action.onNext(.reloadData) + } + self.navigationController?.pushViewController(registerVC, animated: true) }, onError: { [weak self] error in @@ -206,7 +197,6 @@ final class AdminViewController: BaseViewController, View { } private func deleteStore(_ store: AdminStore) { - // 먼저 스토어 상세 정보를 가져와 모든 이미지 URL을 확인 adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) .subscribe( From 227389d2674b86dd6cfaeb51801cb6f6f24bf7a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=80=E1=85=B5=E1=86=B7=E1=84=80=E1=85=B5=E1=84=92?= =?UTF-8?q?=E1=85=A7=E1=86=AB?= Date: Mon, 12 May 2025 19:13:46 +0900 Subject: [PATCH 359/393] =?UTF-8?q?fix/#139:=20=ED=8A=B9=EC=A0=95=EC=9C=84?= =?UTF-8?q?=EC=B9=98=EC=97=90=EC=84=9C=20=ED=99=94=EB=A9=B4=EC=9D=B4?= =?UTF-8?q?=EB=8F=99=EB=90=98=EC=A7=80=EC=95=8A=EA=B3=A0=20=EB=B0=94?= =?UTF-8?q?=EB=A1=9C=20=ED=95=B4=EB=8B=B9=EC=9C=84=EC=B9=98=EC=97=90=20?= =?UTF-8?q?=ED=8F=AC=EC=BB=A4=EC=8A=A4=EB=90=98=EB=8F=84=EB=A1=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 --- .../MapGuideView/FullScreenMapViewController.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift index 66be0ba2..a6cdd98d 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FindMap/MapGuideView/FullScreenMapViewController.swift @@ -106,8 +106,8 @@ class FullScreenMapViewController: MapViewController { let position = NMGLatLng(lat: store.latitude, lng: store.longitude) let cameraUpdate = NMFCameraUpdate(scrollTo: position, zoomTo: 15.0) - cameraUpdate.animation = .easeIn - cameraUpdate.animationDuration = 0.3 + mainView.mapView.cancelTransitions() + cameraUpdate.animation = .none mainView.mapView.moveCamera(cameraUpdate) if let existingMarker = initialMarker { @@ -210,8 +210,8 @@ class FullScreenMapViewController: MapViewController { mainView.setStoreCardHidden(false, animated: true) let cameraUpdate = NMFCameraUpdate(scrollTo: marker.position, zoomTo: 15.0) - cameraUpdate.animation = .easeIn - cameraUpdate.animationDuration = 0.3 + mainView.mapView.cancelTransitions() + cameraUpdate.animation = .none mainView.mapView.moveCamera(cameraUpdate) DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in From cc4e5020e770cbd280cdf1a20185ce74d1fdcdcc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 13 May 2025 06:16:57 +0000 Subject: [PATCH 360/393] style/#139: Apply SwiftLint autocorrect --- .../Presentation/Scene/Admin/AdminViewController.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift index 8c049ba1..4a2ce2e1 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminViewController.swift @@ -176,7 +176,7 @@ final class AdminViewController: BaseViewController, View { adminUseCase.fetchStoreDetail(id: store.id) .observe(on: MainScheduler.instance) .subscribe( - onNext: { [weak self] storeDetail in + onNext: { [weak self] _ in guard let self = self else { return } let registerVC = PopUpStoreRegisterViewController( nickname: self.nickname, From bb997f6919163853106e623a0599c0d3e1f6e95d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 13 May 2025 22:19:52 +0900 Subject: [PATCH 361/393] =?UTF-8?q?fix/#131:=20=EA=B2=B0=EA=B3=BC=20?= =?UTF-8?q?=EC=97=86=EC=9D=8C=20=EB=9D=BC=EB=B2=A8=EC=9D=84=20=EB=88=8C?= =?UTF-8?q?=EB=A0=80=EC=9D=84=20=EB=95=8C=20=EC=B6=A9=EB=8F=8C=EC=9D=B4=20?= =?UTF-8?q?=EB=82=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PopupSearch/Reactor/PopupSearchReactor.swift | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index 6d81b5e1..646443d1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -275,7 +275,8 @@ public final class PopupSearchReactor: Reactor { } case .searchResultItemTapped(let indexPath): - return .just(.present(target: .popupDetail(popupID: self.findPopupStoreID(at: indexPath)))) + guard let popupID = self.findPopupStoreID(at: indexPath) else { return .empty() } + return .just(.present(target: .popupDetail(popupID: popupID))) case .searchResultBookmarkButtonTapped(let indexPath): return fetchSearchResultBookmark(at: indexPath) @@ -419,7 +420,9 @@ private extension PopupSearchReactor { } } - func findPopupStoreID(at indexPath: IndexPath) -> Int { + // 빈 화면에서 탭할때 문제 + func findPopupStoreID(at indexPath: IndexPath) -> Int? { + guard currentState.searchResultItems.indices.contains(indexPath.item) else { return nil } return Int(currentState.searchResultItems[indexPath.item].id) } From 4382cf1d1432e25a1f509b82fb72bbe27c1f4117 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 13:23:57 +0900 Subject: [PATCH 362/393] =?UTF-8?q?chore/#131:=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=EC=A3=BC=EC=84=9D=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 --- .../Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift index cc6c9825..1e190673 100644 --- a/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift +++ b/Poppool/DataLayer/Data/Data/RepositoryImpl/Search/SearchAPIRepositoryImpl.swift @@ -23,7 +23,7 @@ public final class SearchAPIRepositoryImpl: SearchAPIRepository { let request = GetSearchPopupStoreRequestDTO(query: query) let endPoint = SearchAPIEndPoint.getSearchPopUpList(request: request) - return provider.requestData( // 실패했을때는 키워드 저장이 안되도록 수정 + return provider.requestData( with: endPoint, interceptor: tokenInterceptor ) From f6ecadb1018d48c02cbdf361d9f3c06ecdbe3db8 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 13:27:48 +0900 Subject: [PATCH 363/393] =?UTF-8?q?fix/#131:=20static=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=84=A0=EC=96=B8=EB=90=9C=20cell=20identifier=EB=A5=BC=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 - extension에서 제공되는 기본 identifiers 사용하도록 변경 --- .../Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift | 4 ++-- .../Presentation/Presentation/Scene/Admin/ImageCell.swift | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift index d74d2b44..a54c62b5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/AdminRegister/PopUpImagesCollectionView.swift @@ -45,7 +45,7 @@ private extension PopUpImagesCollectionView { func configureUI() { self.backgroundColor = .clear - self.register(ImageCell.self, forCellWithReuseIdentifier: ImageCell.identifier) + self.register(ImageCell.self, forCellWithReuseIdentifier: ImageCell.identifiers) self.dataSource = self self.delegate = self self.showsHorizontalScrollIndicator = false @@ -68,7 +68,7 @@ extension PopUpImagesCollectionView: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { guard let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: ImageCell.identifier, + withReuseIdentifier: ImageCell.identifiers, for: indexPath ) as? ImageCell else { return UICollectionViewCell() diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift index e7062d80..dbf1da59 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Admin/ImageCell.swift @@ -5,8 +5,6 @@ import DesignSystem import SnapKit final class ImageCell: UICollectionViewCell { - static let identifier = "ImageCell" - // UI private let thumbnailImageView = UIImageView().then { $0.contentMode = .scaleAspectFill From edaa734d9075cb9cb5f6c1347a195ee3f99c5536 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 13:57:42 +0900 Subject: [PATCH 364/393] =?UTF-8?q?refactor/#131:=20ReusableView=EC=9D=98?= =?UTF-8?q?=20identifier=EB=A5=BC=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8F=84?= =?UTF-8?q?=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 - UICollectionViewCell은 UICollectionReusableView를 상속하는 객체 - UICollectionReusableView에 이미 identifier가 있으므로 UICollectionViewCell에서는 extension으로 별도로 identifier를 만들지 않아도 사용 가능함 - 추가로 Sectionable에서 사용하는 SupplementaryItem의 indentifier를 static 인스턴스를 가져다 쓰도록 수정 - 핵심이었는 TagCollectionHeaderView의 Identifier는 제거 - 사용하던 곳에서는 extension의 identifiers를 사용하도록 변경 --- .../Extension/UICollectionReusableView+.swift | 11 ++--------- .../Extension/UICollectionViewCell+.swift | 7 ------- .../Sectionable/SectionSupplementaryItem.swift | 4 ++-- .../View/Component/TagCollectionHeaderView.swift | 5 ----- .../PopupSearch/View/PopupSearchView.swift | 8 ++++---- 5 files changed, 8 insertions(+), 27 deletions(-) delete mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift index 384b685b..1edd36fe 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionReusableView+.swift @@ -1,14 +1,7 @@ -// -// UICollectionReusableView+.swift -// MomsVillage -// -// Created by SeoJunYoung on 8/29/24. -// - import UIKit -extension UICollectionReusableView { - var identifiers: String { +public extension UICollectionReusableView { + static var identifiers: String { return String(describing: self) } } diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift deleted file mode 100644 index 3200f30e..00000000 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Extension/UICollectionViewCell+.swift +++ /dev/null @@ -1,7 +0,0 @@ -import UIKit - -public extension UICollectionViewCell { - public static var identifiers: String { - return String(describing: self) - } -} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift index 0eaf2169..00b9c7fe 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Utills/Interfaces/Sectionable/SectionSupplementaryItem.swift @@ -58,12 +58,12 @@ extension SectionSupplementaryItemable { collectionView.register( ReusableView.self, forSupplementaryViewOfKind: kind, - withReuseIdentifier: reusableView.identifiers + withReuseIdentifier: ReusableView.identifiers ) guard let view = collectionView.dequeueReusableSupplementaryView( ofKind: kind, - withReuseIdentifier: reusableView.identifiers, + withReuseIdentifier: ReusableView.identifiers, for: indexPath ) as? ReusableView else { Logger.log( diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift index 1fdad74a..99425b24 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/Common/View/Component/TagCollectionHeaderView.swift @@ -7,11 +7,6 @@ import SnapKit final class TagCollectionHeaderView: UICollectionReusableView { - enum Identifier: String { - case recentSearch = "TagCollectionHeaderView.recentSearch" - case category = "TagCollectionHeaderView.category" - } - // MARK: - Components var disposeBag = DisposeBag() diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 029c440f..2c1421be 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -34,13 +34,13 @@ final class PopupSearchView: UIView { $0.register( TagCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.recentSearch.rawValue, - withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.rawValue + withReuseIdentifier: TagCollectionHeaderView.identifiers ) $0.register( TagCollectionHeaderView.self, forSupplementaryViewOfKind: SectionHeaderKind.category.rawValue, - withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue + withReuseIdentifier: TagCollectionHeaderView.identifiers ) $0.register( @@ -228,7 +228,7 @@ extension PopupSearchView { case .recentSearch: guard let header = collectionView.dequeueReusableSupplementaryView( ofKind: elementKind, - withReuseIdentifier: TagCollectionHeaderView.Identifier.recentSearch.rawValue, + withReuseIdentifier: TagCollectionHeaderView.identifiers, for: indexPath ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } header.configureHeader(title: "최근 검색어", buttonTitle: "모두삭제") @@ -242,7 +242,7 @@ extension PopupSearchView { case .category: guard let header = collectionView.dequeueReusableSupplementaryView( ofKind: elementKind, - withReuseIdentifier: TagCollectionHeaderView.Identifier.category.rawValue, + withReuseIdentifier: TagCollectionHeaderView.identifiers, for: indexPath ) as? TagCollectionHeaderView else { fatalError("\(#file), \(#function) Error") } header.configureHeader(title: "팝업스토어 찾기") From 429c3a1a0832b9d07de7fa7ce518a00f7a5fa309 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 14:08:23 +0900 Subject: [PATCH 365/393] =?UTF-8?q?fix/#131:=20develop=20merge=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B8=ED=95=9C=20=EC=BD=94=EB=93=9C=20=EC=98=A4=EB=A5=98=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 --- .../Scene/Map/FillterSheetView/BalloonChipCell.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift index 0c30e1d4..3612b976 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/FillterSheetView/BalloonChipCell.swift @@ -75,7 +75,7 @@ final class BalloonChipCell: UICollectionViewCell { attributedTitle.addAttribute( .font, - value: UIFont.korFont(style: .bold, size: Constant.fontSize)!, + value: UIFont.korFont(style: .bold, size: Constant.fontSize), range: NSRange(location: .zero, length: attributedTitle.length) ) } else { @@ -97,7 +97,7 @@ final class BalloonChipCell: UICollectionViewCell { attributedTitle.addAttribute( .font, - value: UIFont.korFont(style: .medium, size: Constant.fontSize)!, + value: UIFont.korFont(style: .medium, size: Constant.fontSize), range: NSRange(location: .zero, length: attributedTitle.length) ) } From d8f42562021fe7ed0a778f0ccc984e358153bb7d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 19:01:45 +0900 Subject: [PATCH 366/393] =?UTF-8?q?chore/#150:=20git=20ignore=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 --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3dd82707..f586473e 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,6 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output + +# Cursor +Poppool/buildServer.json From 7cd9a174256af300f15539d116f0bcd5ec74e3c2 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Thu, 15 May 2025 19:49:49 +0900 Subject: [PATCH 367/393] =?UTF-8?q?chore/#150:=20.gitignore=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 --- .gitignore | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitignore b/.gitignore index 3dd82707..4449f4b9 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,8 @@ fastlane/report.xml fastlane/Preview.html fastlane/screenshots/**/*.png fastlane/test_output + +# Cursor +Poppool/buildServer.json +.vscode/* + From c13e8d4917100d951da10c8d012984e6aa8a6bc6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 00:59:16 +0900 Subject: [PATCH 368/393] =?UTF-8?q?feat/#150:=20safe=20index=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=9C=20Collection=20extention=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 --- .../Infrastructure/Extension/Collection+.swift | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift new file mode 100644 index 00000000..a4b22fc3 --- /dev/null +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift @@ -0,0 +1,7 @@ +import Foundation + +public extension Collection { + subscript(safe index: Index) -> Element? { + return indices.contains(index) ? self[index] : nil + } +} From b6a1c04cbe2c1af7f84c589823eea5b965961f8a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 01:43:18 +0900 Subject: [PATCH 369/393] =?UTF-8?q?refactor/#150:=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EC=97=86=EC=9D=8C=20=EB=84=A4=EC=9D=B4?= =?UTF-8?q?=EB=B0=8D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Factory/PopupSearchLayoutFactory.swift | 2 +- .../Reactor/PopupSearchReactor.swift | 24 +++++++++---------- ...SearchResultEmptyCollectionViewCell.swift} | 6 ++--- .../PopupSearch/View/PopupSearchView.swift | 12 +++++----- .../View/PopupSearchViewController.swift | 4 ++-- 5 files changed, 24 insertions(+), 24 deletions(-) rename Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/{SearchResultEmptyTitleCollectionViewCell.swift => SearchResultEmptyCollectionViewCell.swift} (84%) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index 77e6d8e7..d9048589 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -27,7 +27,7 @@ final class PopupSearchLayoutFactory { case .searchResult: let sectionSnapshot = dataSource.snapshot(for: sectionType) let hasEmptyItem = sectionSnapshot.items.contains { item in - if case .searchResultEmptyTitle = item { return true } + if case .searchResultEmptyItem = item { return true } return false } return makeSearchResultSectionLayout(hasEmptyItem: hasEmptyItem) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index 646443d1..aa24bd77 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -48,7 +48,7 @@ public final class PopupSearchReactor: Reactor { case updateClearButtonIsHidden(to: Bool) case updateCurrentPage(to: Int32) case updateSearchingState(to: Bool) - case updateSearchResultEmptyTitle + case updateSearchResultEmpty case updateSearchResultBookmark(indexPath: IndexPath) case updateSearchResultDataSource @@ -68,7 +68,7 @@ public final class PopupSearchReactor: Reactor { var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] var searchResultHeader: SearchResultHeaderModel = SearchResultHeaderModel(filterText: Filter.shared.title) - var searchResultEmptyTitle: String? + var searchResultEmpty: String? @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? @@ -119,7 +119,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateSearchResultDataSource) ]) } @@ -142,7 +142,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), .just(.updateSearchResultDataSource) @@ -174,7 +174,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: false)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateSearchBar(to: nil)), .just(.updateEditingState), .just(.updateSearchResultDataSource) @@ -199,7 +199,7 @@ public final class PopupSearchReactor: Reactor { .just(.updateCurrentPage(to: 0)), .just(.updateSearchBar(to: keyword)), .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), .just(.updateSearchResultDataSource) @@ -231,7 +231,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateSearchResultDataSource) ]) } @@ -250,7 +250,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateSearchResultDataSource) ]) } @@ -269,7 +269,7 @@ public final class PopupSearchReactor: Reactor { .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmptyTitle), + .just(.updateSearchResultEmpty), .just(.updateSearchResultDataSource) ]) } @@ -335,8 +335,8 @@ public final class PopupSearchReactor: Reactor { case .updateSearchingState(let isSearching): newState.isSearching = isSearching - case .updateSearchResultEmptyTitle: - newState.searchResultEmptyTitle = makeSearchResultEmptyTitle(state: newState) + case .updateSearchResultEmpty: + newState.searchResultEmpty = makeSearchResultEmpty(state: newState) case .updateSearchResultBookmark(let indexPath): newState.searchResultItems[indexPath.item].isBookmark.toggle() @@ -437,7 +437,7 @@ private extension PopupSearchReactor { ) } - func makeSearchResultEmptyTitle(state: State) -> String? { + func makeSearchResultEmpty(state: State) -> String? { if !currentState.searchResultItems.isEmpty { return nil } else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyCollectionViewCell.swift similarity index 84% rename from Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift rename to Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyCollectionViewCell.swift index 7d3786fa..9a82752c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyTitleCollectionViewCell.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/Component/Cell/SearchResultEmptyCollectionViewCell.swift @@ -6,7 +6,7 @@ import Infrastructure import SnapKit import Then -final class SearchResultEmptyTitleCollectionViewCell: UICollectionViewCell { +final class SearchResultEmptyCollectionViewCell: UICollectionViewCell { // MARK: - Properties private let emptyLabel = PPLabel( @@ -34,7 +34,7 @@ final class SearchResultEmptyTitleCollectionViewCell: UICollectionViewCell { } // MARK: - SetUp -private extension SearchResultEmptyTitleCollectionViewCell { +private extension SearchResultEmptyCollectionViewCell { func addViews() { [emptyLabel].forEach { self.addSubview($0) @@ -52,7 +52,7 @@ private extension SearchResultEmptyTitleCollectionViewCell { func configureUI() { } } -extension SearchResultEmptyTitleCollectionViewCell { +extension SearchResultEmptyCollectionViewCell { func configureCell(title: String) { self.emptyLabel.text = title } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 2c1421be..5aba2eab 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -59,8 +59,8 @@ final class PopupSearchView: UIView { ) $0.register( - SearchResultEmptyTitleCollectionViewCell.self, - forCellWithReuseIdentifier: SearchResultEmptyTitleCollectionViewCell.identifiers + SearchResultEmptyCollectionViewCell.self, + forCellWithReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers ) // UICollectionView 최 상/하단 빈 영역 @@ -206,11 +206,11 @@ extension PopupSearchView { return cell - case .searchResultEmptyTitle(let title): + case .searchResultEmptyItem(let title): let cell = collectionView.dequeueReusableCell( - withReuseIdentifier: SearchResultEmptyTitleCollectionViewCell.identifiers, + withReuseIdentifier: SearchResultEmptyCollectionViewCell.identifiers, for: indexPath - ) as! SearchResultEmptyTitleCollectionViewCell + ) as! SearchResultEmptyCollectionViewCell cell.configureCell(title: title) @@ -314,7 +314,7 @@ extension PopupSearchView { case categoryItem(TagModel) case searchResultHeaderItem(SearchResultHeaderModel) case searchResultItem(SearchResultModel) - case searchResultEmptyTitle(String) + case searchResultEmptyItem(String) } /// Section의 헤더를 구분하기 위한 변수 diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift index 45262a60..0433b134 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift @@ -205,11 +205,11 @@ extension PopupSearchViewController { .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in - if let emptyTitle = state.searchResultEmptyTitle { + if let searchResultEmpty = state.searchResultEmpty { owner.mainView.updateSearchResultSectionSnapshot( with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader), - empty: PopupSearchView.SectionItem.searchResultEmptyTitle(emptyTitle) + empty: PopupSearchView.SectionItem.searchResultEmptyItem(searchResultEmpty) ) } else { owner.mainView.updateSearchResultSectionSnapshot( From e18f5720d77c12499ecca39c609f719fdeb85151 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 01:46:01 +0900 Subject: [PATCH 370/393] =?UTF-8?q?refactor/#150:=20View=EC=99=80=20Reacto?= =?UTF-8?q?r=20=EA=B3=B5=ED=86=B5=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=A0=20Section=20type=20=EC=A0=95=EC=9D=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Factory/PopupSearchLayoutFactory.swift | 2 +- .../PopupSearch/SectionType/Section.swift | 8 ++++++++ .../PopupSearch/View/PopupSearchView.swift | 16 ++++------------ 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index d9048589..a8f9ff0c 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -4,7 +4,7 @@ import UIKit final class PopupSearchLayoutFactory { func makeCollectionViewLayout( - dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? + dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? ) -> UICollectionViewLayout { return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ -> NSCollectionLayoutSection? in guard let self = self, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift new file mode 100644 index 00000000..9ff211b5 --- /dev/null +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift @@ -0,0 +1,8 @@ +import Foundation + +enum PopupSearchSection: CaseIterable, Hashable { + case recentSearch + case category + case searchResultHeader + case searchResult +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 5aba2eab..5c3fde23 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -11,7 +11,7 @@ import Then final class PopupSearchView: UIView { // MARK: - Properties - private var dataSource: UICollectionViewDiffableDataSource? + private var dataSource: UICollectionViewDiffableDataSource? private let layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() let recentSearchTagRemoveButtonTapped = PublishRelay() @@ -118,7 +118,7 @@ private extension PopupSearchView { extension PopupSearchView { private func configurationDataSourceItem() { self.dataSource = UICollectionViewDiffableDataSource< - PopupSearchView.Section, + PopupSearchSection, PopupSearchView.SectionItem >( collectionView: collectionView @@ -252,7 +252,7 @@ extension PopupSearchView { } } - func updateSectionSnapshot(at section: Section, with items: [SectionItem]) { + func updateSectionSnapshot(at section: PopupSearchSection, with items: [SectionItem]) { if items.isEmpty { guard var snapshot = dataSource?.snapshot() else { return } snapshot.deleteSections([section]) @@ -293,21 +293,13 @@ extension PopupSearchView { dataSource?.apply(snapshot, animatingDifferences: false) } - func getSectionsFromDataSource() -> [Section] { + func getSectionsFromDataSource() -> [PopupSearchSection] { return dataSource?.snapshot().sectionIdentifiers ?? [] } } // MARK: - Section information extension PopupSearchView { - /// View를 구성하는 section을 정의 - enum Section: CaseIterable, Hashable { - case recentSearch - case category - case searchResultHeader - case searchResult - } - /// Section에 들어갈 Item을 정의한 변수 enum SectionItem: Hashable { case recentSearchItem(TagModel) From 123ccf772a673c13fd72bb631c66b800850a8b4d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 02:08:02 +0900 Subject: [PATCH 371/393] =?UTF-8?q?refactor/#150:=20=EB=B9=88=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=EC=9D=BC=EB=95=8C=EC=9D=98=20Section=EA=B3=BC=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SearchResult Section을 유동적으로 사용하던것을 분리함 - 이로 인해 dataSource의 의존성을 줄임 --- .../Factory/PopupSearchLayoutFactory.swift | 43 ++++++++++++++----- .../Reactor/PopupSearchReactor.swift | 4 +- .../PopupSearch/SectionType/Section.swift | 1 + .../PopupSearch/View/PopupSearchView.swift | 14 +++--- .../View/PopupSearchViewController.swift | 26 +++++------ 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index a8f9ff0c..fb1cc401 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -25,12 +25,10 @@ final class PopupSearchLayoutFactory { return makeSearchResultHeaderSectionLayout() case .searchResult: - let sectionSnapshot = dataSource.snapshot(for: sectionType) - let hasEmptyItem = sectionSnapshot.items.contains { item in - if case .searchResultEmptyItem = item { return true } - return false - } - return makeSearchResultSectionLayout(hasEmptyItem: hasEmptyItem) + return makeSearchResultSectionLayout() + + case .searchResultEmpty: + return makeSearchResultEmptySectionLayout() } }) } @@ -95,12 +93,10 @@ final class PopupSearchLayoutFactory { return section } - func makeSearchResultSectionLayout(hasEmptyItem: Bool) -> NSCollectionLayoutSection { - let itemWidth: NSCollectionLayoutDimension = hasEmptyItem ? .fractionalWidth(1.0) : .fractionalWidth(0.5) - + func makeSearchResultSectionLayout() -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( - widthDimension: itemWidth, + widthDimension: .fractionalWidth(0.5), heightDimension: .absolute(249) ) let item = NSCollectionLayoutItem(layoutSize: itemSize) @@ -124,6 +120,33 @@ final class PopupSearchLayoutFactory { return section } + func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { + + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .fractionalHeight(1.0) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .fractionalHeight(1.0) + ) + + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) + + return section + } + func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { // Header let headerSize = NSCollectionLayoutSize( diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index aa24bd77..70f9a939 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -438,7 +438,9 @@ private extension PopupSearchReactor { } func makeSearchResultEmpty(state: State) -> String? { - if !currentState.searchResultItems.isEmpty { return nil } else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } + if !currentState.searchResultItems.isEmpty { return nil } + else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } + else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } } /// 받침에 따라 이/가 를 판단해서 붙여준다. diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift index 9ff211b5..bd858660 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/SectionType/Section.swift @@ -5,4 +5,5 @@ enum PopupSearchSection: CaseIterable, Hashable { case category case searchResultHeader case searchResult + case searchResultEmpty } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 5c3fde23..e02d7f64 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -278,16 +278,18 @@ extension PopupSearchView { empty: SectionItem? = nil ) { guard var snapshot = dataSource?.snapshot() else { return } - - snapshot.deleteSections([.searchResultHeader, .searchResult]) - - snapshot.appendSections( [.searchResultHeader, .searchResult]) - snapshot.appendItems([header], toSection: .searchResultHeader) + snapshot.deleteSections([.searchResultHeader, .searchResult, .searchResultEmpty]) if let empty { - snapshot.appendItems([empty], toSection: .searchResult) + snapshot.appendSections([.searchResultHeader, .searchResultEmpty]) + snapshot.appendItems([header], toSection: .searchResultHeader) + snapshot.appendItems([empty], toSection: .searchResultEmpty) + collectionView.isScrollEnabled = false } else { + snapshot.appendSections([.searchResultHeader, .searchResult]) + snapshot.appendItems([header], toSection: .searchResultHeader) snapshot.appendItems(items, toSection: .searchResult) + collectionView.isScrollEnabled = true } dataSource?.apply(snapshot, animatingDifferences: false) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift index 0433b134..10ca1787 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift @@ -93,12 +93,18 @@ extension PopupSearchViewController { switch sections[indexPath.section] { case .recentSearch: return Reactor.Action.recentSearchTagButtonTapped(indexPath: indexPath) + case .category: return Reactor.Action.categoryTagButtonTapped - case .searchResultHeader: return nil + case .searchResultHeader: + return nil + case .searchResult: return Reactor.Action.searchResultItemTapped(indexPath: indexPath) + + case .searchResultEmpty: + return nil } } .bind(to: reactor.action) @@ -205,18 +211,12 @@ extension PopupSearchViewController { .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in - if let searchResultEmpty = state.searchResultEmpty { - owner.mainView.updateSearchResultSectionSnapshot( - with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), - header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader), - empty: PopupSearchView.SectionItem.searchResultEmptyItem(searchResultEmpty) - ) - } else { - owner.mainView.updateSearchResultSectionSnapshot( - with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), - header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader) - ) - } + owner.mainView.updateSearchResultSectionSnapshot( + with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), + header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader), + empty: state.searchResultEmpty == nil ? nil : + PopupSearchView.SectionItem.searchResultEmptyItem(state.searchResultEmpty!) + ) } .disposed(by: disposeBag) } From 7ad04b938da1c4e6e82845cf8bbff9ff627e69f3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 15:11:58 +0900 Subject: [PATCH 372/393] =?UTF-8?q?refactor/#150:=20=EB=A6=AC=EC=95=A1?= =?UTF-8?q?=ED=84=B0=20=EC=BD=94=EB=93=9C=20=EB=8B=A8=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reactor/PopupSearchReactor.swift | 313 +++++++++--------- 1 file changed, 160 insertions(+), 153 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index 70f9a939..40f81bae 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -109,193 +109,55 @@ public final class PopupSearchReactor: Reactor { public func mutate(action: Action) -> Observable { switch action { case .viewDidLoad: - return fetchSearchResult() - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return Observable.concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.setupSearchResultTotalPageCount(count: response.totalPages)), - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmpty), - .just(.updateSearchResultDataSource) - ]) - } + return handleViewDidLoad() case .searchBarEditing(let text): - return .just(.updateClearButtonIsHidden(to: text.isEmpty ? true : false)) + return handleSearchBarEditing(text) case .searchBarExitEditing(let text): - return fetchSearchResult(keyword: text) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return Observable.concat([ - .just(.setupRecentSearch(items: [])), - .just(.setupCategory(items: [])), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput( - keyword: owner.makePostPositionedText(text), - count: Int64(response.popupStoreList.count) - ))), // FIXME: API에 해당 결과값이 아직 없음 - .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmpty), - .just(.updateClearButtonIsHidden(to: true)), - .just(.updateEditingState), - .just(.updateSearchResultDataSource) - ]) - } + return handleSearchBarExitEditing(text) case .searchBarEndEditing: - return .concat([ - .just(.updateClearButtonIsHidden(to: true)), - .just(.updateEditingState) - ]) + return handleSearchBarEndEditing() case .searchBarClearButtonTapped: - return Observable.concat([ - .just(.updateClearButtonIsHidden(to: true)), - .just(.updateSearchBar(to: nil)) - ]) + return handleSearchBarClear() case .searchBarCancelButtonTapped: - if currentState.isSearching { - return fetchSearchResult() - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return Observable.concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), - .just(.setupSearchResultTotalPageCount(count: response.totalPages)), - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchingState(to: false)), - .just(.updateSearchResultEmpty), - .just(.updateSearchBar(to: nil)), - .just(.updateEditingState), - .just(.updateSearchResultDataSource) - ]) - } - } else { return .just(.present(target: .before)) } + return handleSearchBarCancel() case .recentSearchTagButtonTapped(let indexPath): - let keyword = self.findRecentSearchKeyword(at: indexPath) - return fetchSearchResult(keyword: keyword) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return Observable.concat([ - .just(.setupRecentSearch(items: [])), - .just(.setupCategory(items: [])), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput( - keyword: owner.makePostPositionedText(keyword), - count: Int64(response.popupStoreList.count) - ))), - .just(.setupSearchResultTotalPageCount(count: 0)), // FIXME: API에 해당 결과값이 아직 없음 - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchBar(to: keyword)), - .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmpty), - .just(.updateClearButtonIsHidden(to: true)), - .just(.updateEditingState), - .just(.updateSearchResultDataSource) - ]) - } + return handleRecentSearchTagTap(at: indexPath) case .recentSearchTagRemoveButtonTapped(let text): - self.removeRecentSearchItem(text: text) - return Observable.concat([ - .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateSearchResultDataSource) - ]) + return handleRecentSearchTagRemove(text) case .recentSearchTagRemoveAllButtonTapped: - self.removeAllRecentSearchItems() - return .concat([ - .just(.setupRecentSearch(items: self.makeRecentSearchItems())), - .just(.updateSearchResultDataSource) - ]) + return handleRecentSearchTagRemoveAll() case .categoryTagRemoveButtonTapped(let categoryID): - self.removeCategoryItem(by: categoryID) - return fetchSearchResult() - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return Observable.concat([ - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), - .just(.setupSearchResultTotalPageCount(count: response.totalPages)), - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmpty), - .just(.updateSearchResultDataSource) - ]) - } + return handleCategoryTagRemove(categoryID) case .categoryTagButtonTapped: return .just(.present(target: .categorySelector)) case .categoryChangedBySelector: - return fetchSearchResult() - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return .concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), - .just(.setupSearchResultTotalPageCount(count: response.totalPages)), - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmpty), - .just(.updateSearchResultDataSource) - ]) - } + return handleCategoryChanged() case .searchResultFilterButtonTapped: return .just(.present(target: .filterSelector)) case .searchResultFilterChangedBySelector: - return fetchSearchResult() - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return .concat([ - .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), - .just(.setupCategory(items: owner.makeCategoryItems())), - .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), - .just(.setupSearchResultTotalPageCount(count: response.totalPages)), - .just(.updateCurrentPage(to: 0)), - .just(.updateSearchResultEmpty), - .just(.updateSearchResultDataSource) - ]) - } + return handleFilterChanged() case .searchResultItemTapped(let indexPath): - guard let popupID = self.findPopupStoreID(at: indexPath) else { return .empty() } - return .just(.present(target: .popupDetail(popupID: popupID))) + return handleSearchResultItemTap(at: indexPath) case .searchResultBookmarkButtonTapped(let indexPath): - return fetchSearchResultBookmark(at: indexPath) - .andThen(.concat([ - .just(.updateSearchResultBookmark(indexPath: indexPath)), - .just(.updateSearchResultDataSource) - ])) + return handleSearchResultBookmark(at: indexPath) case .searchResultPrefetchItems(let indexPathList): - guard isPrefetchable(indexPathList: indexPathList) else { return .empty() } - return fetchSearchResult(page: currentState.currentPage + 1) - .withUnretained(self) - .flatMap { (owner, response) -> Observable in - return .concat([ - .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), - .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), - .just(.updateSearchResultDataSource) - ]) - } + return handleSearchResultPrefetch(at: indexPathList) } } @@ -488,3 +350,148 @@ private extension PopupSearchReactor { return isScrollToEnd && hasNextPage } } + + +// MARK: - Mutate Handlers +private extension PopupSearchReactor { + func handleViewDidLoad() -> Observable { + return loadDefaultSearchResults() + } + + func handleSearchBarEditing(_ text: String) -> Observable { + return .just(.updateClearButtonIsHidden(to: !text.isEmpty)) + } + + func handleSearchBarExitEditing(_ text: String) -> Observable { + return loadKeywordSearchResults(text) + } + + func handleSearchBarEndEditing() -> Observable { + return Observable.concat([ + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateEditingState) + ]) + } + + func handleSearchBarClear() -> Observable { + return Observable.concat([ + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateSearchBar(to: nil)) + ]) + } + + func handleSearchBarCancel() -> Observable { + if currentState.isSearching { + return loadDefaultSearchResults() + } else { + return .just(.present(target: .before)) + } + } + + func handleRecentSearchTagTap(at indexPath: IndexPath) -> Observable { + let keyword = findRecentSearchKeyword(at: indexPath) + return loadKeywordSearchResults(keyword) + } + + func handleRecentSearchTagRemove(_ text: String) -> Observable { + removeRecentSearchItem(text: text) + return Observable.concat([ + .just(.setupRecentSearch(items: makeRecentSearchItems())), + .just(.updateSearchResultDataSource) + ]) + } + + func handleRecentSearchTagRemoveAll() -> Observable { + removeAllRecentSearchItems() + return Observable.concat([ + .just(.setupRecentSearch(items: makeRecentSearchItems())), + .just(.updateSearchResultDataSource) + ]) + } + + func handleCategoryTagRemove(_ categoryID: Int) -> Observable { + removeCategoryItem(by: categoryID) + return loadDefaultSearchResults() + } + + func handleCategoryChanged() -> Observable { + return loadDefaultSearchResults() + } + + func handleFilterChanged() -> Observable { + return loadDefaultSearchResults() + } + + func handleSearchResultItemTap(at indexPath: IndexPath) -> Observable { + guard let popupID = findPopupStoreID(at: indexPath) else { return .empty() } + return .just(.present(target: .popupDetail(popupID: popupID))) + } + + func handleSearchResultBookmark(at indexPath: IndexPath) -> Observable { + return fetchSearchResultBookmark(at: indexPath) + .andThen(.concat([ + .just(.updateSearchResultBookmark(indexPath: indexPath)), + .just(.updateSearchResultDataSource) + ])) + } + + func handleSearchResultPrefetch(at indexPathList: [IndexPath]) -> Observable { + guard isPrefetchable(prefetchCount: 4, indexPathList: indexPathList) else { return .empty() } + return fetchSearchResult(page: currentState.currentPage + 1) + .withUnretained(self) + .flatMap { owner, response in + Observable.concat([ + .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), + .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), + .just(.updateSearchResultDataSource) + ]) + } + } +} + +// MARK: - Load Search Results +private extension PopupSearchReactor { + func loadDefaultSearchResults(page: Int32 = 0) -> Observable { + return fetchSearchResult(page: page) + .withUnretained(self) + .flatMap { owner, response in + Observable.concat([ + .just(.setupRecentSearch(items: owner.makeRecentSearchItems())), + .just(.setupCategory(items: owner.makeCategoryItems())), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput(count: response.totalElements))), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), + .just(.setupSearchResultTotalPageCount(count: response.totalPages)), + .just(.updateCurrentPage(to: 0)), + .just(.updateSearchingState(to: false)), + .just(.updateSearchResultEmpty), + .just(.updateSearchBar(to: nil)), + .just(.updateEditingState), + .just(.updateSearchResultDataSource) + ]) + } + } + + func loadKeywordSearchResults(_ keyword: String?) -> Observable { + guard let keyword = keyword else { return .empty() } + return fetchSearchResult(keyword: keyword) + .withUnretained(self) + .flatMap { owner, response in + Observable.concat([ + .just(.setupRecentSearch(items: [])), + .just(.setupCategory(items: [])), + .just(.setupSearchResult(items: owner.makeSearchResultItems(response.popupStoreList, response.loginYn))), + .just(.setupSearchResultHeader(item: owner.makeSearchResultHeaderInput( + keyword: owner.makePostPositionedText(keyword), + count: Int64(response.popupStoreList.count) + ))), + .just(.setupSearchResultTotalPageCount(count: 0)), + .just(.updateCurrentPage(to: 0)), + .just(.updateSearchingState(to: true)), + .just(.updateSearchResultEmpty), + .just(.updateClearButtonIsHidden(to: true)), + .just(.updateEditingState), + .just(.updateSearchResultDataSource) + ]) + } + } +} From 269afcedb3864561958074bcf4d0bf2685522abb Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 15:12:40 +0900 Subject: [PATCH 373/393] =?UTF-8?q?rafactor/#150:=20state=20=EB=8B=A8?= =?UTF-8?q?=EC=88=9C=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Reactor/PopupSearchReactor.swift | 27 +++++++------------ .../View/PopupSearchViewController.swift | 8 +++--- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index 40f81bae..82535e44 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -48,9 +48,8 @@ public final class PopupSearchReactor: Reactor { case updateClearButtonIsHidden(to: Bool) case updateCurrentPage(to: Int32) case updateSearchingState(to: Bool) - case updateSearchResultEmpty case updateSearchResultBookmark(indexPath: IndexPath) - case updateSearchResultDataSource + case updateSearchResultSection case present(target: PresentTarget) } @@ -68,13 +67,12 @@ public final class PopupSearchReactor: Reactor { var categoryItems: [TagModel] = [] var searchResultItems: [SearchResultModel] = [] var searchResultHeader: SearchResultHeaderModel = SearchResultHeaderModel(filterText: Filter.shared.title) - var searchResultEmpty: String? @Pulse var searchBarText: String? = nil @Pulse var present: PresentTarget? @Pulse var clearButtonIsHidden: Bool? @Pulse var endEditing: Void? - @Pulse var updateSearchResultDataSource: Void? + @Pulse var updateSearchResultSection: String? @Pulse var dismiss: Void? fileprivate var isSearching: Bool = false @@ -197,14 +195,11 @@ public final class PopupSearchReactor: Reactor { case .updateSearchingState(let isSearching): newState.isSearching = isSearching - case .updateSearchResultEmpty: - newState.searchResultEmpty = makeSearchResultEmpty(state: newState) - case .updateSearchResultBookmark(let indexPath): newState.searchResultItems[indexPath.item].isBookmark.toggle() - case .updateSearchResultDataSource: - newState.updateSearchResultDataSource = () + case .updateSearchResultSection: + newState.updateSearchResultSection = makeSearchResultEmpty(state: newState) case .present(let target): newState.present = target @@ -397,7 +392,7 @@ private extension PopupSearchReactor { removeRecentSearchItem(text: text) return Observable.concat([ .just(.setupRecentSearch(items: makeRecentSearchItems())), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ]) } @@ -405,7 +400,7 @@ private extension PopupSearchReactor { removeAllRecentSearchItems() return Observable.concat([ .just(.setupRecentSearch(items: makeRecentSearchItems())), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ]) } @@ -431,7 +426,7 @@ private extension PopupSearchReactor { return fetchSearchResultBookmark(at: indexPath) .andThen(.concat([ .just(.updateSearchResultBookmark(indexPath: indexPath)), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ])) } @@ -443,7 +438,7 @@ private extension PopupSearchReactor { Observable.concat([ .just(.appendSearchResult(items: owner.makeSearchResultItems(response.popUpStoreList, response.loginYn))), .just(.updateCurrentPage(to: owner.currentState.currentPage + 1)), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ]) } } @@ -463,10 +458,9 @@ private extension PopupSearchReactor { .just(.setupSearchResultTotalPageCount(count: response.totalPages)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: false)), - .just(.updateSearchResultEmpty), .just(.updateSearchBar(to: nil)), .just(.updateEditingState), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ]) } } @@ -487,10 +481,9 @@ private extension PopupSearchReactor { .just(.setupSearchResultTotalPageCount(count: 0)), .just(.updateCurrentPage(to: 0)), .just(.updateSearchingState(to: true)), - .just(.updateSearchResultEmpty), .just(.updateClearButtonIsHidden(to: true)), .just(.updateEditingState), - .just(.updateSearchResultDataSource) + .just(.updateSearchResultSection) ]) } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift index 10ca1787..f95cb7d0 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchViewController.swift @@ -207,15 +207,17 @@ extension PopupSearchViewController { } .disposed(by: disposeBag) - reactor.pulse(\.$updateSearchResultDataSource) + reactor.pulse(\.$updateSearchResultSection) .withLatestFrom(reactor.state) .withUnretained(self) .subscribe { (owner, state) in + let isEmpty = state.updateSearchResultSection == nil + let emptyCaseTitle = state.updateSearchResultSection + owner.mainView.updateSearchResultSectionSnapshot( with: state.searchResultItems.map(PopupSearchView.SectionItem.searchResultItem), header: PopupSearchView.SectionItem.searchResultHeaderItem(state.searchResultHeader), - empty: state.searchResultEmpty == nil ? nil : - PopupSearchView.SectionItem.searchResultEmptyItem(state.searchResultEmpty!) + empty: isEmpty ? nil : PopupSearchView.SectionItem.searchResultEmptyItem(emptyCaseTitle!) ) } .disposed(by: disposeBag) From 8bf7e35b4841ae7a29695409b85ddc4b7f0e4716 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Fri, 16 May 2025 15:19:58 +0900 Subject: [PATCH 374/393] =?UTF-8?q?fix/#150:=20clear=20=EB=B2=84=ED=8A=BC?= =?UTF-8?q?=EC=9D=B4=20=EB=B0=98=EB=8C=80=EB=A1=9C=20=EB=8F=99=EC=9E=91?= =?UTF-8?q?=ED=95=98=EB=8D=98=20=EB=B6=80=EB=B6=84=20=EC=9B=90=EC=83=81?= =?UTF-8?q?=EB=B3=B5=EA=B5=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index 82535e44..fde9cc33 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -354,7 +354,7 @@ private extension PopupSearchReactor { } func handleSearchBarEditing(_ text: String) -> Observable { - return .just(.updateClearButtonIsHidden(to: !text.isEmpty)) + return .just(.updateClearButtonIsHidden(to: text.isEmpty)) } func handleSearchBarExitEditing(_ text: String) -> Observable { From eada1e17b6aff85c05d4c20dce82808d3de81a40 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 01:10:22 +0900 Subject: [PATCH 375/393] =?UTF-8?q?fix/#150:=20RxCocoa=20=EC=9D=98?= =?UTF-8?q?=EC=A1=B4=EC=84=B1=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchFeature.xcodeproj/project.pbxproj | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index 339781c4..e600b653 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 0506BE882DD79A6C006CDEDE /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0506BE872DD79A6C006CDEDE /* RxCocoa */; }; 052413092DCF7DA100C42E2D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; }; 0524130A2DCF7DA100C42E2D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 054A96202DCE38B500C0DD58 /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; @@ -40,7 +41,6 @@ 05CFFBF42DCB908B0051129F /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; }; 05CFFBF52DCB908B0051129F /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23422DC49AA200C761A5 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05CFFBF72DCB90A10051129F /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05CFFBF62DCB90A10051129F /* SnapKit */; }; - 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233B2DC49A7600C761A5 /* RxCocoa */; }; 05EC233E2DC49A7600C761A5 /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC233D2DC49A7600C761A5 /* RxSwift */; }; 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05EC23402DC49A8B00C761A5 /* ReactorKit */; }; 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05EC23462DC49AA800C761A5 /* DomainInterface.framework */; }; @@ -178,6 +178,7 @@ files = ( 05734C442DCDF7240093825D /* PresentationInterface.framework in Frameworks */, 05EC2AE92DC7C07400C761A5 /* RxRelay in Frameworks */, + 0506BE882DD79A6C006CDEDE /* RxCocoa in Frameworks */, 05EC285F2DC5C1CF00C761A5 /* DesignSystem.framework in Frameworks */, 05EC23472DC49AA800C761A5 /* DomainInterface.framework in Frameworks */, 05EC234B2DC49AB400C761A5 /* Then in Frameworks */, @@ -185,7 +186,6 @@ 05EC234E2DC49AC100C761A5 /* SnapKit in Frameworks */, 05734C082DCDA7D20093825D /* SearchFeatureInterface.framework in Frameworks */, 05EC23412DC49A8B00C761A5 /* ReactorKit in Frameworks */, - 05EC233C2DC49A7600C761A5 /* RxCocoa in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -306,12 +306,12 @@ ); name = SearchFeature; packageProductDependencies = ( - 05EC233B2DC49A7600C761A5 /* RxCocoa */, 05EC233D2DC49A7600C761A5 /* RxSwift */, 05EC23402DC49A8B00C761A5 /* ReactorKit */, 05EC234A2DC49AB400C761A5 /* Then */, 05EC234D2DC49AC100C761A5 /* SnapKit */, 05EC2AE82DC7C07400C761A5 /* RxRelay */, + 0506BE872DD79A6C006CDEDE /* RxCocoa */, ); productName = SearchFeature; productReference = 0516336D2DC457A900A6C0D1 /* SearchFeature.framework */; @@ -938,6 +938,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 0506BE872DD79A6C006CDEDE /* RxCocoa */ = { + isa = XCSwiftPackageProductDependency; + package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; + productName = RxCocoa; + }; 054A96252DCE38E900C0DD58 /* Tabman */ = { isa = XCSwiftPackageProductDependency; package = 054A96242DCE38E900C0DD58 /* XCRemoteSwiftPackageReference "Tabman" */; @@ -973,11 +978,6 @@ package = 05EC234C2DC49AC100C761A5 /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - 05EC233B2DC49A7600C761A5 /* RxCocoa */ = { - isa = XCSwiftPackageProductDependency; - package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; - productName = RxCocoa; - }; 05EC233D2DC49A7600C761A5 /* RxSwift */ = { isa = XCSwiftPackageProductDependency; package = 05EC233A2DC49A7600C761A5 /* XCRemoteSwiftPackageReference "RxSwift" */; From c68ef47483a449c32d4d055f7417b24487fa9437 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 01:11:36 +0900 Subject: [PATCH 376/393] =?UTF-8?q?refactor/#150:=20Fatory=EB=A5=BC=20valu?= =?UTF-8?q?e=20semantic=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 --- .../PopupSearch/Factory/PopupSearchLayoutFactory.swift | 2 +- .../SearchFeature/PopupSearch/View/PopupSearchView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index fb1cc401..b79662f1 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -1,7 +1,6 @@ import UIKit // MARK: - Layout -final class PopupSearchLayoutFactory { func makeCollectionViewLayout( dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? @@ -9,6 +8,7 @@ final class PopupSearchLayoutFactory { return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ -> NSCollectionLayoutSection? in guard let self = self, let dataSource = dataSourceProvider() else { return nil } +struct PopupSearchLayoutFactory { // sectionIndex를 사용하여 현재 dataSource에서 Section 타입을 가져옴 guard sectionIndex < dataSource.snapshot().numberOfSections, diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index e02d7f64..9ea87db4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -12,7 +12,7 @@ final class PopupSearchView: UIView { // MARK: - Properties private var dataSource: UICollectionViewDiffableDataSource? - private let layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() + private var layoutFactory: PopupSearchLayoutFactory = PopupSearchLayoutFactory() let recentSearchTagRemoveButtonTapped = PublishRelay() let recentSearchTagRemoveAllButtonTapped = PublishRelay() From 30c6167af22f1030a58da22591a5b0407c8f2de3 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 01:12:39 +0900 Subject: [PATCH 377/393] =?UTF-8?q?refactor/#150:=20=EB=B0=98=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20=EB=B0=8F=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EB=B0=94=EC=9D=B4=EB=8D=94=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 --- .../Layout/CollectionLayoutProvidable.swift | 5 + .../Layout/GridCollectionLayoutProvider.swift | 32 +++++ .../Layout/HeaderLayoutProvidable.swift | 5 + .../Layout/TagCollectionLayoutProvider.swift | 51 +++++++ .../Factory/PopupSearchLayoutFactory.swift | 128 +++++------------- .../PopupSearch/View/PopupSearchView.swift | 9 +- 6 files changed, 131 insertions(+), 99 deletions(-) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift new file mode 100644 index 00000000..d7c3efec --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift @@ -0,0 +1,5 @@ +import UIKit + +public protocol CollectionLayoutProvidable { + func makeLayout() -> NSCollectionLayoutSection +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift new file mode 100644 index 00000000..bfd54a1f --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift @@ -0,0 +1,32 @@ +import UIKit + +public struct GridCollectionLayoutProvider: CollectionLayoutProvidable { + public init() { } + + public func makeLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(0.5), + heightDimension: .absolute(249) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(249) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item, item] + ) + group.interItemSpacing = .fixed(16) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 24 + + return section + } +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift new file mode 100644 index 00000000..f700a126 --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift @@ -0,0 +1,5 @@ +import UIKit + +public protocol HeaderLayoutProvidable { + func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift new file mode 100644 index 00000000..0f6fb1d5 --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift @@ -0,0 +1,51 @@ +import UIKit + +public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLayoutProvidable { + public init() { } + + public func makeLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .absolute(31) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .estimated(31) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + section.interGroupSpacing = 6 + + return section + } + + public func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(24) + ) + return NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + } + + public func configureSectionInsets(_ section: NSCollectionLayoutSection, isRecentSearch: Bool) { + if isRecentSearch { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) + } else { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index b79662f1..82b96ddf 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -1,74 +1,51 @@ import UIKit +import DesignSystem // MARK: - Layout - - func makeCollectionViewLayout( - dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? - ) -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ -> NSCollectionLayoutSection? in - guard let self = self, - let dataSource = dataSourceProvider() else { return nil } struct PopupSearchLayoutFactory { + private let tagLayoutProvider = TagCollectionLayoutProvider() + private let gridLayoutProvider = GridCollectionLayoutProvider() + + private var sectionProvider: ((Int) -> PopupSearchSection?)? + + mutating func setSectionProvider(_ provider: @escaping (Int) -> PopupSearchSection?) { + self.sectionProvider = provider + } - // sectionIndex를 사용하여 현재 dataSource에서 Section 타입을 가져옴 - guard sectionIndex < dataSource.snapshot().numberOfSections, - let sectionType = dataSource.sectionIdentifier(for: sectionIndex) else { return nil } + func makeCollectionViewLayout() -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout { sectionIndex, environment -> NSCollectionLayoutSection? in + guard let sectionType = sectionProvider?(sectionIndex) else { return nil } switch sectionType { case .recentSearch: - return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) - + let layout = self.tagLayoutProvider.makeLayout() + self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: true) + layout.boundarySupplementaryItems = [ + self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) + ] + return layout + case .category: - return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.category.rawValue) - + let layout = self.tagLayoutProvider.makeLayout() + self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: false) + layout.boundarySupplementaryItems = [ + self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + ] + return layout + case .searchResultHeader: return makeSearchResultHeaderSectionLayout() - + case .searchResult: - return makeSearchResultSectionLayout() - + return self.gridLayoutProvider.makeLayout() + case .searchResultEmpty: return makeSearchResultEmptySectionLayout() } - }) - } - - func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .estimated(31) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .continuous - - if headerKind == PopupSearchView.SectionHeaderKind.recentSearch.rawValue { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) - } else { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) } - - section.interGroupSpacing = 6 - - section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] - - return section } - - func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { + + private func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -93,35 +70,7 @@ struct PopupSearchLayoutFactory { return section } - func makeSearchResultSectionLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(0.5), - heightDimension: .absolute(249) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(249) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item, item] - ) - group.interItemSpacing = .fixed(16) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) - section.interGroupSpacing = 24 - - return section - } - - func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { - + private func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -146,17 +95,4 @@ struct PopupSearchLayoutFactory { return section } - - func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(24) - ) - return NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) - } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 9ea87db4..da8d3b02 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -27,9 +27,12 @@ final class PopupSearchView: UIView { public let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { - let layout = layoutFactory.makeCollectionViewLayout { [weak self] in self?.dataSource } - - $0.setCollectionViewLayout(layout, animated: false) + layoutFactory.setSectionProvider { [weak self] index in + guard let self, let dataSource else { return nil } + return dataSource.sectionIdentifier(for: index) + } + + $0.setCollectionViewLayout(layoutFactory.makeCollectionViewLayout(), animated: false) $0.register( TagCollectionHeaderView.self, From 5505d8e4858b9367dd30a329d903ba877a61923c Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 01:12:39 +0900 Subject: [PATCH 378/393] =?UTF-8?q?refactor/#150:=20=EB=B0=98=EB=B3=B5?= =?UTF-8?q?=EB=90=98=EB=8A=94=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EC=B6=94=EC=83=81=ED=99=94=20=EB=8D=B0=EC=9D=B4=ED=84=B0?= =?UTF-8?q?=EC=86=8C=EC=8A=A4=20=EA=B2=B0=ED=95=A9=EB=8F=84=20=EC=A4=84?= =?UTF-8?q?=EC=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layout/CollectionLayoutProvidable.swift | 5 + .../Layout/GridCollectionLayoutProvider.swift | 32 +++++ .../Layout/HeaderLayoutProvidable.swift | 5 + .../Layout/TagCollectionLayoutProvider.swift | 51 +++++++ .../Factory/PopupSearchLayoutFactory.swift | 128 +++++------------- .../PopupSearch/View/PopupSearchView.swift | 9 +- 6 files changed, 131 insertions(+), 99 deletions(-) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift new file mode 100644 index 00000000..d7c3efec --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutProvidable.swift @@ -0,0 +1,5 @@ +import UIKit + +public protocol CollectionLayoutProvidable { + func makeLayout() -> NSCollectionLayoutSection +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift new file mode 100644 index 00000000..bfd54a1f --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift @@ -0,0 +1,32 @@ +import UIKit + +public struct GridCollectionLayoutProvider: CollectionLayoutProvidable { + public init() { } + + public func makeLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(0.5), + heightDimension: .absolute(249) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(249) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item, item] + ) + group.interItemSpacing = .fixed(16) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) + section.interGroupSpacing = 24 + + return section + } +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift new file mode 100644 index 00000000..f700a126 --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift @@ -0,0 +1,5 @@ +import UIKit + +public protocol HeaderLayoutProvidable { + func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift new file mode 100644 index 00000000..0f6fb1d5 --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift @@ -0,0 +1,51 @@ +import UIKit + +public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLayoutProvidable { + public init() { } + + public func makeLayout() -> NSCollectionLayoutSection { + // Item + let itemSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .absolute(31) + ) + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + // Group + let groupSize = NSCollectionLayoutSize( + widthDimension: .estimated(100), + heightDimension: .estimated(31) + ) + let group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: [item] + ) + + // Section + let section = NSCollectionLayoutSection(group: group) + section.orthogonalScrollingBehavior = .continuous + section.interGroupSpacing = 6 + + return section + } + + public func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { + let headerSize = NSCollectionLayoutSize( + widthDimension: .fractionalWidth(1.0), + heightDimension: .absolute(24) + ) + return NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: .top + ) + } + + public func configureSectionInsets(_ section: NSCollectionLayoutSection, isRecentSearch: Bool) { + if isRecentSearch { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) + } else { + section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) + } + } +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index b79662f1..82b96ddf 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -1,74 +1,51 @@ import UIKit +import DesignSystem // MARK: - Layout - - func makeCollectionViewLayout( - dataSourceProvider: @escaping () -> UICollectionViewDiffableDataSource? - ) -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout(sectionProvider: { [weak self] sectionIndex, _ -> NSCollectionLayoutSection? in - guard let self = self, - let dataSource = dataSourceProvider() else { return nil } struct PopupSearchLayoutFactory { + private let tagLayoutProvider = TagCollectionLayoutProvider() + private let gridLayoutProvider = GridCollectionLayoutProvider() + + private var sectionProvider: ((Int) -> PopupSearchSection?)? + + mutating func setSectionProvider(_ provider: @escaping (Int) -> PopupSearchSection?) { + self.sectionProvider = provider + } - // sectionIndex를 사용하여 현재 dataSource에서 Section 타입을 가져옴 - guard sectionIndex < dataSource.snapshot().numberOfSections, - let sectionType = dataSource.sectionIdentifier(for: sectionIndex) else { return nil } + func makeCollectionViewLayout() -> UICollectionViewLayout { + return UICollectionViewCompositionalLayout { sectionIndex, environment -> NSCollectionLayoutSection? in + guard let sectionType = sectionProvider?(sectionIndex) else { return nil } switch sectionType { case .recentSearch: - return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) - + let layout = self.tagLayoutProvider.makeLayout() + self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: true) + layout.boundarySupplementaryItems = [ + self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) + ] + return layout + case .category: - return makeTagSectionLayout(PopupSearchView.SectionHeaderKind.category.rawValue) - + let layout = self.tagLayoutProvider.makeLayout() + self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: false) + layout.boundarySupplementaryItems = [ + self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + ] + return layout + case .searchResultHeader: return makeSearchResultHeaderSectionLayout() - + case .searchResult: - return makeSearchResultSectionLayout() - + return self.gridLayoutProvider.makeLayout() + case .searchResultEmpty: return makeSearchResultEmptySectionLayout() } - }) - } - - func makeTagSectionLayout(_ headerKind: String) -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .estimated(31) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .continuous - - if headerKind == PopupSearchView.SectionHeaderKind.recentSearch.rawValue { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) - } else { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) } - - section.interGroupSpacing = 6 - - section.boundarySupplementaryItems = [makeTagCollectionHeaderLayout(headerKind)] - - return section } - - func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { + + private func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -93,35 +70,7 @@ struct PopupSearchLayoutFactory { return section } - func makeSearchResultSectionLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(0.5), - heightDimension: .absolute(249) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(249) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item, item] - ) - group.interItemSpacing = .fixed(16) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) - section.interGroupSpacing = 24 - - return section - } - - func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { - + private func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { // Item let itemSize = NSCollectionLayoutSize( widthDimension: .fractionalWidth(1.0), @@ -146,17 +95,4 @@ struct PopupSearchLayoutFactory { return section } - - func makeTagCollectionHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - // Header - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(24) - ) - return NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) - } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index 9ea87db4..da8d3b02 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -27,9 +27,12 @@ final class PopupSearchView: UIView { public let searchBar = PPSearchBarView() lazy var collectionView = UICollectionView(frame: .zero, collectionViewLayout: .init()).then { - let layout = layoutFactory.makeCollectionViewLayout { [weak self] in self?.dataSource } - - $0.setCollectionViewLayout(layout, animated: false) + layoutFactory.setSectionProvider { [weak self] index in + guard let self, let dataSource else { return nil } + return dataSource.sectionIdentifier(for: index) + } + + $0.setCollectionViewLayout(layoutFactory.makeCollectionViewLayout(), animated: false) $0.register( TagCollectionHeaderView.self, From 4fde99d93a45f5eba2afbbb963ea48f07f314697 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 20:15:02 +0900 Subject: [PATCH 379/393] =?UTF-8?q?refactor/#150:=20TagCollection=20layout?= =?UTF-8?q?=20inset=20=EC=84=A4=EC=A0=95=EC=9D=84=20=EC=99=B8=EB=B6=80?= =?UTF-8?q?=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 --- .../Layout/TagCollectionLayoutProvider.swift | 8 -------- .../Factory/PopupSearchLayoutFactory.swift | 16 +++++++++++----- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift index 0f6fb1d5..fafe7d65 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift @@ -40,12 +40,4 @@ public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLay alignment: .top ) } - - public func configureSectionInsets(_ section: NSCollectionLayoutSection, isRecentSearch: Bool) { - if isRecentSearch { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) - } else { - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) - } - } } diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index 82b96ddf..c0e786a9 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -13,23 +13,29 @@ struct PopupSearchLayoutFactory { } func makeCollectionViewLayout() -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout { sectionIndex, environment -> NSCollectionLayoutSection? in + return UICollectionViewCompositionalLayout { + sectionIndex, + environment -> NSCollectionLayoutSection? in guard let sectionType = sectionProvider?(sectionIndex) else { return nil } switch sectionType { case .recentSearch: let layout = self.tagLayoutProvider.makeLayout() - self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: true) + layout.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) layout.boundarySupplementaryItems = [ - self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.recentSearch.rawValue) + self.tagLayoutProvider.makeHeaderLayout( + PopupSearchView.SectionHeaderKind.recentSearch.rawValue + ) ] return layout case .category: let layout = self.tagLayoutProvider.makeLayout() - self.tagLayoutProvider.configureSectionInsets(layout, isRecentSearch: false) + layout.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) layout.boundarySupplementaryItems = [ - self.tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + self.tagLayoutProvider.makeHeaderLayout( + PopupSearchView.SectionHeaderKind.category.rawValue + ) ] return layout From 98ae966b4378dc31bd2cdb49886200d37afcbd0f Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sat, 17 May 2025 22:16:47 +0900 Subject: [PATCH 380/393] =?UTF-8?q?chore/#150:=20gitignore=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 --- .gitignore | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 4449f4b9..01fa263e 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,5 @@ fastlane/screenshots/**/*.png fastlane/test_output # Cursor -Poppool/buildServer.json +**/buildServer.json .vscode/* - From 08e997224cb430b08f3a689af2009b8e81e8cc1b Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 18 May 2025 22:45:16 +0900 Subject: [PATCH 381/393] =?UTF-8?q?feat/#150:=20CollectionLayoutBuilder=20?= =?UTF-8?q?=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Layout/CollectionLayoutBuilder.swift | 185 ++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift new file mode 100644 index 00000000..dc1b037a --- /dev/null +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift @@ -0,0 +1,185 @@ +import UIKit + +public final class CollectionLayoutBuilder { + private var itemSize: NSCollectionLayoutSize? + private var groupSize: NSCollectionLayoutSize? + private var numberOfItemsPerGroup: Int = 1 + private var interItemSpacing: NSCollectionLayoutSpacing? + private var section: NSCollectionLayoutSection? + private var headerItem: NSCollectionLayoutBoundarySupplementaryItem? + + public init() { } + + public init(section existingSection: NSCollectionLayoutSection) { + self.section = existingSection + } + + @discardableResult + public func item( + width: NSCollectionLayoutDimension, + height: NSCollectionLayoutDimension + ) -> Self { + itemSize = NSCollectionLayoutSize( + widthDimension: width, + heightDimension: height + ) + + return self + } + + @discardableResult + public func group( + width: NSCollectionLayoutDimension, + height: NSCollectionLayoutDimension + ) -> Self { + groupSize = NSCollectionLayoutSize( + widthDimension: width, + heightDimension: height + ) + + return self + } + + @discardableResult + public func numberOfItemsPerGroup(_ count: Int) -> Self { + numberOfItemsPerGroup = count + + return self + } + + @discardableResult + public func itemSpacing(_ spacing: CGFloat) -> Self { + interItemSpacing = .fixed(spacing) + + return self + } + + @discardableResult + public func withContentInsets( + top: CGFloat = 0, + leading: CGFloat = 0, + bottom: CGFloat = 0, + trailing: CGFloat = 0 + ) -> Self { + section?.contentInsets = NSDirectionalEdgeInsets( + top: top, + leading: leading, + bottom: bottom, + trailing: trailing + ) + + return self + } + + @discardableResult + public func composeSection(_ axis: UIAxis) -> Self { + guard let itemSize, let groupSize else { + fatalError("Item and Group must be set before creating section") + } + + let item = NSCollectionLayoutItem(layoutSize: itemSize) + + var group: NSCollectionLayoutGroup! + + switch axis { + case .vertical: + group = NSCollectionLayoutGroup.vertical( + layoutSize: groupSize, + subitems: Array(repeating: item, count: numberOfItemsPerGroup) + ) + + case .horizontal: + group = NSCollectionLayoutGroup.horizontal( + layoutSize: groupSize, + subitems: Array(repeating: item, count: numberOfItemsPerGroup) + ) + + default: fatalError("Can't compose section to selected axis") + } + + if let interItemSpacing { + group.interItemSpacing = interItemSpacing + } + + section = NSCollectionLayoutSection(group: group) + + return self + } + + @discardableResult + public func header( + elementKind: String, + width: NSCollectionLayoutDimension = .fractionalWidth(1.0), + height: NSCollectionLayoutDimension = .fractionalHeight(1.0), + alignment: NSRectAlignment = .top + ) -> Self { + let headerSize = NSCollectionLayoutSize( + widthDimension: width, + heightDimension: height + ) + + headerItem = NSCollectionLayoutBoundarySupplementaryItem( + layoutSize: headerSize, + elementKind: elementKind, + alignment: alignment + ) + + if let headerItem { + section?.boundarySupplementaryItems = [headerItem] + } + + return self + } + + @discardableResult + public func withScrollingBehavior(_ behavior: UICollectionLayoutSectionOrthogonalScrollingBehavior) -> Self { + section?.orthogonalScrollingBehavior = behavior + + return self + } + + @discardableResult + public func groupSpacing(_ spacing: CGFloat) -> Self { + section?.interGroupSpacing = spacing + + return self + } + + @discardableResult + public func modifySection(_ modifier: (NSCollectionLayoutSection) -> Void) -> Self { + if let section = self.section { + modifier(section) + } + return self + } + + @discardableResult + public func withExistingHeader(_ headerItem: NSCollectionLayoutBoundarySupplementaryItem) -> Self { + self.headerItem = headerItem + + if let section = self.section { + section.boundarySupplementaryItems = [headerItem] + } + + return self + } + + @discardableResult + public func header(_ headerItems: [NSCollectionLayoutBoundarySupplementaryItem]) -> Self { + if let section = self.section { + section.boundarySupplementaryItems = headerItems + } + + return self + } + + public func build() -> NSCollectionLayoutSection { + guard let section else { fatalError("Section must be created before building") } + return section + } + + public func buildHeader() -> NSCollectionLayoutBoundarySupplementaryItem { + guard let headerItem else { fatalError("Header must be created before building") } + return headerItem + } +} From 47f5066b5936ed2a0554a4209d598deed1f70d4d Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 18 May 2025 22:46:51 +0900 Subject: [PATCH 382/393] =?UTF-8?q?refactor/#150:=20LayoutFactory=EC=97=90?= =?UTF-8?q?=20=EB=B9=8C=EB=8D=94=20=ED=8C=A8=ED=84=B4=EC=9D=84=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=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 --- .../Layout/GridCollectionLayoutProvider.swift | 35 +++++----------- .../Layout/TagCollectionLayoutProvider.swift | 42 +++++-------------- 2 files changed, 20 insertions(+), 57 deletions(-) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift index bfd54a1f..a712d7cc 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift @@ -4,29 +4,14 @@ public struct GridCollectionLayoutProvider: CollectionLayoutProvidable { public init() { } public func makeLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(0.5), - heightDimension: .absolute(249) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(249) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item, item] - ) - group.interItemSpacing = .fixed(16) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) - section.interGroupSpacing = 24 - - return section + return CollectionLayoutBuilder() + .item(width: .fractionalWidth(0.5), height: .absolute(249)) + .group(width: .fractionalWidth(1.0), height: .absolute(249)) + .numberOfItemsPerGroup(2) + .itemSpacing(16) + .composeSection(.horizontal) + .withContentInsets(top: 16, leading: 20, bottom: 0, trailing: 20) + .groupSpacing(24) + .build() } -} +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift index fafe7d65..24f34a57 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift @@ -4,40 +4,18 @@ public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLay public init() { } public func makeLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .absolute(31) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .estimated(100), - heightDimension: .estimated(31) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.orthogonalScrollingBehavior = .continuous - section.interGroupSpacing = 6 - - return section + return CollectionLayoutBuilder() + .item(width: .estimated(100), height: .absolute(31)) + .group(width: .estimated(100), height: .estimated(31)) + .composeSection(.vertical) + .withScrollingBehavior(.continuous) + .groupSpacing(6) + .build() } public func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { - let headerSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .absolute(24) - ) - return NSCollectionLayoutBoundarySupplementaryItem( - layoutSize: headerSize, - elementKind: elementKind, - alignment: .top - ) + return CollectionLayoutBuilder() + .header(elementKind: elementKind, height: .absolute(24)) + .buildHeader() } } From 2580238a367fb814bd43c6b30e10d79af625139e Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 18 May 2025 22:48:01 +0900 Subject: [PATCH 383/393] =?UTF-8?q?refactor/#150:=20PopupSearch=EC=9D=98?= =?UTF-8?q?=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A6=AC=EC=97=90=20=EB=B9=8C=EB=8D=94=20=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Factory/PopupSearchLayoutFactory.swift | 116 +++++++----------- 1 file changed, 45 insertions(+), 71 deletions(-) diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift index c0e786a9..e134a780 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Factory/PopupSearchLayoutFactory.swift @@ -1,104 +1,78 @@ import UIKit + import DesignSystem // MARK: - Layout struct PopupSearchLayoutFactory { private let tagLayoutProvider = TagCollectionLayoutProvider() private let gridLayoutProvider = GridCollectionLayoutProvider() - + private var sectionProvider: ((Int) -> PopupSearchSection?)? - + mutating func setSectionProvider(_ provider: @escaping (Int) -> PopupSearchSection?) { self.sectionProvider = provider } func makeCollectionViewLayout() -> UICollectionViewLayout { - return UICollectionViewCompositionalLayout { - sectionIndex, - environment -> NSCollectionLayoutSection? in + return UICollectionViewCompositionalLayout { (sectionIndex, _) -> NSCollectionLayoutSection? in + guard let sectionType = sectionProvider?(sectionIndex) else { return nil } switch sectionType { case .recentSearch: - let layout = self.tagLayoutProvider.makeLayout() - layout.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 48, trailing: 20) - layout.boundarySupplementaryItems = [ - self.tagLayoutProvider.makeHeaderLayout( - PopupSearchView.SectionHeaderKind.recentSearch.rawValue - ) - ] - return layout - + return makeRecentSearchSectionLayout() + case .category: - let layout = self.tagLayoutProvider.makeLayout() - layout.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20) - layout.boundarySupplementaryItems = [ - self.tagLayoutProvider.makeHeaderLayout( - PopupSearchView.SectionHeaderKind.category.rawValue - ) - ] - return layout - + return makeCategorySectionLayout() + case .searchResultHeader: return makeSearchResultHeaderSectionLayout() - + case .searchResult: return self.gridLayoutProvider.makeLayout() - + case .searchResultEmpty: return makeSearchResultEmptySectionLayout() } } } - + + private func makeRecentSearchSectionLayout() -> NSCollectionLayoutSection { + + return CollectionLayoutBuilder(section: tagLayoutProvider.makeLayout()) + .withContentInsets(top: 16, leading: 20, bottom: 48, trailing: 20) + .header([self.tagLayoutProvider.makeHeaderLayout( + PopupSearchView.SectionHeaderKind.recentSearch.rawValue + )]) + .build() + } + + private func makeCategorySectionLayout() -> NSCollectionLayoutSection { + + return CollectionLayoutBuilder(section: tagLayoutProvider.makeLayout()) + .withContentInsets(top: 16, leading: 20, bottom: 16, trailing: 20) + .header([ + tagLayoutProvider.makeHeaderLayout(PopupSearchView.SectionHeaderKind.category.rawValue) + ]) + .build() + } + private func makeSearchResultHeaderSectionLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(22) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .estimated(22) - ) - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20) - - return section + + return CollectionLayoutBuilder() + .item(width: .fractionalWidth(1.0), height: .estimated(22)) + .group(width: .fractionalWidth(1.0), height: .estimated(22)) + .composeSection(.horizontal) + .withContentInsets(top: 0, leading: 20, bottom: 0, trailing: 20) + .build() } private func makeSearchResultEmptySectionLayout() -> NSCollectionLayoutSection { - // Item - let itemSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .fractionalHeight(1.0) - ) - let item = NSCollectionLayoutItem(layoutSize: itemSize) - - // Group - let groupSize = NSCollectionLayoutSize( - widthDimension: .fractionalWidth(1.0), - heightDimension: .fractionalHeight(1.0) - ) - - let group = NSCollectionLayoutGroup.horizontal( - layoutSize: groupSize, - subitems: [item] - ) - - // Section - let section = NSCollectionLayoutSection(group: group) - section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 20, bottom: 0, trailing: 20) - - return section + + return CollectionLayoutBuilder() + .item(width: .fractionalWidth(1.0), height: .fractionalHeight(1.0)) + .group(width: .fractionalWidth(1.0), height: .fractionalHeight(1.0)) + .composeSection(.vertical) + .build() } } From 65d74c89572194c22f46270e4b70fc1e3b2d5f5e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 18 May 2025 13:57:12 +0000 Subject: [PATCH 384/393] style/#150: Apply SwiftLint autocorrect --- .../Extension/Collection+.swift | 2 +- .../Layout/CollectionLayoutBuilder.swift | 46 +++++++++---------- .../Layout/GridCollectionLayoutProvider.swift | 2 +- .../Layout/HeaderLayoutProvidable.swift | 2 +- .../Layout/TagCollectionLayoutProvider.swift | 6 +-- .../Reactor/PopupSearchReactor.swift | 5 +- .../PopupSearch/View/PopupSearchView.swift | 2 +- 7 files changed, 31 insertions(+), 34 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift index a4b22fc3..f2c58874 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/Collection+.swift @@ -4,4 +4,4 @@ public extension Collection { subscript(safe index: Index) -> Element? { return indices.contains(index) ? self[index] : nil } -} +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift index dc1b037a..82c9a901 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/CollectionLayoutBuilder.swift @@ -7,7 +7,7 @@ public final class CollectionLayoutBuilder { private var interItemSpacing: NSCollectionLayoutSpacing? private var section: NSCollectionLayoutSection? private var headerItem: NSCollectionLayoutBoundarySupplementaryItem? - + public init() { } public init(section existingSection: NSCollectionLayoutSection) { @@ -26,7 +26,7 @@ public final class CollectionLayoutBuilder { return self } - + @discardableResult public func group( width: NSCollectionLayoutDimension, @@ -39,21 +39,21 @@ public final class CollectionLayoutBuilder { return self } - + @discardableResult public func numberOfItemsPerGroup(_ count: Int) -> Self { numberOfItemsPerGroup = count return self } - + @discardableResult public func itemSpacing(_ spacing: CGFloat) -> Self { interItemSpacing = .fixed(spacing) return self } - + @discardableResult public func withContentInsets( top: CGFloat = 0, @@ -70,13 +70,13 @@ public final class CollectionLayoutBuilder { return self } - + @discardableResult public func composeSection(_ axis: UIAxis) -> Self { guard let itemSize, let groupSize else { fatalError("Item and Group must be set before creating section") } - + let item = NSCollectionLayoutItem(layoutSize: itemSize) var group: NSCollectionLayoutGroup! @@ -96,16 +96,16 @@ public final class CollectionLayoutBuilder { default: fatalError("Can't compose section to selected axis") } - + if let interItemSpacing { group.interItemSpacing = interItemSpacing } - + section = NSCollectionLayoutSection(group: group) return self } - + @discardableResult public func header( elementKind: String, @@ -117,34 +117,34 @@ public final class CollectionLayoutBuilder { widthDimension: width, heightDimension: height ) - + headerItem = NSCollectionLayoutBoundarySupplementaryItem( layoutSize: headerSize, elementKind: elementKind, alignment: alignment ) - + if let headerItem { section?.boundarySupplementaryItems = [headerItem] } - + return self } - + @discardableResult public func withScrollingBehavior(_ behavior: UICollectionLayoutSectionOrthogonalScrollingBehavior) -> Self { section?.orthogonalScrollingBehavior = behavior return self } - + @discardableResult public func groupSpacing(_ spacing: CGFloat) -> Self { section?.interGroupSpacing = spacing return self } - + @discardableResult public func modifySection(_ modifier: (NSCollectionLayoutSection) -> Void) -> Self { if let section = self.section { @@ -152,15 +152,15 @@ public final class CollectionLayoutBuilder { } return self } - + @discardableResult public func withExistingHeader(_ headerItem: NSCollectionLayoutBoundarySupplementaryItem) -> Self { self.headerItem = headerItem - + if let section = self.section { section.boundarySupplementaryItems = [headerItem] } - + return self } @@ -169,17 +169,17 @@ public final class CollectionLayoutBuilder { if let section = self.section { section.boundarySupplementaryItems = headerItems } - + return self } - + public func build() -> NSCollectionLayoutSection { guard let section else { fatalError("Section must be created before building") } return section } - + public func buildHeader() -> NSCollectionLayoutBoundarySupplementaryItem { guard let headerItem else { fatalError("Header must be created before building") } return headerItem } -} +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift index a712d7cc..15322073 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/GridCollectionLayoutProvider.swift @@ -2,7 +2,7 @@ import UIKit public struct GridCollectionLayoutProvider: CollectionLayoutProvidable { public init() { } - + public func makeLayout() -> NSCollectionLayoutSection { return CollectionLayoutBuilder() .item(width: .fractionalWidth(0.5), height: .absolute(249)) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift index f700a126..ad2e25fe 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/HeaderLayoutProvidable.swift @@ -2,4 +2,4 @@ import UIKit public protocol HeaderLayoutProvidable { func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem -} +} diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift index 24f34a57..47fc72c1 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/Layout/TagCollectionLayoutProvider.swift @@ -2,7 +2,7 @@ import UIKit public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLayoutProvidable { public init() { } - + public func makeLayout() -> NSCollectionLayoutSection { return CollectionLayoutBuilder() .item(width: .estimated(100), height: .absolute(31)) @@ -12,10 +12,10 @@ public struct TagCollectionLayoutProvider: CollectionLayoutProvidable, HeaderLay .groupSpacing(6) .build() } - + public func makeHeaderLayout(_ elementKind: String) -> NSCollectionLayoutBoundarySupplementaryItem { return CollectionLayoutBuilder() .header(elementKind: elementKind, height: .absolute(24)) .buildHeader() } -} +} diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift index fde9cc33..2c931308 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/Reactor/PopupSearchReactor.swift @@ -295,9 +295,7 @@ private extension PopupSearchReactor { } func makeSearchResultEmpty(state: State) -> String? { - if !currentState.searchResultItems.isEmpty { return nil } - else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } - else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } + if !currentState.searchResultItems.isEmpty { return nil } else if currentState.isSearching { return "검색 결과가 없어요 :(\n다른 키워드로 검색해주세요" } else { return "검색 결과가 없어요 :(\n다른 옵션을 선택해주세요" } } /// 받침에 따라 이/가 를 판단해서 붙여준다. @@ -346,7 +344,6 @@ private extension PopupSearchReactor { } } - // MARK: - Mutate Handlers private extension PopupSearchReactor { func handleViewDidLoad() -> Observable { diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift index da8d3b02..bcd5b3e8 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature/PopupSearch/View/PopupSearchView.swift @@ -31,7 +31,7 @@ final class PopupSearchView: UIView { guard let self, let dataSource else { return nil } return dataSource.sectionIdentifier(for: index) } - + $0.setCollectionViewLayout(layoutFactory.makeCollectionViewLayout(), animated: false) $0.register( From db403e373d3f9eab865b55930d560526eb5915e4 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 19 May 2025 12:18:59 +0900 Subject: [PATCH 385/393] =?UTF-8?q?chore/#150:=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=EB=9E=98=EB=B9=97=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .coderabbit.yaml | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..85d62c3b --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,93 @@ +language: ko-KR # 언어 설정 + +early_access: true # 미리보기 기능 활성화 +enable_free_tier: true # 프리 티어 활성화 +auto_resolve_threads: false # 자동 해결 비활성화 + +reviews: + profile: chill + request_changes_workflow: true + high_level_summary: true # 리뷰에 대해 요약(high-level summary)를 자동 작성 + high_level_summary_placeholder: '@coderabbitai 요약' + auto_title_placeholder: '@coderabbitai' + poem: true + review_status: true # PR 리뷰 상태를 리뷰 요약란에 표시 + collapse_walkthrough: false # 리뷰 단계 설명을 기본적으로 접지 않음 + + abort_on_close: true # PR이 닫히면 리뷰 수행을 중단(abort) + + + auto_review: + enabled: true # 자동 리뷰 기능을 활성화 + auto_incremental_review: true # 커밋이 추가될 때마다 변경 사항에 대해서만 자동 수행 + ignore_title_keywords: [] # PR 제목에 포함되면 리뷰를 건너뛰는 키워드 목록 + labels: [] # 특정 라벨이 붙은 PR만 자동 리뷰 대상 + drafts: false # Draft 상태인 PR은 자동 리뷰 대상에서 제외(false면 제외) + base_branches: [] # 특정 브랜치만 리뷰하도록 + + tools: + shellcheck: # 셸 스크립트 문법 및 보안 검사 + enabled: true + ruff: # Python 코드 스타일 검사기 + enabled: true + markdownlint: # 마크다운 문법 검사 + enabled: true + github-checks: # GitHub 체크 연동 + 타임아웃(ms 단위) + enabled: true + timeout_ms: 90000 + languagetool: # 맞춤법, 문법 검사 + enabled: true + disabled_rules: + - EN_UNPAIRED_BRACKETS + - EN_UNPAIRED_QUOTES + disabled_categories: + - TYPOS + - TYPOGRAPHY + - CASING + enabled_only: false + level: default + enabled_rules: [] + enabled_categories: [] + biome: # JavaScript/TypeScript 정적 분석 + enabled: true + hadolint: # Dockerfile 코드 스타일 검사 + enabled: true + swiftlint: # Swift 코드 스타일 검사 + enabled: true + phpstan: # PHP 정적 분석 + enabled: true + level: default + golangci-lint: # Go 코드 스타일 검사 + enabled: true + yamllint: # YAML 형식 검사 + enabled: true + gitleaks: # Git 시크릿 노출 탐지 + enabled: true + checkov: # 인프라 보안 검사 + enabled: true + ast-grep: # AST 기반 코드 패턴 검사 + packages: [] + rule_dirs: [] + util_dirs: [] + essential_rules: true + +# CodeRabbit AI 챗 기능을 사용 가능하게 하고, +# 한 번에 처리 가능한 토큰 수를 최대 4096으로 제한 +chat: + enabled: true + max_token_length: 4096 + + +# 지식 기반에 사용할 학습 범위를 지정하십시오. +# 'Local' - Repository +# 'Global'- Organization +# 'Auto' - Repository(users public) + Organization(private) +knowledge_base: + web_search: # AI 웹 검색 허용 + enabled: true + learnings: # 학습 범위 설정 (local, global, auto) + scope: local + issues: # 이슈 자동 참조 범위 설정 (local, global, auto) + scope: auto + jira: + project_keys: [] From 7a1778a10006860e30506f1e69bbbc1b84cdda08 Mon Sep 17 00:00:00 2001 From: JunYoung Date: Wed, 21 May 2025 18:16:31 +0900 Subject: [PATCH 386/393] =?UTF-8?q?fix/#138:=20=EC=93=B0=EB=A0=88=EB=93=9C?= =?UTF-8?q?=20=EC=8A=A4=EC=9C=84=EC=B9=AD=EC=9D=B4=20=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=BD=94=EB=93=9C=EC=97=90=EB=A7=8C=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=ED=95=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 --- .../Extension/UIImageView+.swift | 31 +++++-------------- .../ImageLoader/ImageLoader.swift | 8 +++-- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift index b1c65770..651ebfde 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/Extension/UIImageView+.swift @@ -55,33 +55,16 @@ public extension UIImageView { // 현재 이미지 URL을 업데이트 currentImageURL = encodedURL - // 먼저 메모리 캐시 확인 - if let cachedImage = MemoryStorage.shared.fetchImage(url: encodedURL) { - self.image = cachedImage - completion?() - return - } - - // 다음으로 디스크 캐시 확인 - if let diskImage = DiskStorage.shared.fetchImage(url: encodedURL) { - MemoryStorage.shared.store(image: diskImage, url: encodedURL) - self.image = diskImage - completion?() - return - } - ImageLoader.shared.loadImage(with: encodedURL, defaultImage: placeholderImage, imageQuality: .origin) { [weak self] image in guard let self else { return } defer { completion?() } - DispatchQueue.main.async { - // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) - if self.currentImageURL == encodedURL { - if let image = image { - self.image = image - } else if self.image == nil { - // 이미지 로드 실패 시 기본 이미지 표시 - self.image = self.placeholderImage - } + // 현재 요청 ID와 캡처된 ID가 일치하는지 확인 (다른 이미지로 변경되었으면 무시) + if self.currentImageURL == encodedURL { + if let image = image { + self.image = image + } else if self.image == nil { + // 이미지 로드 실패 시 기본 이미지 표시 + self.image = self.placeholderImage } } } diff --git a/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift b/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift index efaecd9d..203d009b 100644 --- a/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift +++ b/Poppool/CoreLayer/Infrastructure/Infrastructure/ImageLoader/ImageLoader.swift @@ -98,12 +98,14 @@ private extension ImageLoader { if let data = data, let image = UIImage(data: data) { MemoryStorage.shared.store(image: image, url: stringURL) DiskStorage.shared.store(image: image, url: stringURL) - completion(.success(image)) + DispatchQueue.main.async { completion(.success(image)) } } else { - completion(.failure(ImageLoaderError.convertError(description: "Failed to convert data to UIImage"))) + DispatchQueue.main.async { + completion(.failure(ImageLoaderError.convertError(description: "Failed to convert data to UIImage"))) + } } case .failure(let error): - completion(.failure(error)) + DispatchQueue.main.async { completion(.failure(error)) } } } } From b0f9b86541e1611d4f0380f87a95691d8acc6b32 Mon Sep 17 00:00:00 2001 From: YeongHoon Song <37678646+0Hooni@users.noreply.github.com> Date: Tue, 10 Jun 2025 21:40:42 +0900 Subject: [PATCH 387/393] Update deploy_on_release.yml --- .github/workflows/deploy_on_release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 7c12c739..06d56cc8 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -10,7 +10,7 @@ jobs: runs-on: macos-15 # 최신 macOS 15 환경에서 실행 env: # app archive 및 export 에 쓰일 환경 변수 설정 - XC_PROJECT: Poppool/Poppool.xcodeproj + XC_WORKSPACE: Poppool/Poppool.xcworkspace XC_SCHEME: Poppool XC_ARCHIVE: Poppool.xcarchive @@ -90,7 +90,7 @@ jobs: - name: ⬇️ Archive app # 빌드 및 아카이브 run: | - xcodebuild clean archive -project $XC_PROJECT -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE + xcodebuild clean archive -project $XC_WORKSPACE -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE - name: ⬆️ Export app # export 를 통해 ipa 파일 만듦 run: | From 1a585ef0c316cade0a24d882a9dbedf1947bd1e2 Mon Sep 17 00:00:00 2001 From: YeongHoon Song <37678646+0Hooni@users.noreply.github.com> Date: Tue, 10 Jun 2025 21:53:20 +0900 Subject: [PATCH 388/393] Update deploy_on_release.yml --- .github/workflows/deploy_on_release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy_on_release.yml b/.github/workflows/deploy_on_release.yml index 06d56cc8..b6d01a45 100644 --- a/.github/workflows/deploy_on_release.yml +++ b/.github/workflows/deploy_on_release.yml @@ -90,7 +90,7 @@ jobs: - name: ⬇️ Archive app # 빌드 및 아카이브 run: | - xcodebuild clean archive -project $XC_WORKSPACE -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE + xcodebuild clean archive -workspace $XC_WORKSPACE -scheme $XC_SCHEME -configuration release -archivePath $XC_ARCHIVE - name: ⬆️ Export app # export 를 통해 ipa 파일 만듦 run: | From b6e3ff6bbdafad7d95078c28928f3100996f1894 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 10 Jun 2025 22:15:58 +0900 Subject: [PATCH 389/393] =?UTF-8?q?fix:=20=EB=A6=B4=EB=A6=AC=EC=A6=88?= =?UTF-8?q?=EC=9A=A9=20=ED=94=84=EB=A1=9C=EB=B9=84=EC=A0=80=EB=8B=9D?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Poppool/Poppool.xcodeproj/project.pbxproj | 17 ++++++++++------- .../xcshareddata/xcschemes/Poppool.xcscheme | 2 +- .../xcschemes/SearchFeatureDemo.xcscheme | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 42b9bd8c..933f09f7 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -80,6 +80,7 @@ isa = PBXFileSystemSynchronizedBuildFileExceptionSet; membershipExceptions = ( /Localized/Resource/LaunchScreen.storyboard, + Resource/Debug.xcconfig, Resource/Info.plist, ); target = BDCA41BC2CF35AC0005EECF6 /* Poppool */; @@ -212,7 +213,7 @@ attributes = { BuildIndependentTargetsInParallel = 1; LastSwiftUpdateCheck = 1540; - LastUpgradeCheck = 1620; + LastUpgradeCheck = 1640; TargetAttributes = { BDCA41BC2CF35AC0005EECF6 = { CreatedOnToolsVersion = 15.4; @@ -422,7 +423,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -444,11 +446,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -468,7 +470,8 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_ENTITLEMENTS = Poppool/Resource/Poppool.entitlements; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_IDENTITY = "Apple Development"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -490,11 +493,11 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.1.0; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme index 77d7ce2e..f14947f9 100644 --- a/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme +++ b/Poppool/Poppool.xcodeproj/xcshareddata/xcschemes/Poppool.xcscheme @@ -1,6 +1,6 @@ Date: Tue, 10 Jun 2025 22:44:47 +0900 Subject: [PATCH 390/393] =?UTF-8?q?fix:=20Target=20=EB=94=94=EB=B0=94?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4=20=EC=A0=95=EB=A6=AC=20=EB=B0=8F=20nested=20?= =?UTF-8?q?framework=20=EB=AC=B8=EC=A0=9C=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Data/Data.xcodeproj/project.pbxproj | 12 ++- .../Domain/Domain.xcodeproj/project.pbxproj | 12 ++- Poppool/Poppool.xcodeproj/project.pbxproj | 84 +++++++++---------- .../Presentation.xcodeproj/project.pbxproj | 33 -------- .../SearchFeature.xcodeproj/project.pbxproj | 40 ++------- 5 files changed, 68 insertions(+), 113 deletions(-) diff --git a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj index 427daac0..66df90ab 100644 --- a/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj +++ b/Poppool/DataLayer/Data/Data.xcodeproj/project.pbxproj @@ -417,11 +417,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Data; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -450,11 +454,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Data; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj index 94cc85b7..21286382 100644 --- a/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj +++ b/Poppool/DomainLayer/Domain/Domain.xcodeproj/project.pbxproj @@ -424,11 +424,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Domain; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -458,11 +462,15 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.Domain; 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,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index 933f09f7..c3af574e 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -9,12 +9,24 @@ /* Begin PBXBuildFile section */ 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1D82DB67C5900B141FF /* RxSwift */; }; 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C3C52DB67D7800B141FF /* RxGesture */; }; - 05734C5A2DCDFAC20093825D /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; }; - 05734C5B2DCDFAC20093825D /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05734C5C2DCDFAC20093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; }; - 05734C5D2DCDFAC20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05734C5F2DCE04CE0093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; }; - 05734C602DCE04CE0093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5CD2DF86C740070BB93 /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; + 0543C5CE2DF86C740070BB93 /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5D02DF86C790070BB93 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; }; + 0543C5D12DF86C790070BB93 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5D22DF86C7B0070BB93 /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; }; + 0543C5D32DF86C7B0070BB93 /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5D42DF86C7C0070BB93 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; + 0543C5D52DF86C7C0070BB93 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5D62DF86C7E0070BB93 /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; }; + 0543C5D72DF86C7E0070BB93 /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5D82DF86C7F0070BB93 /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; }; + 0543C5D92DF86C7F0070BB93 /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5DA2DF86C800070BB93 /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; }; + 0543C5DB2DF86C800070BB93 /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C5E2DCE04CE0093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5DC2DF86C810070BB93 /* SearchFeature.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; }; + 0543C5DD2DF86C810070BB93 /* SearchFeature.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C582DCDFAC20093825D /* SearchFeature.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 0543C5DE2DF86C830070BB93 /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; }; + 0543C5DF2DF86C830070BB93 /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C592DCDFAC20093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C632DCE04FA0093825D /* Pageboy in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C622DCE04FA0093825D /* Pageboy */; }; 05734C662DCE05070093825D /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C652DCE05070093825D /* PanModal */; }; 05734C682DCE05240093825D /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C672DCE05240093825D /* SnapKit */; }; @@ -22,39 +34,27 @@ 05734C6E2DCE05680093825D /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C6D2DCE05680093825D /* Tabman */; }; 05734C712DCE059D0093825D /* Then in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C702DCE059D0093825D /* Then */; }; 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */ = {isa = PBXBuildFile; productRef = 05BBA73D2DB75DA60047A061 /* KakaoSDKUser */; }; - 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; }; - 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05BDD3D52DB66E1700C1E192 /* DomainInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; }; - 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6072DB53A4900508FFD /* Data.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; }; - 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6082DB53A4900508FFD /* Domain.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; }; - 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D6092DB53A4900508FFD /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; }; - 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05C1D60A2DB53A4900508FFD /* Presentation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - 05CFFC432DCC83290051129F /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; }; - 05CFFC442DCC83290051129F /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05CFFC422DCC83290051129F /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4E1514292D99480200DFD08F /* NMapsMap */; }; 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 4E15142D2D994A3A00DFD08F /* KakaoSDKAuth */; }; 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 4EE360FC2D91876300D2441D /* NMapsMap */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ - 05C1D6132DB53A4900508FFD /* Embed Frameworks */ = { + 0543C5CF2DF86C740070BB93 /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; dstPath = ""; dstSubfolderSpec = 10; files = ( - 05734C5B2DCDFAC20093825D /* SearchFeature.framework in Embed Frameworks */, - 05BDD3D72DB66E1700C1E192 /* DomainInterface.framework in Embed Frameworks */, - 05C1D6102DB53A4900508FFD /* Infrastructure.framework in Embed Frameworks */, - 05C1D60E2DB53A4900508FFD /* Domain.framework in Embed Frameworks */, - 05734C602DCE04CE0093825D /* PresentationInterface.framework in Embed Frameworks */, - 05CFFC442DCC83290051129F /* DesignSystem.framework in Embed Frameworks */, - 05C1D6122DB53A4900508FFD /* Presentation.framework in Embed Frameworks */, - 05734C5D2DCDFAC20093825D /* SearchFeatureInterface.framework in Embed Frameworks */, - 05C1D60C2DB53A4900508FFD /* Data.framework in Embed Frameworks */, + 0543C5D32DF86C7B0070BB93 /* Domain.framework in Embed Frameworks */, + 0543C5CE2DF86C740070BB93 /* Data.framework in Embed Frameworks */, + 0543C5DB2DF86C800070BB93 /* PresentationInterface.framework in Embed Frameworks */, + 0543C5DF2DF86C830070BB93 /* SearchFeatureInterface.framework in Embed Frameworks */, + 0543C5D92DF86C7F0070BB93 /* Presentation.framework in Embed Frameworks */, + 0543C5D72DF86C7E0070BB93 /* Infrastructure.framework in Embed Frameworks */, + 0543C5DD2DF86C810070BB93 /* SearchFeature.framework in Embed Frameworks */, + 0543C5D52DF86C7C0070BB93 /* DomainInterface.framework in Embed Frameworks */, + 0543C5D12DF86C790070BB93 /* DesignSystem.framework in Embed Frameworks */, ); name = "Embed Frameworks"; runOnlyForDeploymentPostprocessing = 0; @@ -106,25 +106,25 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 0543C5D02DF86C790070BB93 /* DesignSystem.framework in Frameworks */, 05734C662DCE05070093825D /* PanModal in Frameworks */, - 05C1D60F2DB53A4900508FFD /* Infrastructure.framework in Frameworks */, - 05BDD3D62DB66E1700C1E192 /* DomainInterface.framework in Frameworks */, + 0543C5D42DF86C7C0070BB93 /* DomainInterface.framework in Frameworks */, + 0543C5D62DF86C7E0070BB93 /* Infrastructure.framework in Frameworks */, 0522C1D92DB67C5900B141FF /* RxSwift in Frameworks */, + 0543C5D82DF86C7F0070BB93 /* Presentation.framework in Frameworks */, + 0543C5DE2DF86C830070BB93 /* SearchFeatureInterface.framework in Frameworks */, 4E15142E2D994A3A00DFD08F /* KakaoSDKAuth in Frameworks */, + 0543C5D22DF86C7B0070BB93 /* Domain.framework in Frameworks */, 05734C6E2DCE05680093825D /* Tabman in Frameworks */, - 05C1D60D2DB53A4900508FFD /* Domain.framework in Frameworks */, - 05C1D6112DB53A4900508FFD /* Presentation.framework in Frameworks */, - 05734C5C2DCDFAC20093825D /* SearchFeatureInterface.framework in Frameworks */, - 05C1D60B2DB53A4900508FFD /* Data.framework in Frameworks */, 05734C712DCE059D0093825D /* Then in Frameworks */, 05BBA73E2DB75DA60047A061 /* KakaoSDKUser in Frameworks */, 05734C6B2DCE05550093825D /* ReactorKit in Frameworks */, - 05734C5A2DCDFAC20093825D /* SearchFeature.framework in Frameworks */, - 05734C5F2DCE04CE0093825D /* PresentationInterface.framework in Frameworks */, 0522C3C62DB67D7800B141FF /* RxGesture in Frameworks */, - 05CFFC432DCC83290051129F /* DesignSystem.framework in Frameworks */, + 0543C5DC2DF86C810070BB93 /* SearchFeature.framework in Frameworks */, 4EE360FD2D91876300D2441D /* NMapsMap in Frameworks */, + 0543C5DA2DF86C800070BB93 /* PresentationInterface.framework in Frameworks */, 05734C682DCE05240093825D /* SnapKit in Frameworks */, + 0543C5CD2DF86C740070BB93 /* Data.framework in Frameworks */, 05734C632DCE04FA0093825D /* Pageboy in Frameworks */, 4E15142A2D99480200DFD08F /* NMapsMap in Frameworks */, ); @@ -178,7 +178,7 @@ BDCA41B92CF35AC0005EECF6 /* Sources */, BDCA41BA2CF35AC0005EECF6 /* Frameworks */, BDCA41BB2CF35AC0005EECF6 /* Resources */, - 05C1D6132DB53A4900508FFD /* Embed Frameworks */, + 0543C5CF2DF86C740070BB93 /* Embed Frameworks */, ); buildRules = ( ); @@ -424,7 +424,7 @@ 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 = ""; @@ -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*]" = poppoolProvisioningProfile; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -471,7 +471,7 @@ 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 = ""; @@ -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*]" = poppoolProvisioningProfile; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; diff --git a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj index f64f8ae2..75b55bd5 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/Presentation/Presentation.xcodeproj/project.pbxproj @@ -10,14 +10,10 @@ 05125B982DB626E3001342A2 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05125B972DB626E3001342A2 /* ReactorKit */; }; 05125BA12DB6275C001342A2 /* PanModal in Frameworks */ = {isa = PBXBuildFile; productRef = 05125BA02DB6275C001342A2 /* PanModal */; }; 051631302DC3D1FD00A6C0D1 /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; }; - 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 0522C1E12DB67C8300B141FF /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0522C1E02DB67C8300B141FF /* RxSwift */; }; 05734C3C2DCDF6FE0093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; }; - 05734C3D2DCDF6FE0093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C352DCDF6EC0093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C412DCDF7190093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; }; - 05734C422DCDF7190093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C402DCDF7190093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C492DCDF7960093825D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; }; - 05734C4A2DCDF7960093825D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C482DCDF7960093825D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C572DCDF9E80093825D /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 05734C562DCDF9E80093825D /* NMapsMap */; }; 05BDD5CC2DB6756500C1E192 /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CB2DB6756500C1E192 /* SnapKit */; }; 05BDD5CF2DB6770300C1E192 /* Lottie in Frameworks */ = {isa = PBXBuildFile; productRef = 05BDD5CE2DB6770300C1E192 /* Lottie */; }; @@ -43,33 +39,6 @@ }; /* End PBXContainerItemProxy section */ -/* Begin PBXCopyFilesBuildPhase section */ - 05734C4B2DCDF7960093825D /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05734C4A2DCDF7960093825D /* DesignSystem.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 05BDD5C22DB6744000C1E192 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05734C422DCDF7190093825D /* SearchFeatureInterface.framework in Embed Frameworks */, - 05734C3D2DCDF6FE0093825D /* PresentationInterface.framework in Embed Frameworks */, - 051631312DC3D1FD00A6C0D1 /* DesignSystem.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - /* Begin PBXFileReference section */ 0516312F2DC3D1FD00A6C0D1 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C352DCDF6EC0093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -190,7 +159,6 @@ 05734C312DCDF6EC0093825D /* Sources */, 05734C322DCDF6EC0093825D /* Frameworks */, 05734C332DCDF6EC0093825D /* Resources */, - 05734C4B2DCDF7960093825D /* Embed Frameworks */, ); buildRules = ( ); @@ -214,7 +182,6 @@ 058CC9002DB537960084221A /* Sources */, 058CC9012DB537960084221A /* Frameworks */, 058CC9022DB537960084221A /* Resources */, - 05BDD5C22DB6744000C1E192 /* Embed Frameworks */, ); buildRules = ( ); diff --git a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj index e600b653..271b78b4 100644 --- a/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj +++ b/Poppool/PresentationLayer/SearchFeature/SearchFeature.xcodeproj/project.pbxproj @@ -9,18 +9,14 @@ /* Begin PBXBuildFile section */ 0506BE882DD79A6C006CDEDE /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0506BE872DD79A6C006CDEDE /* RxCocoa */; }; 052413092DCF7DA100C42E2D /* DesignSystem.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; }; - 0524130A2DCF7DA100C42E2D /* DesignSystem.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 052413082DCF7DA100C42E2D /* DesignSystem.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 054A96202DCE38B500C0DD58 /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; 054A96212DCE38B500C0DD58 /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 054A96262DCE38E900C0DD58 /* Tabman in Frameworks */ = {isa = PBXBuildFile; productRef = 054A96252DCE38E900C0DD58 /* Tabman */; }; 054A96282DCE390A00C0DD58 /* ReactorKit in Frameworks */ = {isa = PBXBuildFile; productRef = 054A96272DCE390A00C0DD58 /* ReactorKit */; }; 054A962B2DCE3A0300C0DD58 /* NMapsMap in Frameworks */ = {isa = PBXBuildFile; productRef = 054A962A2DCE3A0300C0DD58 /* NMapsMap */; }; 05734C082DCDA7D20093825D /* SearchFeatureInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; }; - 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734BF52DCDA6B90093825D /* SearchFeatureInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C2B2DCDD6380093825D /* Infrastructure.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; }; - 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C2A2DCDD6380093825D /* Infrastructure.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C442DCDF7240093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C432DCDF7240093825D /* PresentationInterface.framework */; }; - 05734C452DCDF7240093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C432DCDF7240093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C502DCDF8B40093825D /* PresentationInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */; }; 05734C512DCDF8B40093825D /* PresentationInterface.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 05734C532DCDF8B80093825D /* Presentation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 05734C522DCDF8B80093825D /* Presentation.framework */; }; @@ -75,30 +71,6 @@ /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ - 05734C0C2DCDA7D20093825D /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05734C452DCDF7240093825D /* PresentationInterface.framework in Embed Frameworks */, - 05734C092DCDA7D20093825D /* SearchFeatureInterface.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; - 05734C2D2DCDD6390093825D /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - 05734C2C2DCDD6380093825D /* Infrastructure.framework in Embed Frameworks */, - 0524130A2DCF7DA100C42E2D /* DesignSystem.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; 05CFFBE72DCB8F6C0051129F /* Embed Frameworks */ = { isa = PBXCopyFilesBuildPhase; buildActionMask = 2147483647; @@ -130,8 +102,6 @@ 05734C432DCDF7240093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C4F2DCDF8B40093825D /* PresentationInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PresentationInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05734C522DCDF8B80093825D /* Presentation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Presentation.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CoordinatorKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23422DC49AA200C761A5 /* DesignSystem.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DesignSystem.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC23462DC49AA800C761A5 /* DomainInterface.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DomainInterface.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 05EC28642DC5FDF800C761A5 /* Domain.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Domain.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -254,8 +224,6 @@ 05734C432DCDF7240093825D /* PresentationInterface.framework */, 05734C2A2DCDD6380093825D /* Infrastructure.framework */, 05734C232DCDD4CF0093825D /* DesignSystem.framework */, - 058AE0B12DCCE3E9009119B2 /* CoordinatorKit.framework */, - 058AE0AD2DCCE3E1009119B2 /* CoordinatorKit.framework */, 05EC286D2DC5FE6000C761A5 /* Infrastructure.framework */, 05EC286A2DC5FE5B00C761A5 /* Data.framework */, 05EC28642DC5FDF800C761A5 /* Domain.framework */, @@ -294,7 +262,6 @@ 051633692DC457A900A6C0D1 /* Sources */, 0516336A2DC457A900A6C0D1 /* Frameworks */, 0516336B2DC457A900A6C0D1 /* Resources */, - 05734C0C2DCDA7D20093825D /* Embed Frameworks */, ); buildRules = ( ); @@ -357,7 +324,6 @@ 05734BF12DCDA6B90093825D /* Sources */, 05734BF22DCDA6B90093825D /* Frameworks */, 05734BF32DCDA6B90093825D /* Resources */, - 05734C2D2DCDD6390093825D /* Embed Frameworks */, ); buildRules = ( ); @@ -706,6 +672,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = NO; CODE_SIGN_IDENTITY = "Apple Development"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Manual; @@ -729,11 +696,13 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolSearchFeatureDemoProvisioning; + SKIP_INSTALL = NO; 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_SKIP_AUTOLINKING_ALL_FRAMEWORKS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; @@ -744,6 +713,7 @@ buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_SKIP_APP_STORE_DEPLOYMENT = NO; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; @@ -761,11 +731,13 @@ MARKETING_VERSION = 1.0; PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool.SearchFeatureDemo; PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = NO; 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_SKIP_AUTOLINKING_ALL_FRAMEWORKS = NO; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 1; }; From 822cdc88d06a164c4b3c460c7bd159f2919b59c6 Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Tue, 10 Jun 2025 22:54:13 +0900 Subject: [PATCH 391/393] =?UTF-8?q?fix:=20=EB=B0=B0=ED=8F=AC=EC=9A=A9=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=B9=84=EC=A0=80=EB=8B=9D=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=82=AC=EC=9D=B4=EB=8B=9D=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 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Poppool/Poppool.xcodeproj/project.pbxproj b/Poppool/Poppool.xcodeproj/project.pbxproj index c3af574e..c4430902 100644 --- a/Poppool/Poppool.xcodeproj/project.pbxproj +++ b/Poppool/Poppool.xcodeproj/project.pbxproj @@ -424,7 +424,7 @@ 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 Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -450,7 +450,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -471,7 +471,7 @@ 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 Developer"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; CODE_SIGN_STYLE = Manual; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = ""; @@ -497,7 +497,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.poppoolIOS.poppool; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = poppoolProvisioningProfile; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = PoppoolGitHubAction; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From 0b33ace24ff793ac56fed281a2ef35cbeb82e94a Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Sun, 15 Jun 2025 18:11:42 +0900 Subject: [PATCH 392/393] =?UTF-8?q?fix/#155:=20=EA=B2=80=EC=83=89=EB=B0=94?= =?UTF-8?q?=20R=EA=B0=92=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DesignSystem/DesignSystem/Components/PPSearchBarView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift index 230078f1..0306def6 100644 --- a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift +++ b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Components/PPSearchBarView.swift @@ -15,6 +15,7 @@ public class PPSearchBarView: UIView { $0.searchTextField.setPlaceholder(text: "팝업스토어명을 입력해보세요", color: .g400, font: .korFont(style: .regular, size: 14)) $0.tintColor = .g400 $0.backgroundColor = .g50 + $0.layer.cornerRadius = 4 $0.setImage(UIImage(named: "icon_search_gray"), for: .search, state: .normal) $0.searchBarStyle = .minimal $0.searchTextField.clearButtonMode = .never From a6a18cd4df0e783ffb0d6d3b6e2d68c9b1ee50af Mon Sep 17 00:00:00 2001 From: 0Hooni Date: Mon, 16 Jun 2025 20:43:05 +0900 Subject: [PATCH 393/393] =?UTF-8?q?fix/#155:=20=EC=84=9C=EC=B9=98=EB=B0=94?= =?UTF-8?q?=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EB=8B=A4=EB=A5=B4=EB=8D=98=20?= =?UTF-8?q?=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Scene/Home/Main/HomeController.swift | 2 +- .../Scene/Map/MapView/MapSearchInput.swift | 34 ++++++++++--------- .../Scene/Map/MapView/MapView.swift | 2 +- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift index 990f7ce3..508c6c07 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Home/Main/HomeController.swift @@ -83,7 +83,7 @@ private extension HomeController { view.addSubview(homeHeaderView) homeHeaderView.snp.makeConstraints { make in - make.top.equalTo(view.safeAreaLayoutGuide).inset(7) + make.top.equalTo(view.safeAreaLayoutGuide).inset(12) make.leading.trailing.equalToSuperview() } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift index e5532286..cccf90ba 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapSearchInput.swift @@ -1,7 +1,11 @@ +import UIKit + +import DesignSystem + import ReactorKit import RxCocoa import RxSwift -import UIKit +import Then final class MapSearchInput: UIView, View { // MARK: - Components @@ -17,7 +21,7 @@ final class MapSearchInput: UIView, View { private let containerView: UIView = { let view = UIView() view.backgroundColor = .white - view.layer.cornerRadius = 8 + view.layer.cornerRadius = 4 return view }() @@ -28,22 +32,20 @@ final class MapSearchInput: UIView, View { return iv }() - let searchTextField: UITextField = { - let textField = UITextField() - textField.placeholder = "팝업스토어명, 지역을 입력해보세요" - textField.font = UIFont.systemFont(ofSize: 14, weight: .regular) - textField.clearButtonMode = .whileEditing - textField.textColor = .g400 - textField.returnKeyType = .search - textField.enablesReturnKeyAutomatically = true - textField.attributedPlaceholder = NSAttributedString( + let searchTextField = UITextField().then { + $0.placeholder = "팝업스토어명, 지역을 입력해보세요" + $0.font = .korFont(style: .regular, size: 14) + $0.clearButtonMode = .whileEditing + $0.textColor = .g400 + $0.returnKeyType = .search + $0.enablesReturnKeyAutomatically = true + $0.attributedPlaceholder = NSAttributedString( string: "팝업스토어명, 지역을 입력해보세요", attributes: [NSAttributedString.Key.foregroundColor: UIColor.g400] ) // 편집은 하지 않고, 탭으로 화면 전환을 유도 - textField.isEnabled = false - return textField - }() + $0.isEnabled = false + } // MARK: - Init init() { @@ -117,13 +119,13 @@ private extension MapSearchInput { } searchIcon.snp.makeConstraints { make in - make.leading.equalToSuperview().offset(16) + make.leading.equalToSuperview().offset(12) make.centerY.equalToSuperview() make.size.equalTo(20) } searchTextField.snp.makeConstraints { make in - make.leading.equalTo(searchIcon.snp.trailing).offset(8) + make.leading.equalTo(searchIcon.snp.trailing).offset(4) make.centerY.equalToSuperview() make.trailing.equalToSuperview().offset(-16) } diff --git a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift index af1529e9..f5737db4 100644 --- a/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift +++ b/Poppool/PresentationLayer/Presentation/Presentation/Scene/Map/MapView/MapView.swift @@ -101,7 +101,7 @@ private extension MapView { searchFilterContainer.addSubview(searchInput) searchInput.snp.makeConstraints { make in - make.top.equalToSuperview() + make.top.equalTo(safeAreaLayoutGuide).inset(12) make.leading.trailing.equalToSuperview().inset(20) make.height.equalTo(37) }

    @=tI&eGDVCr1! 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 z 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

    $_|Me{LJQI{U8CrqyQ((MAgn(Z6#+ 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 literal 0 HcmV?d00001 diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Medium.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Medium.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e1d3a9b85ef35ab02885525a38d220e71f11c752 GIT binary patch literal 2287684 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

  • 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≥9&kOkrY}eHl_qE3|oCa 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 H87caI7&Rq{ literal 0 HcmV?d00001 diff --git a/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Light.ttf b/Poppool/PresentationLayer/DesignSystem/DesignSystem/Font/GothicA1-Light.ttf new file mode 100644 index 0000000000000000000000000000000000000000..1d88fa2b07451e6fd48b22d32de2d3b29ca296f9 GIT binary patch literal 2296968 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%0D-f8S+Yx*ZkgRA z0L!2M4SIL=v^UO{bJw<(KG{DFT??e*KC{|-W;V=iX_6;xZf%me);w^2PgnLf%HE7a z{^z#5!y9JLm_4Ia{oB?!BVU$oIFvb5I8-^*IMmglGK@o>LxDq)11c3%DyURYsi0Cp zrHV=wl`1N-uvsltRH~>{QK_O*MWu>L4V4-yHB@S-)KICRQbVPNN)447Ds@!qsMJxZ zqf$quj!GSsIx2Nk>Pkg8&Wf)OQ!4dgN~JzbsnmxlmHIHHQXi&N>cf;ueV9_I4?`u7 zN*#aSREnq+Q7NKQM5TyI z5tSk;MO2EY6j3RmQbMJKioAES#VVmvLZyUC36&BmB~;3&lu;?8Qbq-K>t$5RsFYDD zqf$l%cIy>XDyURYsZa~-)?v2}yLH&D!)_gR>#$pg-8$^nVYd#ub=a-LZXI^(uv>@S zI_%bAw+_2?*sa5E9d_%mTZi2`?ABqo4!d>Ot;22|cI&WPhuu2t)?v4<*v)3Byc}}K z)B8O~s<&dqe9J1AwL$+NLPfDF z5h{w^$}q*Q9H>RHD-pFQb|s<~#jZruqS&nrQ|!utS`@nyQHx?%B5G0WN<=M+U5Thg zv0E9Y*p&mdD0U^H7R9ba)S}pxh*}i8m0^lqIZ%sYS0ZXr>`Fu}id~7QMX@UpwJ3Hg z!xX!6pccihMAV|#m55ptyAn~0Vz)9(u`35^QS3@YEs9-Os_p{2dDzXvZXR~?u$zb7JnZISHxIjV&YP(eV7CCf1=uaXZUJ@+uq)@D zJV32(0e0mqT_s_+0J{a)Ex>L8b_=jufZYP@7GPJ-Yn2M@7GSpky9L-Sz-|F{3$R;& z-2&{&$-7d4-2&_uV7CCf1=uaXZUJ@+uv>s#`I4ekV7CCf1=uaXZUJ@+uv>uL0_+xG zSH83;71%AnZUJ@+uv>uL0_+xGw*b2Z*p)9aN(FWcuv>uL0_+xGw*b2Z*e$?r0d@