diff --git a/KoreaOneStep.xcodeproj/project.pbxproj b/KoreaOneStep.xcodeproj/project.pbxproj index 99d0125..b83d57c 100644 --- a/KoreaOneStep.xcodeproj/project.pbxproj +++ b/KoreaOneStep.xcodeproj/project.pbxproj @@ -7,89 +7,34 @@ objects = { /* Begin PBXBuildFile section */ - 000C39442C72334F003452C1 /* BookmarkViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000C39432C72334F003452C1 /* BookmarkViewModel.swift */; }; - 000C394E2C724066003452C1 /* BookmarkSectionData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000C394D2C724066003452C1 /* BookmarkSectionData.swift */; }; - 000F34A42BBAE49100643F56 /* KoreaTravelingAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000F34A32BBAE49100643F56 /* KoreaTravelingAPI.swift */; }; - 0015DE6A2BA19363002CA32C /* FloatingPanel in Frameworks */ = {isa = PBXBuildFile; productRef = 0015DE692BA19363002CA32C /* FloatingPanel */; }; - 0015DE6C2BA19592002CA32C /* ContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0015DE6B2BA19592002CA32C /* ContentViewController.swift */; }; - 0015DE6F2BA19CCE002CA32C /* SearchResultListTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0015DE6E2BA19CCE002CA32C /* SearchResultListTableViewCell.swift */; }; - 001D3E982BA6DAA800065C83 /* TouristDestionationCommonInformationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001D3E972BA6DAA800065C83 /* TouristDestionationCommonInformationModel.swift */; }; - 001D3E9A2BA6E2AD00065C83 /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001D3E992BA6E2AD00065C83 /* DetailViewModel.swift */; }; - 001D3E9C2BA6F4D100065C83 /* ProvidedImpairmentAidServicesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001D3E9B2BA6F4D100065C83 /* ProvidedImpairmentAidServicesModel.swift */; }; + 000103AD2CCF3592008DD062 /* KoreaTravelingUsecase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000103AC2CCF3592008DD062 /* KoreaTravelingUsecase.swift */; }; + 000103AF2CCF35C1008DD062 /* KoreaTravelingRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000103AE2CCF35C1008DD062 /* KoreaTravelingRepository.swift */; }; + 000103B12CCF35D3008DD062 /* KoreaTravelingAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000103B02CCF35D3008DD062 /* KoreaTravelingAPI.swift */; }; + 000103B32CCF35F6008DD062 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 000103B22CCF35F6008DD062 /* NetworkManager.swift */; }; + 001C2A9F2CD2543D003EC431 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 001C2A9E2CD2543D003EC431 /* ViewController.swift */; }; 001DD5C32BFA3C4E00825DAA /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 001DD5C22BFA3C4E00825DAA /* Kingfisher */; }; 001DD5D32BFA497400825DAA /* RealmSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 001DD5D22BFA497400825DAA /* RealmSwift */; }; 001DD5D42BFA49AD00825DAA /* RealmSwift in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 001DD5D22BFA497400825DAA /* RealmSwift */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; }; 001DD5D52BFA4C0B00825DAA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 00FD1C462B99840A00C2A76E /* Assets.xcassets */; }; 001DD5D62BFA4C1500825DAA /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00FD1C432B99840900C2A76E /* Main.storyboard */; }; - 0024F2FA2B9B45B600799623 /* TabBarViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0024F2F92B9B45B600799623 /* TabBarViewController.swift */; }; - 0024F2FF2B9B464A00799623 /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0024F2FE2B9B464A00799623 /* MainViewController.swift */; }; - 0024F3012B9B465200799623 /* BookmarkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0024F3002B9B465200799623 /* BookmarkViewController.swift */; }; - 0024F3032B9B465900799623 /* SettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0024F3022B9B465900799623 /* SettingViewController.swift */; }; - 0024F3052B9B4DE300799623 /* UIViewControllerConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0024F3042B9B4DE300799623 /* UIViewControllerConfiguration.swift */; }; - 00287F4B2BA2C1EA00B56A15 /* FilteringDistanceStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00287F4A2BA2C1EA00B56A15 /* FilteringDistanceStackView.swift */; }; - 00287F4D2BA2D44800B56A15 /* FilteringCategoryStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00287F4C2BA2D44800B56A15 /* FilteringCategoryStackView.swift */; }; - 00287F512BA2DDC300B56A15 /* FilteringOrder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00287F502BA2DDC300B56A15 /* FilteringOrder.swift */; }; - 003247282BA87015009642D4 /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003247272BA87015009642D4 /* SearchViewModel.swift */; }; - 0032472A2BA87317009642D4 /* AreaCodeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 003247292BA87317009642D4 /* AreaCodeModel.swift */; }; 003CE0472C087D66003B92FD /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 003CE0462C087D66003B92FD /* README.md */; }; - 004296412C74F17600D56138 /* MainViewModel2.swift in Sources */ = {isa = PBXBuildFile; fileRef = 004296402C74F17600D56138 /* MainViewModel2.swift */; }; 0047079C2C6F1AFD0025E5DB /* RxCocoa in Frameworks */ = {isa = PBXBuildFile; productRef = 0047079B2C6F1AFD0025E5DB /* RxCocoa */; }; 0047079E2C6F1AFD0025E5DB /* RxSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 0047079D2C6F1AFD0025E5DB /* RxSwift */; }; - 004CC96B2C8EF03100A7BBE4 /* Differentiator in Frameworks */ = {isa = PBXBuildFile; productRef = 004CC96A2C8EF03100A7BBE4 /* Differentiator */; }; - 004CC96D2C8EF03100A7BBE4 /* RxDataSources in Frameworks */ = {isa = PBXBuildFile; productRef = 004CC96C2C8EF03100A7BBE4 /* RxDataSources */; }; - 0051C2A32BA3516E0094D4BC /* TourType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0051C2A22BA3516E0094D4BC /* TourType.swift */; }; 0051C2A62BA35B6C0094D4BC /* SnapKit in Frameworks */ = {isa = PBXBuildFile; productRef = 0051C2A52BA35B6C0094D4BC /* SnapKit */; }; - 0051C2A92BA35BD80094D4BC /* Alamofire in Frameworks */ = {isa = PBXBuildFile; productRef = 0051C2A82BA35BD80094D4BC /* Alamofire */; }; - 005E57192BA00FFF005CD618 /* UITableViewHeaderFooterView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E57182BA00FFF005CD618 /* UITableViewHeaderFooterView+Extension.swift */; }; - 005E571B2BA010B0005CD618 /* RegionDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E571A2BA010B0005CD618 /* RegionDetailTableViewCell.swift */; }; - 005E571D2BA0190E005CD618 /* DetailTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E571C2BA0190E005CD618 /* DetailTableViewSection.swift */; }; - 005E57202BA01CB5005CD618 /* PhoneNumberTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E571F2BA01CB5005CD618 /* PhoneNumberTableViewCell.swift */; }; - 005E57222BA01F9F005CD618 /* AddressTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E57212BA01F9F005CD618 /* AddressTableViewCell.swift */; }; - 005E57242BA02466005CD618 /* ServiceProvidedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E57232BA02466005CD618 /* ServiceProvidedTableViewCell.swift */; }; - 005E572C2BA0395F005CD618 /* ServiceProvidedInternalCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E572B2BA0395F005CD618 /* ServiceProvidedInternalCollectionViewCell.swift */; }; - 005E572E2BA03AEC005CD618 /* ServiceProvidedDetailTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 005E572D2BA03AEC005CD618 /* ServiceProvidedDetailTableViewCell.swift */; }; - 0063D5F72BA531640081B59D /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0063D5F62BA531640081B59D /* MainViewModel.swift */; }; - 0063D5F92BA531E50081B59D /* CustomObservable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0063D5F82BA531E50081B59D /* CustomObservable.swift */; }; - 0063D5FD2BA5356B0081B59D /* KoreaTravelingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0063D5FC2BA5356B0081B59D /* KoreaTravelingManager.swift */; }; - 007323F62BA5892C001B0A2E /* LocationBasedTourismInformationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007323F52BA5892B001B0A2E /* LocationBasedTourismInformationModel.swift */; }; - 007323F82BA5A119001B0A2E /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007323F72BA5A119001B0A2E /* String+Extension.swift */; }; - 007323FD2BA5E621001B0A2E /* LocationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007323FC2BA5E621001B0A2E /* LocationManager.swift */; }; 007324012BA613D2001B0A2E /* APIKeys.swift in Sources */ = {isa = PBXBuildFile; fileRef = 007324002BA613D2001B0A2E /* APIKeys.swift */; }; - 008123232BA96A2500ABC9B7 /* KeywordBasedSearchgingModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008123222BA96A2500ABC9B7 /* KeywordBasedSearchgingModel.swift */; }; - 008123252BA9778200ABC9B7 /* SearchedResultsTableViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 008123242BA9778200ABC9B7 /* SearchedResultsTableViewTableViewCell.swift */; }; - 0081E8972B9D9C0500EF615D /* CellIdentifiable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8962B9D9C0500EF615D /* CellIdentifiable.swift */; }; - 0081E8992B9DAF4600EF615D /* UITableViewCell+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8982B9DAF4600EF615D /* UITableViewCell+Extension.swift */; }; - 0081E89C2B9DB10E00EF615D /* SettingTableViewCellTitle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E89B2B9DB10E00EF615D /* SettingTableViewCellTitle.swift */; }; - 0081E8A32B9DC11500EF615D /* SearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8A22B9DC11500EF615D /* SearchViewController.swift */; }; - 0081E8A62B9DC68200EF615D /* SearchTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8A52B9DC68200EF615D /* SearchTableViewCell.swift */; }; - 0081E8A82B9DC71100EF615D /* UITableViewCellConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8A72B9DC71100EF615D /* UITableViewCellConfiguration.swift */; }; - 0081E8AB2B9DD8E400EF615D /* FilterViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8AA2B9DD8E400EF615D /* FilterViewController.swift */; }; - 0081E8AE2B9DE05000EF615D /* TTGTags in Frameworks */ = {isa = PBXBuildFile; productRef = 0081E8AD2B9DE05000EF615D /* TTGTags */; }; - 0081E8B02B9DF2A100EF615D /* FilterTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0081E8AF2B9DF2A100EF615D /* FilterTableViewCell.swift */; }; - 009ACA7F2BAAD77100C937F7 /* RecentKeyword.swift in Sources */ = {isa = PBXBuildFile; fileRef = 009ACA7E2BAAD77100C937F7 /* RecentKeyword.swift */; }; + 008F083B2CCE2CAA00AFB60D /* FlexLayout in Frameworks */ = {isa = PBXBuildFile; productRef = 008F083A2CCE2CAA00AFB60D /* FlexLayout */; }; + 008F083E2CCE2CCC00AFB60D /* PinLayout in Frameworks */ = {isa = PBXBuildFile; productRef = 008F083D2CCE2CCC00AFB60D /* PinLayout */; }; 00A3D6222BAEE47800632748 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 00A3D6212BAEE47700632748 /* GoogleService-Info.plist */; }; - 00C8CB8B2BB1C23F0074A58A /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00C8CB8A2BB1C23F0074A58A /* UIViewController+Extension.swift */; }; - 00D18FC72BAC153700E1DC2A /* Toast in Frameworks */ = {isa = PBXBuildFile; productRef = 00D18FC62BAC153700E1DC2A /* Toast */; }; - 00D18FC92BAC167400E1DC2A /* ToastMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00D18FC82BAC167400E1DC2A /* ToastMessage.swift */; }; - 00DFE3BB2B9F3682005BF9D6 /* BookmarkCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DFE3BA2B9F3682005BF9D6 /* BookmarkCollectionViewCell.swift */; }; - 00DFE3BE2B9F452C005BF9D6 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00DFE3BD2B9F452C005BF9D6 /* DetailViewController.swift */; }; + 00E9F8842CCE7AF100E07A8A /* RxMoya in Frameworks */ = {isa = PBXBuildFile; productRef = 00E9F8832CCE7AF100E07A8A /* RxMoya */; }; + 00E9F8A12CCE891500E07A8A /* LocationBasedTourismInformationDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F89C2CCE891500E07A8A /* LocationBasedTourismInformationDTO.swift */; }; + 00E9F8A22CCE891500E07A8A /* KeywordBasedSearchingDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F8A02CCE891500E07A8A /* KeywordBasedSearchingDTO.swift */; }; + 00E9F8A32CCE891500E07A8A /* ProvidedImpairmentAidServicesDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F89E2CCE891500E07A8A /* ProvidedImpairmentAidServicesDTO.swift */; }; + 00E9F8A42CCE891500E07A8A /* TouristDestionationCommonInformationDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F89D2CCE891500E07A8A /* TouristDestionationCommonInformationDTO.swift */; }; + 00E9F8A52CCE891500E07A8A /* AreaCodeDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F89F2CCE891500E07A8A /* AreaCodeDTO.swift */; }; + 00E9F8A72CCE893000E07A8A /* DTOModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00E9F8A62CCE893000E07A8A /* DTOModelType.swift */; }; 00EB3B242BAD68DC0073A52A /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 00EB3B232BAD68DC0073A52A /* FirebaseAnalyticsWithoutAdIdSupport */; }; 00EB3B262BAD68DC0073A52A /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 00EB3B252BAD68DC0073A52A /* FirebaseCrashlytics */; }; - 00ED3FEE2C707B6E00B50FB2 /* ViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00ED3FED2C707B6E00B50FB2 /* ViewModelType.swift */; }; - 00ED3FF02C707D5900B50FB2 /* SettingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00ED3FEF2C707D5900B50FB2 /* SettingViewModel.swift */; }; 00ED3FF52C708EEC00B50FB2 /* RxAppState in Frameworks */ = {isa = PBXBuildFile; productRef = 00ED3FF42C708EEC00B50FB2 /* RxAppState */; }; - 00F4F86B2BA7B57200071D89 /* RealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F4F86A2BA7B57200071D89 /* RealmManager.swift */; }; - 00F4F86F2BA7B6AE00071D89 /* Bookmark.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F4F86E2BA7B6AE00071D89 /* Bookmark.swift */; }; - 00F59C992BA1E70100CE49C4 /* ContentTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F59C982BA1E70100CE49C4 /* ContentTableViewSection.swift */; }; - 00F59C9D2BA1E94700CE49C4 /* AmbientSettingsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F59C9C2BA1E94700CE49C4 /* AmbientSettingsTableViewCell.swift */; }; - 00F59C9F2BA1F25600CE49C4 /* AccordionTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F59C9E2BA1F25600CE49C4 /* AccordionTableViewCell.swift */; }; - 00F77AA72B9EBC1200A6A2E0 /* FilterTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77AA62B9EBC1200A6A2E0 /* FilterTableViewSection.swift */; }; - 00F77AC92B9EE55D00A6A2E0 /* BookmarkHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77AC82B9EE55D00A6A2E0 /* BookmarkHeaderView.swift */; }; - 00F77ACB2B9EE68600A6A2E0 /* BookmarkTableViewSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77ACA2B9EE68600A6A2E0 /* BookmarkTableViewSection.swift */; }; - 00F77ACF2B9EEA8C00A6A2E0 /* UICollectionViewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77ACE2B9EEA8C00A6A2E0 /* UICollectionViewConfiguration.swift */; }; - 00F77AD12B9EED9700A6A2E0 /* BookmarkHeaderViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77AD02B9EED9700A6A2E0 /* BookmarkHeaderViewCell.swift */; }; - 00F77AD42B9EEDFA00A6A2E0 /* UICollectionViewCellConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77AD32B9EEDFA00A6A2E0 /* UICollectionViewCellConfiguration.swift */; }; - 00F77ADB2B9F07A500A6A2E0 /* UICollectionReusableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00F77ADA2B9F07A500A6A2E0 /* UICollectionReusableView+Extension.swift */; }; 00FD1C3E2B99840900C2A76E /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FD1C3D2B99840900C2A76E /* AppDelegate.swift */; }; 00FD1C402B99840900C2A76E /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 00FD1C3F2B99840900C2A76E /* SceneDelegate.swift */; }; 00FD1C4A2B99840A00C2A76E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 00FD1C482B99840A00C2A76E /* LaunchScreen.storyboard */; }; @@ -112,72 +57,20 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 000C39432C72334F003452C1 /* BookmarkViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkViewModel.swift; sourceTree = ""; }; - 000C394D2C724066003452C1 /* BookmarkSectionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkSectionData.swift; sourceTree = ""; }; - 000F34A32BBAE49100643F56 /* KoreaTravelingAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoreaTravelingAPI.swift; sourceTree = ""; }; - 0015DE6B2BA19592002CA32C /* ContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewController.swift; sourceTree = ""; }; - 0015DE6E2BA19CCE002CA32C /* SearchResultListTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchResultListTableViewCell.swift; sourceTree = ""; }; - 001D3E972BA6DAA800065C83 /* TouristDestionationCommonInformationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouristDestionationCommonInformationModel.swift; sourceTree = ""; }; - 001D3E992BA6E2AD00065C83 /* DetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewModel.swift; sourceTree = ""; }; - 001D3E9B2BA6F4D100065C83 /* ProvidedImpairmentAidServicesModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProvidedImpairmentAidServicesModel.swift; sourceTree = ""; }; - 0024F2F92B9B45B600799623 /* TabBarViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarViewController.swift; sourceTree = ""; }; - 0024F2FE2B9B464A00799623 /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; - 0024F3002B9B465200799623 /* BookmarkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkViewController.swift; sourceTree = ""; }; - 0024F3022B9B465900799623 /* SettingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingViewController.swift; sourceTree = ""; }; - 0024F3042B9B4DE300799623 /* UIViewControllerConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewControllerConfiguration.swift; sourceTree = ""; }; - 00287F4A2BA2C1EA00B56A15 /* FilteringDistanceStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilteringDistanceStackView.swift; sourceTree = ""; }; - 00287F4C2BA2D44800B56A15 /* FilteringCategoryStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilteringCategoryStackView.swift; sourceTree = ""; }; - 00287F502BA2DDC300B56A15 /* FilteringOrder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilteringOrder.swift; sourceTree = ""; }; - 003247272BA87015009642D4 /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = ""; }; - 003247292BA87317009642D4 /* AreaCodeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AreaCodeModel.swift; sourceTree = ""; }; + 000103AC2CCF3592008DD062 /* KoreaTravelingUsecase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoreaTravelingUsecase.swift; sourceTree = ""; }; + 000103AE2CCF35C1008DD062 /* KoreaTravelingRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoreaTravelingRepository.swift; sourceTree = ""; }; + 000103B02CCF35D3008DD062 /* KoreaTravelingAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoreaTravelingAPI.swift; sourceTree = ""; }; + 000103B22CCF35F6008DD062 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; + 001C2A9E2CD2543D003EC431 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 003CE0462C087D66003B92FD /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; - 004296402C74F17600D56138 /* MainViewModel2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel2.swift; sourceTree = ""; }; - 0051C2A22BA3516E0094D4BC /* TourType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TourType.swift; sourceTree = ""; }; - 005E57182BA00FFF005CD618 /* UITableViewHeaderFooterView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewHeaderFooterView+Extension.swift"; sourceTree = ""; }; - 005E571A2BA010B0005CD618 /* RegionDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RegionDetailTableViewCell.swift; sourceTree = ""; }; - 005E571C2BA0190E005CD618 /* DetailTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailTableViewSection.swift; sourceTree = ""; }; - 005E571F2BA01CB5005CD618 /* PhoneNumberTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhoneNumberTableViewCell.swift; sourceTree = ""; }; - 005E57212BA01F9F005CD618 /* AddressTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressTableViewCell.swift; sourceTree = ""; }; - 005E57232BA02466005CD618 /* ServiceProvidedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProvidedTableViewCell.swift; sourceTree = ""; }; - 005E572B2BA0395F005CD618 /* ServiceProvidedInternalCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProvidedInternalCollectionViewCell.swift; sourceTree = ""; }; - 005E572D2BA03AEC005CD618 /* ServiceProvidedDetailTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProvidedDetailTableViewCell.swift; sourceTree = ""; }; - 0063D5F62BA531640081B59D /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; - 0063D5F82BA531E50081B59D /* CustomObservable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomObservable.swift; sourceTree = ""; }; - 0063D5FC2BA5356B0081B59D /* KoreaTravelingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KoreaTravelingManager.swift; sourceTree = ""; }; - 007323F52BA5892B001B0A2E /* LocationBasedTourismInformationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationBasedTourismInformationModel.swift; sourceTree = ""; }; - 007323F72BA5A119001B0A2E /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; - 007323FC2BA5E621001B0A2E /* LocationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationManager.swift; sourceTree = ""; }; 007324002BA613D2001B0A2E /* APIKeys.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIKeys.swift; sourceTree = ""; }; - 008123222BA96A2500ABC9B7 /* KeywordBasedSearchgingModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeywordBasedSearchgingModel.swift; sourceTree = ""; }; - 008123242BA9778200ABC9B7 /* SearchedResultsTableViewTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchedResultsTableViewTableViewCell.swift; sourceTree = ""; }; - 0081E8962B9D9C0500EF615D /* CellIdentifiable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellIdentifiable.swift; sourceTree = ""; }; - 0081E8982B9DAF4600EF615D /* UITableViewCell+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableViewCell+Extension.swift"; sourceTree = ""; }; - 0081E89B2B9DB10E00EF615D /* SettingTableViewCellTitle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingTableViewCellTitle.swift; sourceTree = ""; }; - 0081E8A22B9DC11500EF615D /* SearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewController.swift; sourceTree = ""; }; - 0081E8A52B9DC68200EF615D /* SearchTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchTableViewCell.swift; sourceTree = ""; }; - 0081E8A72B9DC71100EF615D /* UITableViewCellConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewCellConfiguration.swift; sourceTree = ""; }; - 0081E8AA2B9DD8E400EF615D /* FilterViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterViewController.swift; sourceTree = ""; }; - 0081E8AF2B9DF2A100EF615D /* FilterTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterTableViewCell.swift; sourceTree = ""; }; - 009ACA7E2BAAD77100C937F7 /* RecentKeyword.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentKeyword.swift; sourceTree = ""; }; 00A3D6212BAEE47700632748 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; - 00C8CB8A2BB1C23F0074A58A /* UIViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = ""; }; - 00D18FC82BAC167400E1DC2A /* ToastMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToastMessage.swift; sourceTree = ""; }; - 00DFE3BA2B9F3682005BF9D6 /* BookmarkCollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkCollectionViewCell.swift; sourceTree = ""; }; - 00DFE3BD2B9F452C005BF9D6 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; - 00ED3FED2C707B6E00B50FB2 /* ViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelType.swift; sourceTree = ""; }; - 00ED3FEF2C707D5900B50FB2 /* SettingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingViewModel.swift; sourceTree = ""; }; - 00F4F86A2BA7B57200071D89 /* RealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmManager.swift; sourceTree = ""; }; - 00F4F86E2BA7B6AE00071D89 /* Bookmark.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bookmark.swift; sourceTree = ""; }; - 00F59C982BA1E70100CE49C4 /* ContentTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentTableViewSection.swift; sourceTree = ""; }; - 00F59C9C2BA1E94700CE49C4 /* AmbientSettingsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmbientSettingsTableViewCell.swift; sourceTree = ""; }; - 00F59C9E2BA1F25600CE49C4 /* AccordionTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccordionTableViewCell.swift; sourceTree = ""; }; - 00F77AA62B9EBC1200A6A2E0 /* FilterTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterTableViewSection.swift; sourceTree = ""; }; - 00F77AC82B9EE55D00A6A2E0 /* BookmarkHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHeaderView.swift; sourceTree = ""; }; - 00F77ACA2B9EE68600A6A2E0 /* BookmarkTableViewSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkTableViewSection.swift; sourceTree = ""; }; - 00F77ACE2B9EEA8C00A6A2E0 /* UICollectionViewConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewConfiguration.swift; sourceTree = ""; }; - 00F77AD02B9EED9700A6A2E0 /* BookmarkHeaderViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkHeaderViewCell.swift; sourceTree = ""; }; - 00F77AD32B9EEDFA00A6A2E0 /* UICollectionViewCellConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UICollectionViewCellConfiguration.swift; sourceTree = ""; }; - 00F77ADA2B9F07A500A6A2E0 /* UICollectionReusableView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionReusableView+Extension.swift"; sourceTree = ""; }; + 00E9F89C2CCE891500E07A8A /* LocationBasedTourismInformationDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationBasedTourismInformationDTO.swift; sourceTree = ""; }; + 00E9F89D2CCE891500E07A8A /* TouristDestionationCommonInformationDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TouristDestionationCommonInformationDTO.swift; sourceTree = ""; }; + 00E9F89E2CCE891500E07A8A /* ProvidedImpairmentAidServicesDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProvidedImpairmentAidServicesDTO.swift; sourceTree = ""; }; + 00E9F89F2CCE891500E07A8A /* AreaCodeDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AreaCodeDTO.swift; sourceTree = ""; }; + 00E9F8A02CCE891500E07A8A /* KeywordBasedSearchingDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeywordBasedSearchingDTO.swift; sourceTree = ""; }; + 00E9F8A62CCE893000E07A8A /* DTOModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DTOModelType.swift; sourceTree = ""; }; 00FD1C3A2B99840900C2A76E /* KoreaOneStep.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KoreaOneStep.app; sourceTree = BUILT_PRODUCTS_DIR; }; 00FD1C3D2B99840900C2A76E /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 00FD1C3F2B99840900C2A76E /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; @@ -196,20 +89,17 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 004CC96B2C8EF03100A7BBE4 /* Differentiator in Frameworks */, - 00D18FC72BAC153700E1DC2A /* Toast in Frameworks */, 00ED3FF52C708EEC00B50FB2 /* RxAppState in Frameworks */, - 004CC96D2C8EF03100A7BBE4 /* RxDataSources in Frameworks */, 00EB3B242BAD68DC0073A52A /* FirebaseAnalyticsWithoutAdIdSupport in Frameworks */, 0047079C2C6F1AFD0025E5DB /* RxCocoa in Frameworks */, 00EB3B262BAD68DC0073A52A /* FirebaseCrashlytics in Frameworks */, - 0015DE6A2BA19363002CA32C /* FloatingPanel in Frameworks */, 0051C2A62BA35B6C0094D4BC /* SnapKit in Frameworks */, 0047079E2C6F1AFD0025E5DB /* RxSwift in Frameworks */, - 0051C2A92BA35BD80094D4BC /* Alamofire in Frameworks */, + 00E9F8842CCE7AF100E07A8A /* RxMoya in Frameworks */, + 008F083E2CCE2CCC00AFB60D /* PinLayout in Frameworks */, 001DD5C32BFA3C4E00825DAA /* Kingfisher in Frameworks */, - 0081E8AE2B9DE05000EF615D /* TTGTags in Frameworks */, 001DD5D32BFA497400825DAA /* RealmSwift in Frameworks */, + 008F083B2CCE2CAA00AFB60D /* FlexLayout in Frameworks */, 59C03BE6C333BA2CA5EC9571 /* Pods_KoreaOneStep.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -217,348 +107,85 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 000C394C2C72405D003452C1 /* BookmarkViewModel */ = { + 00E9F88D2CCE889400E07A8A /* Domain */ = { isa = PBXGroup; children = ( - 000C39432C72334F003452C1 /* BookmarkViewModel.swift */, - 000C394D2C724066003452C1 /* BookmarkSectionData.swift */, + 00E9F88E2CCE88A100E07A8A /* Usecase */, + 00E9F88F2CCE88B700E07A8A /* Entity */, ); - path = BookmarkViewModel; + path = Domain; sourceTree = ""; }; - 000F34A52BBAF4AD00643F56 /* Network */ = { + 00E9F88E2CCE88A100E07A8A /* Usecase */ = { isa = PBXGroup; children = ( - 000F34A32BBAE49100643F56 /* KoreaTravelingAPI.swift */, + 000103AC2CCF3592008DD062 /* KoreaTravelingUsecase.swift */, ); - path = Network; - sourceTree = ""; - }; - 0015DE6D2BA19CA6002CA32C /* FloatingPanel */ = { - isa = PBXGroup; - children = ( - 0015DE6B2BA19592002CA32C /* ContentViewController.swift */, - 00F59C9B2BA1E79D00CE49C4 /* AmbientSettings */, - 00F59C9A2BA1E78E00CE49C4 /* SearchList */, - ); - path = FloatingPanel; - sourceTree = ""; - }; - 0024F2F12B9B438D00799623 /* Scenes */ = { - isa = PBXGroup; - children = ( - 0024F2F92B9B45B600799623 /* TabBarViewController.swift */, - 0024F2FB2B9B45D700799623 /* Main */, - 0024F2FC2B9B45E000799623 /* Bookmark */, - 0024F2FD2B9B45ED00799623 /* Setting */, - 00DFE3BC2B9F4519005BF9D6 /* Detail */, - ); - path = Scenes; - sourceTree = ""; - }; - 0024F2F32B9B43A600799623 /* Models */ = { - isa = PBXGroup; - children = ( - 003247202BA81A20009642D4 /* Network */, - 00F4F86D2BA7B69000071D89 /* Realm */, - ); - path = Models; - sourceTree = ""; - }; - 0024F2F42B9B43AD00799623 /* ViewModels */ = { - isa = PBXGroup; - children = ( - 0063D5FA2BA532670081B59D /* CommunicationBase */, - 0063D5F62BA531640081B59D /* MainViewModel.swift */, - 004296402C74F17600D56138 /* MainViewModel2.swift */, - 001D3E992BA6E2AD00065C83 /* DetailViewModel.swift */, - 000C394C2C72405D003452C1 /* BookmarkViewModel */, - 003247272BA87015009642D4 /* SearchViewModel.swift */, - 00ED3FEF2C707D5900B50FB2 /* SettingViewModel.swift */, - ); - path = ViewModels; - sourceTree = ""; - }; - 0024F2F52B9B43B500799623 /* Extensions */ = { - isa = PBXGroup; - children = ( - 0081E8982B9DAF4600EF615D /* UITableViewCell+Extension.swift */, - 00F77ADA2B9F07A500A6A2E0 /* UICollectionReusableView+Extension.swift */, - 005E57182BA00FFF005CD618 /* UITableViewHeaderFooterView+Extension.swift */, - 007323F72BA5A119001B0A2E /* String+Extension.swift */, - 00C8CB8A2BB1C23F0074A58A /* UIViewController+Extension.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - 0024F2FB2B9B45D700799623 /* Main */ = { - isa = PBXGroup; - children = ( - 0024F2FE2B9B464A00799623 /* MainViewController.swift */, - 0015DE6D2BA19CA6002CA32C /* FloatingPanel */, - 0081E8A42B9DC66C00EF615D /* Search */, - ); - path = Main; - sourceTree = ""; - }; - 0024F2FC2B9B45E000799623 /* Bookmark */ = { - isa = PBXGroup; - children = ( - 0024F3002B9B465200799623 /* BookmarkViewController.swift */, - 00DFE3BA2B9F3682005BF9D6 /* BookmarkCollectionViewCell.swift */, - 00F77AD22B9EED9D00A6A2E0 /* BookmarkFirstSectionTableViewCell */, - ); - path = Bookmark; + path = Usecase; sourceTree = ""; }; - 0024F2FD2B9B45ED00799623 /* Setting */ = { + 00E9F88F2CCE88B700E07A8A /* Entity */ = { isa = PBXGroup; children = ( - 0024F3022B9B465900799623 /* SettingViewController.swift */, + 00E9F89C2CCE891500E07A8A /* LocationBasedTourismInformationDTO.swift */, + 00E9F89D2CCE891500E07A8A /* TouristDestionationCommonInformationDTO.swift */, + 00E9F89E2CCE891500E07A8A /* ProvidedImpairmentAidServicesDTO.swift */, + 00E9F89F2CCE891500E07A8A /* AreaCodeDTO.swift */, + 00E9F8A02CCE891500E07A8A /* KeywordBasedSearchingDTO.swift */, ); - path = Setting; + path = Entity; sourceTree = ""; }; - 0024F3062B9B4DF100799623 /* Protocols */ = { + 00E9F8A92CCE8B7D00E07A8A /* Data */ = { isa = PBXGroup; children = ( - 0081E8962B9D9C0500EF615D /* CellIdentifiable.swift */, - 0024F3042B9B4DE300799623 /* UIViewControllerConfiguration.swift */, - 0081E8A72B9DC71100EF615D /* UITableViewCellConfiguration.swift */, - 00F77ACE2B9EEA8C00A6A2E0 /* UICollectionViewConfiguration.swift */, - 00F77AD32B9EEDFA00A6A2E0 /* UICollectionViewCellConfiguration.swift */, - 00ED3FED2C707B6E00B50FB2 /* ViewModelType.swift */, + 00E9F8AA2CCE8B9200E07A8A /* Repository */, + 00E9F8AB2CCE8B9800E07A8A /* Network */, ); - path = Protocols; + path = Data; sourceTree = ""; }; - 00287F4E2BA2D6C300B56A15 /* AccordionTableViewCell */ = { + 00E9F8AA2CCE8B9200E07A8A /* Repository */ = { isa = PBXGroup; children = ( - 00F59C9E2BA1F25600CE49C4 /* AccordionTableViewCell.swift */, - 00287F4F2BA2D6C900B56A15 /* SubViews */, + 000103AE2CCF35C1008DD062 /* KoreaTravelingRepository.swift */, ); - path = AccordionTableViewCell; + path = Repository; sourceTree = ""; }; - 00287F4F2BA2D6C900B56A15 /* SubViews */ = { + 00E9F8AB2CCE8B9800E07A8A /* Network */ = { isa = PBXGroup; children = ( - 00287F4A2BA2C1EA00B56A15 /* FilteringDistanceStackView.swift */, - 00287F4C2BA2D44800B56A15 /* FilteringCategoryStackView.swift */, - ); - path = SubViews; - sourceTree = ""; - }; - 00287F522BA323E700B56A15 /* Main */ = { - isa = PBXGroup; - children = ( - 0051C2A22BA3516E0094D4BC /* TourType.swift */, - 00287F582BA3248500B56A15 /* FloatingPanel */, - 00287F532BA3241600B56A15 /* Search */, - ); - path = Main; - sourceTree = ""; - }; - 00287F532BA3241600B56A15 /* Search */ = { - isa = PBXGroup; - children = ( - 00287F542BA3242B00B56A15 /* Filter */, - ); - path = Search; - sourceTree = ""; - }; - 00287F542BA3242B00B56A15 /* Filter */ = { - isa = PBXGroup; - children = ( - 00F77AA62B9EBC1200A6A2E0 /* FilterTableViewSection.swift */, - ); - path = Filter; - sourceTree = ""; - }; - 00287F552BA3244A00B56A15 /* Detail */ = { - isa = PBXGroup; - children = ( - 005E571C2BA0190E005CD618 /* DetailTableViewSection.swift */, - ); - path = Detail; - sourceTree = ""; - }; - 00287F562BA3245500B56A15 /* Setting */ = { - isa = PBXGroup; - children = ( - 0081E89B2B9DB10E00EF615D /* SettingTableViewCellTitle.swift */, - ); - path = Setting; - sourceTree = ""; - }; - 00287F572BA3246100B56A15 /* Bookmark */ = { - isa = PBXGroup; - children = ( - 00F77ACA2B9EE68600A6A2E0 /* BookmarkTableViewSection.swift */, - ); - path = Bookmark; - sourceTree = ""; - }; - 00287F582BA3248500B56A15 /* FloatingPanel */ = { - isa = PBXGroup; - children = ( - 00287F502BA2DDC300B56A15 /* FilteringOrder.swift */, - 00F59C982BA1E70100CE49C4 /* ContentTableViewSection.swift */, - ); - path = FloatingPanel; - sourceTree = ""; - }; - 003247202BA81A20009642D4 /* Network */ = { - isa = PBXGroup; - children = ( - 007323F52BA5892B001B0A2E /* LocationBasedTourismInformationModel.swift */, - 001D3E972BA6DAA800065C83 /* TouristDestionationCommonInformationModel.swift */, - 001D3E9B2BA6F4D100065C83 /* ProvidedImpairmentAidServicesModel.swift */, - 003247292BA87317009642D4 /* AreaCodeModel.swift */, - 008123222BA96A2500ABC9B7 /* KeywordBasedSearchgingModel.swift */, + 000103B22CCF35F6008DD062 /* NetworkManager.swift */, + 000103B02CCF35D3008DD062 /* KoreaTravelingAPI.swift */, ); path = Network; sourceTree = ""; }; - 003247212BA81A58009642D4 /* Network */ = { + 00E9F8B62CCE8CD600E07A8A /* Presentation */ = { isa = PBXGroup; children = ( - 0063D5FC2BA5356B0081B59D /* KoreaTravelingManager.swift */, + 001C2A9E2CD2543D003EC431 /* ViewController.swift */, ); - path = Network; + path = Presentation; sourceTree = ""; }; - 003247222BA81A5F009642D4 /* Realm */ = { + 00E9F8BA2CCE8D1D00E07A8A /* Application */ = { isa = PBXGroup; children = ( - 00F4F86A2BA7B57200071D89 /* RealmManager.swift */, - ); - path = Realm; - sourceTree = ""; - }; - 005E571E2BA01CA2005CD618 /* DetailTableViewCells */ = { - isa = PBXGroup; - children = ( - 005E571A2BA010B0005CD618 /* RegionDetailTableViewCell.swift */, - 005E571F2BA01CB5005CD618 /* PhoneNumberTableViewCell.swift */, - 005E57212BA01F9F005CD618 /* AddressTableViewCell.swift */, - 005E57252BA02994005CD618 /* ServiceProvided */, - 005E572D2BA03AEC005CD618 /* ServiceProvidedDetailTableViewCell.swift */, - ); - path = DetailTableViewCells; - sourceTree = ""; - }; - 005E57252BA02994005CD618 /* ServiceProvided */ = { - isa = PBXGroup; - children = ( - 005E57232BA02466005CD618 /* ServiceProvidedTableViewCell.swift */, - 005E572B2BA0395F005CD618 /* ServiceProvidedInternalCollectionViewCell.swift */, - ); - path = ServiceProvided; - sourceTree = ""; - }; - 0063D5FA2BA532670081B59D /* CommunicationBase */ = { - isa = PBXGroup; - children = ( - 0063D5F82BA531E50081B59D /* CustomObservable.swift */, - ); - path = CommunicationBase; - sourceTree = ""; - }; - 0063D5FB2BA535290081B59D /* Managers */ = { - isa = PBXGroup; - children = ( - 007323FC2BA5E621001B0A2E /* LocationManager.swift */, - 003247212BA81A58009642D4 /* Network */, - 003247222BA81A5F009642D4 /* Realm */, - ); - path = Managers; - sourceTree = ""; - }; - 0081E89A2B9DB0E100EF615D /* Enums */ = { - isa = PBXGroup; - children = ( - 000F34A52BBAF4AD00643F56 /* Network */, - 0081E8B42B9E155300EF615D /* Scenes */, - ); - path = Enums; - sourceTree = ""; - }; - 0081E8A42B9DC66C00EF615D /* Search */ = { - isa = PBXGroup; - children = ( - 0081E8A22B9DC11500EF615D /* SearchViewController.swift */, - 0081E8A52B9DC68200EF615D /* SearchTableViewCell.swift */, - 008123242BA9778200ABC9B7 /* SearchedResultsTableViewTableViewCell.swift */, - 0081E8A92B9DD87C00EF615D /* Filter */, - ); - path = Search; - sourceTree = ""; - }; - 0081E8A92B9DD87C00EF615D /* Filter */ = { - isa = PBXGroup; - children = ( - 0081E8AA2B9DD8E400EF615D /* FilterViewController.swift */, - 0081E8AF2B9DF2A100EF615D /* FilterTableViewCell.swift */, - ); - path = Filter; - sourceTree = ""; - }; - 0081E8B42B9E155300EF615D /* Scenes */ = { - isa = PBXGroup; - children = ( - 00287F522BA323E700B56A15 /* Main */, - 00287F572BA3246100B56A15 /* Bookmark */, - 00287F562BA3245500B56A15 /* Setting */, - 00287F552BA3244A00B56A15 /* Detail */, - 00D18FC82BAC167400E1DC2A /* ToastMessage.swift */, - ); - path = Scenes; - sourceTree = ""; - }; - 00DFE3BC2B9F4519005BF9D6 /* Detail */ = { - isa = PBXGroup; - children = ( - 00DFE3BD2B9F452C005BF9D6 /* DetailViewController.swift */, - 005E571E2BA01CA2005CD618 /* DetailTableViewCells */, - ); - path = Detail; - sourceTree = ""; - }; - 00F4F86D2BA7B69000071D89 /* Realm */ = { - isa = PBXGroup; - children = ( - 00F4F86E2BA7B6AE00071D89 /* Bookmark.swift */, - 009ACA7E2BAAD77100C937F7 /* RecentKeyword.swift */, - ); - path = Realm; - sourceTree = ""; - }; - 00F59C9A2BA1E78E00CE49C4 /* SearchList */ = { - isa = PBXGroup; - children = ( - 0015DE6E2BA19CCE002CA32C /* SearchResultListTableViewCell.swift */, - ); - path = SearchList; - sourceTree = ""; - }; - 00F59C9B2BA1E79D00CE49C4 /* AmbientSettings */ = { - isa = PBXGroup; - children = ( - 00F59C9C2BA1E94700CE49C4 /* AmbientSettingsTableViewCell.swift */, - 00287F4E2BA2D6C300B56A15 /* AccordionTableViewCell */, + 00FD1C3D2B99840900C2A76E /* AppDelegate.swift */, + 00FD1C3F2B99840900C2A76E /* SceneDelegate.swift */, + 00E9F8BB2CCE8D2600E07A8A /* Common */, ); - path = AmbientSettings; + path = Application; sourceTree = ""; }; - 00F77AD22B9EED9D00A6A2E0 /* BookmarkFirstSectionTableViewCell */ = { + 00E9F8BB2CCE8D2600E07A8A /* Common */ = { isa = PBXGroup; children = ( - 00F77AC82B9EE55D00A6A2E0 /* BookmarkHeaderView.swift */, - 00F77AD02B9EED9700A6A2E0 /* BookmarkHeaderViewCell.swift */, + 00E9F8A62CCE893000E07A8A /* DTOModelType.swift */, ); - path = BookmarkFirstSectionTableViewCell; + path = Common; sourceTree = ""; }; 00FD1C312B99840900C2A76E = { @@ -584,15 +211,10 @@ 00FD1C3C2B99840900C2A76E /* KoreaOneStep */ = { isa = PBXGroup; children = ( - 00FD1C3D2B99840900C2A76E /* AppDelegate.swift */, - 00FD1C3F2B99840900C2A76E /* SceneDelegate.swift */, - 0081E89A2B9DB0E100EF615D /* Enums */, - 0024F3062B9B4DF100799623 /* Protocols */, - 0024F2F52B9B43B500799623 /* Extensions */, - 0024F2F42B9B43AD00799623 /* ViewModels */, - 0063D5FB2BA535290081B59D /* Managers */, - 0024F2F32B9B43A600799623 /* Models */, - 0024F2F12B9B438D00799623 /* Scenes */, + 00E9F8BA2CCE8D1D00E07A8A /* Application */, + 00E9F8B62CCE8CD600E07A8A /* Presentation */, + 00E9F88D2CCE889400E07A8A /* Domain */, + 00E9F8A92CCE8B7D00E07A8A /* Data */, 00FD1C432B99840900C2A76E /* Main.storyboard */, 00FD1C462B99840A00C2A76E /* Assets.xcassets */, 00FD1C482B99840A00C2A76E /* LaunchScreen.storyboard */, @@ -641,11 +263,7 @@ ); name = KoreaOneStep; packageProductDependencies = ( - 0081E8AD2B9DE05000EF615D /* TTGTags */, - 0015DE692BA19363002CA32C /* FloatingPanel */, 0051C2A52BA35B6C0094D4BC /* SnapKit */, - 0051C2A82BA35BD80094D4BC /* Alamofire */, - 00D18FC62BAC153700E1DC2A /* Toast */, 00EB3B232BAD68DC0073A52A /* FirebaseAnalyticsWithoutAdIdSupport */, 00EB3B252BAD68DC0073A52A /* FirebaseCrashlytics */, 001DD5C22BFA3C4E00825DAA /* Kingfisher */, @@ -653,8 +271,9 @@ 0047079B2C6F1AFD0025E5DB /* RxCocoa */, 0047079D2C6F1AFD0025E5DB /* RxSwift */, 00ED3FF42C708EEC00B50FB2 /* RxAppState */, - 004CC96A2C8EF03100A7BBE4 /* Differentiator */, - 004CC96C2C8EF03100A7BBE4 /* RxDataSources */, + 008F083A2CCE2CAA00AFB60D /* FlexLayout */, + 008F083D2CCE2CCC00AFB60D /* PinLayout */, + 00E9F8832CCE7AF100E07A8A /* RxMoya */, ); productName = KoreaOneStep; productReference = 00FD1C3A2B99840900C2A76E /* KoreaOneStep.app */; @@ -686,17 +305,15 @@ ); mainGroup = 00FD1C312B99840900C2A76E; packageReferences = ( - 0081E8AC2B9DE05000EF615D /* XCRemoteSwiftPackageReference "TTGTagCollectionView" */, - 0015DE682BA19362002CA32C /* XCRemoteSwiftPackageReference "FloatingPanel" */, 0051C2A42BA35B6C0094D4BC /* XCRemoteSwiftPackageReference "SnapKit" */, - 0051C2A72BA35BD80094D4BC /* XCRemoteSwiftPackageReference "Alamofire" */, - 00D18FC52BAC153700E1DC2A /* XCRemoteSwiftPackageReference "Toast-Swift" */, 00EB3B222BAD68DC0073A52A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, 001DD5C12BFA3C4E00825DAA /* XCRemoteSwiftPackageReference "Kingfisher" */, 001DD5D12BFA497300825DAA /* XCRemoteSwiftPackageReference "realm-swift" */, 0047079A2C6F1AFD0025E5DB /* XCRemoteSwiftPackageReference "RxSwift" */, 00ED3FF32C708EEC00B50FB2 /* XCRemoteSwiftPackageReference "RxAppState" */, - 004CC9692C8EF03100A7BBE4 /* XCRemoteSwiftPackageReference "RxDataSources" */, + 008F08392CCE2CAA00AFB60D /* XCRemoteSwiftPackageReference "FlexLayout" */, + 008F083C2CCE2CCC00AFB60D /* XCRemoteSwiftPackageReference "PinLayout" */, + 00E9F8822CCE7AF100E07A8A /* XCRemoteSwiftPackageReference "Moya" */, ); productRefGroup = 00FD1C3B2B99840900C2A76E /* Products */; projectDirPath = ""; @@ -792,72 +409,20 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 00287F4D2BA2D44800B56A15 /* FilteringCategoryStackView.swift in Sources */, - 001D3E9C2BA6F4D100065C83 /* ProvidedImpairmentAidServicesModel.swift in Sources */, - 005E571B2BA010B0005CD618 /* RegionDetailTableViewCell.swift in Sources */, - 005E572C2BA0395F005CD618 /* ServiceProvidedInternalCollectionViewCell.swift in Sources */, - 00287F4B2BA2C1EA00B56A15 /* FilteringDistanceStackView.swift in Sources */, - 00F77ADB2B9F07A500A6A2E0 /* UICollectionReusableView+Extension.swift in Sources */, - 0081E8AB2B9DD8E400EF615D /* FilterViewController.swift in Sources */, - 000C39442C72334F003452C1 /* BookmarkViewModel.swift in Sources */, - 00F59C9F2BA1F25600CE49C4 /* AccordionTableViewCell.swift in Sources */, - 00F77AC92B9EE55D00A6A2E0 /* BookmarkHeaderView.swift in Sources */, - 0024F2FA2B9B45B600799623 /* TabBarViewController.swift in Sources */, - 00F77ACB2B9EE68600A6A2E0 /* BookmarkTableViewSection.swift in Sources */, - 003247282BA87015009642D4 /* SearchViewModel.swift in Sources */, - 005E571D2BA0190E005CD618 /* DetailTableViewSection.swift in Sources */, - 00ED3FF02C707D5900B50FB2 /* SettingViewModel.swift in Sources */, - 00F59C992BA1E70100CE49C4 /* ContentTableViewSection.swift in Sources */, - 00F4F86B2BA7B57200071D89 /* RealmManager.swift in Sources */, - 00287F512BA2DDC300B56A15 /* FilteringOrder.swift in Sources */, - 001D3E9A2BA6E2AD00065C83 /* DetailViewModel.swift in Sources */, - 000F34A42BBAE49100643F56 /* KoreaTravelingAPI.swift in Sources */, - 00C8CB8B2BB1C23F0074A58A /* UIViewController+Extension.swift in Sources */, - 0081E8A32B9DC11500EF615D /* SearchViewController.swift in Sources */, - 005E57222BA01F9F005CD618 /* AddressTableViewCell.swift in Sources */, - 0081E8972B9D9C0500EF615D /* CellIdentifiable.swift in Sources */, + 00E9F8A12CCE891500E07A8A /* LocationBasedTourismInformationDTO.swift in Sources */, + 001C2A9F2CD2543D003EC431 /* ViewController.swift in Sources */, + 000103AD2CCF3592008DD062 /* KoreaTravelingUsecase.swift in Sources */, + 000103B32CCF35F6008DD062 /* NetworkManager.swift in Sources */, + 00E9F8A22CCE891500E07A8A /* KeywordBasedSearchingDTO.swift in Sources */, + 00E9F8A32CCE891500E07A8A /* ProvidedImpairmentAidServicesDTO.swift in Sources */, + 00E9F8A42CCE891500E07A8A /* TouristDestionationCommonInformationDTO.swift in Sources */, + 00E9F8A72CCE893000E07A8A /* DTOModelType.swift in Sources */, + 00E9F8A52CCE891500E07A8A /* AreaCodeDTO.swift in Sources */, + 000103AF2CCF35C1008DD062 /* KoreaTravelingRepository.swift in Sources */, 00FD1C3E2B99840900C2A76E /* AppDelegate.swift in Sources */, - 00F77AD42B9EEDFA00A6A2E0 /* UICollectionViewCellConfiguration.swift in Sources */, - 00F4F86F2BA7B6AE00071D89 /* Bookmark.swift in Sources */, - 005E57202BA01CB5005CD618 /* PhoneNumberTableViewCell.swift in Sources */, - 00F77ACF2B9EEA8C00A6A2E0 /* UICollectionViewConfiguration.swift in Sources */, - 000C394E2C724066003452C1 /* BookmarkSectionData.swift in Sources */, - 00ED3FEE2C707B6E00B50FB2 /* ViewModelType.swift in Sources */, - 0063D5F92BA531E50081B59D /* CustomObservable.swift in Sources */, - 008123252BA9778200ABC9B7 /* SearchedResultsTableViewTableViewCell.swift in Sources */, - 00F77AA72B9EBC1200A6A2E0 /* FilterTableViewSection.swift in Sources */, - 0032472A2BA87317009642D4 /* AreaCodeModel.swift in Sources */, - 00F77AD12B9EED9700A6A2E0 /* BookmarkHeaderViewCell.swift in Sources */, - 0024F3052B9B4DE300799623 /* UIViewControllerConfiguration.swift in Sources */, - 0081E8992B9DAF4600EF615D /* UITableViewCell+Extension.swift in Sources */, - 0015DE6C2BA19592002CA32C /* ContentViewController.swift in Sources */, 00FD1C402B99840900C2A76E /* SceneDelegate.swift in Sources */, - 005E57192BA00FFF005CD618 /* UITableViewHeaderFooterView+Extension.swift in Sources */, - 005E572E2BA03AEC005CD618 /* ServiceProvidedDetailTableViewCell.swift in Sources */, - 007323FD2BA5E621001B0A2E /* LocationManager.swift in Sources */, + 000103B12CCF35D3008DD062 /* KoreaTravelingAPI.swift in Sources */, 007324012BA613D2001B0A2E /* APIKeys.swift in Sources */, - 0015DE6F2BA19CCE002CA32C /* SearchResultListTableViewCell.swift in Sources */, - 004296412C74F17600D56138 /* MainViewModel2.swift in Sources */, - 007323F62BA5892C001B0A2E /* LocationBasedTourismInformationModel.swift in Sources */, - 001D3E982BA6DAA800065C83 /* TouristDestionationCommonInformationModel.swift in Sources */, - 00F59C9D2BA1E94700CE49C4 /* AmbientSettingsTableViewCell.swift in Sources */, - 005E57242BA02466005CD618 /* ServiceProvidedTableViewCell.swift in Sources */, - 00DFE3BE2B9F452C005BF9D6 /* DetailViewController.swift in Sources */, - 0024F3012B9B465200799623 /* BookmarkViewController.swift in Sources */, - 00DFE3BB2B9F3682005BF9D6 /* BookmarkCollectionViewCell.swift in Sources */, - 0051C2A32BA3516E0094D4BC /* TourType.swift in Sources */, - 0063D5F72BA531640081B59D /* MainViewModel.swift in Sources */, - 007323F82BA5A119001B0A2E /* String+Extension.swift in Sources */, - 00D18FC92BAC167400E1DC2A /* ToastMessage.swift in Sources */, - 0081E8B02B9DF2A100EF615D /* FilterTableViewCell.swift in Sources */, - 0024F3032B9B465900799623 /* SettingViewController.swift in Sources */, - 0081E8A62B9DC68200EF615D /* SearchTableViewCell.swift in Sources */, - 0081E8A82B9DC71100EF615D /* UITableViewCellConfiguration.swift in Sources */, - 0024F2FF2B9B464A00799623 /* MainViewController.swift in Sources */, - 009ACA7F2BAAD77100C937F7 /* RecentKeyword.swift in Sources */, - 0081E89C2B9DB10E00EF615D /* SettingTableViewCellTitle.swift in Sources */, - 008123232BA96A2500ABC9B7 /* KeywordBasedSearchgingModel.swift in Sources */, - 0063D5FD2BA5356B0081B59D /* KoreaTravelingManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1025,7 +590,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UIUserInterfaceStyle = Light; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1073,7 +638,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UIUserInterfaceStyle = Light; - IPHONEOS_DEPLOYMENT_TARGET = 15.0; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1123,14 +688,6 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 0015DE682BA19362002CA32C /* XCRemoteSwiftPackageReference "FloatingPanel" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scenee/FloatingPanel.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.8.2; - }; - }; 001DD5C12BFA3C4E00825DAA /* XCRemoteSwiftPackageReference "Kingfisher" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/onevcat/Kingfisher.git"; @@ -1155,14 +712,6 @@ minimumVersion = 6.7.1; }; }; - 004CC9692C8EF03100A7BBE4 /* XCRemoteSwiftPackageReference "RxDataSources" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/RxSwiftCommunity/RxDataSources.git"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 5.0.2; - }; - }; 0051C2A42BA35B6C0094D4BC /* XCRemoteSwiftPackageReference "SnapKit" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/SnapKit/SnapKit"; @@ -1171,28 +720,28 @@ minimumVersion = 5.7.1; }; }; - 0051C2A72BA35BD80094D4BC /* XCRemoteSwiftPackageReference "Alamofire" */ = { + 008F08392CCE2CAA00AFB60D /* XCRemoteSwiftPackageReference "FlexLayout" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/Alamofire/Alamofire"; + repositoryURL = "https://github.com/layoutBox/FlexLayout.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.9.1; + minimumVersion = 2.0.10; }; }; - 0081E8AC2B9DE05000EF615D /* XCRemoteSwiftPackageReference "TTGTagCollectionView" */ = { + 008F083C2CCE2CCC00AFB60D /* XCRemoteSwiftPackageReference "PinLayout" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/zekunyan/TTGTagCollectionView.git"; + repositoryURL = "https://github.com/layoutBox/PinLayout"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 2.4.2; + minimumVersion = 1.10.5; }; }; - 00D18FC52BAC153700E1DC2A /* XCRemoteSwiftPackageReference "Toast-Swift" */ = { + 00E9F8822CCE7AF100E07A8A /* XCRemoteSwiftPackageReference "Moya" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/scalessec/Toast-Swift.git"; + repositoryURL = "https://github.com/Moya/Moya.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 5.1.1; + minimumVersion = 15.0.3; }; }; 00EB3B222BAD68DC0073A52A /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { @@ -1214,11 +763,6 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 0015DE692BA19363002CA32C /* FloatingPanel */ = { - isa = XCSwiftPackageProductDependency; - package = 0015DE682BA19362002CA32C /* XCRemoteSwiftPackageReference "FloatingPanel" */; - productName = FloatingPanel; - }; 001DD5C22BFA3C4E00825DAA /* Kingfisher */ = { isa = XCSwiftPackageProductDependency; package = 001DD5C12BFA3C4E00825DAA /* XCRemoteSwiftPackageReference "Kingfisher" */; @@ -1239,35 +783,25 @@ package = 0047079A2C6F1AFD0025E5DB /* XCRemoteSwiftPackageReference "RxSwift" */; productName = RxSwift; }; - 004CC96A2C8EF03100A7BBE4 /* Differentiator */ = { - isa = XCSwiftPackageProductDependency; - package = 004CC9692C8EF03100A7BBE4 /* XCRemoteSwiftPackageReference "RxDataSources" */; - productName = Differentiator; - }; - 004CC96C2C8EF03100A7BBE4 /* RxDataSources */ = { - isa = XCSwiftPackageProductDependency; - package = 004CC9692C8EF03100A7BBE4 /* XCRemoteSwiftPackageReference "RxDataSources" */; - productName = RxDataSources; - }; 0051C2A52BA35B6C0094D4BC /* SnapKit */ = { isa = XCSwiftPackageProductDependency; package = 0051C2A42BA35B6C0094D4BC /* XCRemoteSwiftPackageReference "SnapKit" */; productName = SnapKit; }; - 0051C2A82BA35BD80094D4BC /* Alamofire */ = { + 008F083A2CCE2CAA00AFB60D /* FlexLayout */ = { isa = XCSwiftPackageProductDependency; - package = 0051C2A72BA35BD80094D4BC /* XCRemoteSwiftPackageReference "Alamofire" */; - productName = Alamofire; + package = 008F08392CCE2CAA00AFB60D /* XCRemoteSwiftPackageReference "FlexLayout" */; + productName = FlexLayout; }; - 0081E8AD2B9DE05000EF615D /* TTGTags */ = { + 008F083D2CCE2CCC00AFB60D /* PinLayout */ = { isa = XCSwiftPackageProductDependency; - package = 0081E8AC2B9DE05000EF615D /* XCRemoteSwiftPackageReference "TTGTagCollectionView" */; - productName = TTGTags; + package = 008F083C2CCE2CCC00AFB60D /* XCRemoteSwiftPackageReference "PinLayout" */; + productName = PinLayout; }; - 00D18FC62BAC153700E1DC2A /* Toast */ = { + 00E9F8832CCE7AF100E07A8A /* RxMoya */ = { isa = XCSwiftPackageProductDependency; - package = 00D18FC52BAC153700E1DC2A /* XCRemoteSwiftPackageReference "Toast-Swift" */; - productName = Toast; + package = 00E9F8822CCE7AF100E07A8A /* XCRemoteSwiftPackageReference "Moya" */; + productName = RxMoya; }; 00EB3B232BAD68DC0073A52A /* FirebaseAnalyticsWithoutAdIdSupport */ = { isa = XCSwiftPackageProductDependency; diff --git a/KoreaOneStep/AppDelegate.swift b/KoreaOneStep/Application/AppDelegate.swift similarity index 100% rename from KoreaOneStep/AppDelegate.swift rename to KoreaOneStep/Application/AppDelegate.swift diff --git a/KoreaOneStep/Application/Common/DTOModelType.swift b/KoreaOneStep/Application/Common/DTOModelType.swift new file mode 100644 index 0000000..6f99fbd --- /dev/null +++ b/KoreaOneStep/Application/Common/DTOModelType.swift @@ -0,0 +1,42 @@ +// +// DTOModelType.swift +// KoreaOneStep +// +// Created by SUCHAN CHANG on 10/27/24. +// + +import Foundation + +protocol DTOModelType: Decodable { + associatedtype Response: ResponseType + + var response: Response { get } +} + +protocol ResponseType: Decodable { + associatedtype Header: HeaderType + associatedtype Body: BodyType + + var header: Header { get } + var body: Body { get } +} + +protocol HeaderType: Decodable { + var resultCode: String { get } + var resultMsg: String { get } +} + +protocol BodyType: Decodable { + associatedtype Items : ItemType + + var items: Items { get } + var numOfRows: Int { get } + var pageNo: Int { get } + var totalCount: Int { get } +} + +protocol ItemType: Decodable { + associatedtype Item: Decodable + + var item: [Item] { get } +} diff --git a/KoreaOneStep/SceneDelegate.swift b/KoreaOneStep/Application/SceneDelegate.swift similarity index 97% rename from KoreaOneStep/SceneDelegate.swift rename to KoreaOneStep/Application/SceneDelegate.swift index 3dfcef8..8bb46a8 100644 --- a/KoreaOneStep/SceneDelegate.swift +++ b/KoreaOneStep/Application/SceneDelegate.swift @@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { guard let windowScene = (scene as? UIWindowScene) else { return } window = UIWindow(windowScene: windowScene) - window?.rootViewController = TabBarViewController() + window?.rootViewController = ViewController() window?.makeKeyAndVisible() } diff --git a/KoreaOneStep/Enums/Network/KoreaTravelingAPI.swift b/KoreaOneStep/Data/Network/KoreaTravelingAPI.swift similarity index 82% rename from KoreaOneStep/Enums/Network/KoreaTravelingAPI.swift rename to KoreaOneStep/Data/Network/KoreaTravelingAPI.swift index 057ba04..1e135e4 100644 --- a/KoreaOneStep/Enums/Network/KoreaTravelingAPI.swift +++ b/KoreaOneStep/Data/Network/KoreaTravelingAPI.swift @@ -1,20 +1,14 @@ // -// KoreaTravelingAPI.swift +// KoreaTravelingAPI2.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 4/1/24. +// Created by SUCHAN CHANG on 10/28/24. // import Foundation -import Alamofire +import Moya enum KoreaTravelingAPI { - static let radiusDefaultValue = FilteringOrder.FilteringDistance.oneKiloMeter.rawValue - static let arrageDefaultValue = FilteringOrder.title.sortingCode - static let contentTypeIdDefaultValue = "" - static let areaCodeDefaultValue = "" - static let sigunguCodeDefaultValue = "" - case providedImpairmentAidServices(contentId: String) case touristDestionationCommonInformation( contentId: String, @@ -33,34 +27,36 @@ enum KoreaTravelingAPI { areaCode: String, sigunguCode: String ) - - var baseURL: String { - return "https://apis.data.go.kr/B551011/KorWithService1" +} + +extension KoreaTravelingAPI: TargetType { + var baseURL: URL { + return URL(string: APIKeys.baseURL)! } - var endpoint: String { + var path: String { switch self { case .providedImpairmentAidServices: - return "\(baseURL)/detailWithTour1" + return "/detailWithTour1" case .touristDestionationCommonInformation: - return "\(baseURL)/detailCommon1" + return "/detailCommon1" case .locationBasedTourismInformation: - return "\(baseURL)/locationBasedList1" + return "/locationBasedList1" case .areaCode: - return "\(baseURL)/areaCode1" + return "/areaCode1" case .keywordBasedSearching: - return "\(baseURL)/searchKeyword1" + return "/searchKeyword1" } } - var method: HTTPMethod { + var method: Moya.Method { return .get } - var parameters: [String: String] { + var task: Moya.Task { switch self { case .providedImpairmentAidServices(let contentId): - return [ + let parameters: [String: String] = [ "serviceKey": APIKeys.serviceKey, // 필수, 인증키(서비스키) "numOfRows": "30", // 한페이지결과수 "pageNo": "1", // 페이지번호 @@ -69,8 +65,9 @@ enum KoreaTravelingAPI { "contentId": contentId, // 필수, 콘텐츠ID "_type": "json" // 응답메세지 형식 : REST방식의 URL호출 시 json값 추가(디폴트 응답메세지 형식은XML) ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case .touristDestionationCommonInformation(let contentId, let contentTypeId): - return [ + let parameters: [String: String] = [ "serviceKey": APIKeys.serviceKey, // 필수, 인증키(서비스키) "numOfRows": "30", // 한페이지결과수 "pageNo": "1", // 페이지번호 @@ -87,8 +84,9 @@ enum KoreaTravelingAPI { "_type": "json", // 응답메세지 형식 : REST방식의 URL호출 시 json값 추가(디폴트 응답메세지 형식은XML) "contentTypeId": contentTypeId // 관광타입(관광지, 숙박 등) ID ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case .locationBasedTourismInformation(let latitude, let longitude, let radius, let arrange, let contentTypeId): - return [ + let parameters: [String: String] = [ "serviceKey": APIKeys.serviceKey, // 인증키 "numOfRows": "30", // 한페이지 결과수 "pageNo": "1", // 페이지 번호 @@ -102,8 +100,9 @@ enum KoreaTravelingAPI { "_type": "json", // 응답메세지 형식 : REST방식의 URL호출 시 json값 추가(디폴트 응답메세지 형식은XML) "contentTypeId": contentTypeId // 관광타입(12:관광지, 14:문화시설, 15:축제공연행사, 25:여행코스, 28:레포츠, 32:숙박, 38:쇼핑, 39:음식점) ID ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case .areaCode(let areaCode): - return [ + let parameters: [String: String] = [ "serviceKey": APIKeys.serviceKey, // 필수, 인증키(서비스키) "numOfRows": "500", // 한페이지 결과수 "pageNo": "1", // 페이지 번호 @@ -112,8 +111,9 @@ enum KoreaTravelingAPI { "_type": "json", // 응답메세지 형식 : REST방식의 URL호출 시 json값 추가(디폴트 응답메세지 형식은XML) "areaCode": areaCode // 지역코드 ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) case .keywordBasedSearching(let keyword, let areaCode, let sigunguCode): - return [ + let parameters: [String: String] = [ "serviceKey": APIKeys.serviceKey, // 필수, 인증키(서비스키) "numOfRows": "300", // 한페이지 결과수 "pageNo": "1", // 페이지 번호 @@ -126,6 +126,11 @@ enum KoreaTravelingAPI { "areaCode": areaCode, // 지역코드 "sigunguCode": sigunguCode // 시군구 코드(지역코드 필수) ] + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } + + var headers: [String : String]? { + return ["Content-Type": "application/json"] + } } diff --git a/KoreaOneStep/Data/Network/NetworkManager.swift b/KoreaOneStep/Data/Network/NetworkManager.swift new file mode 100644 index 0000000..fcb0175 --- /dev/null +++ b/KoreaOneStep/Data/Network/NetworkManager.swift @@ -0,0 +1,40 @@ +// +// NetworkManager2.swift +// KoreaOneStep +// +// Created by SUCHAN CHANG on 10/28/24. +// + +import Foundation +import Moya +import RxSwift + +protocol NetworkManagerProtocl { + func request(_ target: KoreaTravelingAPI) -> Single +} + +final class NetworkManager: NetworkManagerProtocl { + private let provider: MoyaProvider + + init() { + provider = MoyaProvider() + } + + func request(_ target: KoreaTravelingAPI) -> Single { + return Single.create { [weak self] singleObserver in + guard let self else { return Disposables.create() } + + provider.request(target) { result in + switch result { + case .success(let response): + singleObserver(.success(response)) + case .failure(let moyaError): + print(moyaError) + // TODO: - 네트워크 오류 처리하기 + // singleObserver(.failure(moyaError)) + } + } + return Disposables.create() + } + } +} diff --git a/KoreaOneStep/Data/Repository/KoreaTravelingRepository.swift b/KoreaOneStep/Data/Repository/KoreaTravelingRepository.swift new file mode 100644 index 0000000..8466163 --- /dev/null +++ b/KoreaOneStep/Data/Repository/KoreaTravelingRepository.swift @@ -0,0 +1,61 @@ +// +// KoreaTravelingRepository2.swift +// KoreaOneStep +// +// Created by SUCHAN CHANG on 10/28/24. +// + +import Foundation +import RxMoya +import RxSwift + +protocol KoreaTravelingRepositoryProtocol { + func fetchProvidedImpairmentAidServices(api: KoreaTravelingAPI) -> Single + func fetchTouristDestionationCommonInformation(api: KoreaTravelingAPI) -> Single + func fetchLocationBasedTourismInformation(api: KoreaTravelingAPI) -> Single<[LBItem]> + func fetchAreaCode(api: KoreaTravelingAPI) -> Single<[ACItem]> + func fetchKeywordBasedSearching(api: KoreaTravelingAPI) -> Single<[KSItem]> +} + +final class KoreaTravelingRepository: KoreaTravelingRepositoryProtocol { + private let network: NetworkManagerProtocl + + init(network: NetworkManagerProtocl) { + self.network = network + } + + func fetchProvidedImpairmentAidServices(api: KoreaTravelingAPI) -> Single { + network + .request(api) + .map(ProvidedImpairmentAidServicesDTO.self) + .map { $0.response.body.items.item[0] } + } + + func fetchTouristDestionationCommonInformation(api: KoreaTravelingAPI) -> Single { + network + .request(api) + .map(TouristDestionationCommonInformationDTO.self) + .map { $0.response.body.items.item[0] } + } + + func fetchLocationBasedTourismInformation(api: KoreaTravelingAPI) -> Single<[LBItem]> { + network + .request(api) + .map(LocationBasedTourismInformationDTO.self) + .map { $0.response.body.items.item } + } + + func fetchAreaCode(api: KoreaTravelingAPI) -> Single<[ACItem]> { + network + .request(api) + .map(AreaCodeDTO.self) + .map { $0.response.body.items.item } + } + + func fetchKeywordBasedSearching(api: KoreaTravelingAPI) -> Single<[KSItem]> { + network + .request(api) + .map(KeywordBasedSearchingDTO.self) + .map { $0.response.body.items.item } + } +} diff --git a/KoreaOneStep/Models/Network/AreaCodeModel.swift b/KoreaOneStep/Domain/Entity/AreaCodeDTO.swift similarity index 69% rename from KoreaOneStep/Models/Network/AreaCodeModel.swift rename to KoreaOneStep/Domain/Entity/AreaCodeDTO.swift index ab55320..5eb97a6 100644 --- a/KoreaOneStep/Models/Network/AreaCodeModel.swift +++ b/KoreaOneStep/Domain/Entity/AreaCodeDTO.swift @@ -1,34 +1,34 @@ // -// AreaCodeModel.swift +// AreaCodeDTO.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 3/18/24. +// Created by SUCHAN CHANG on 10/27/24. // import Foundation -struct AreaCodeModel: Decodable { +struct AreaCodeDTO: DTOModelType { let response: ACResponse } -struct ACResponse: Decodable { +struct ACResponse: ResponseType { let header: ACHeader let body: ACBody } -struct ACHeader: Decodable { +struct ACHeader: HeaderType { let resultCode: String let resultMsg: String } -struct ACBody: Decodable { +struct ACBody: BodyType { let items: ACItems let numOfRows: Int let pageNo: Int let totalCount: Int } -struct ACItems: Decodable { +struct ACItems: ItemType { let item: [ACItem] } diff --git a/KoreaOneStep/Models/Network/KeywordBasedSearchgingModel.swift b/KoreaOneStep/Domain/Entity/KeywordBasedSearchingDTO.swift similarity index 81% rename from KoreaOneStep/Models/Network/KeywordBasedSearchgingModel.swift rename to KoreaOneStep/Domain/Entity/KeywordBasedSearchingDTO.swift index 8642d32..7f1de1d 100644 --- a/KoreaOneStep/Models/Network/KeywordBasedSearchgingModel.swift +++ b/KoreaOneStep/Domain/Entity/KeywordBasedSearchingDTO.swift @@ -1,38 +1,37 @@ // -// KeywordBasedSearchgingModel.swift +// KeywordBasedSearchingDTO.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 3/19/24. +// Created by SUCHAN CHANG on 10/27/24. // import Foundation -struct KeywordBasedSearchgingModel: Decodable { +struct KeywordBasedSearchingDTO: DTOModelType { let response: KSResponse } -struct KSResponse: Decodable { +struct KSResponse: ResponseType { let header: KSHeader let body: KSBody } -struct KSHeader: Decodable { +struct KSHeader: HeaderType { let resultCode: String let resultMsg: String } -struct KSBody: Decodable { +struct KSBody: BodyType { let items: KSItems let numOfRows: Int let pageNo: Int let totalCount: Int } -struct KSItems: Decodable { +struct KSItems: ItemType { let item: [KSItem] } -// TODO: - 모델 공통부분 통합 처리 struct KSItem: Decodable { let addr1: String // 주소 let addr2: String // 상세주소 diff --git a/KoreaOneStep/Models/Network/LocationBasedTourismInformationModel.swift b/KoreaOneStep/Domain/Entity/LocationBasedTourismInformationDTO.swift similarity index 76% rename from KoreaOneStep/Models/Network/LocationBasedTourismInformationModel.swift rename to KoreaOneStep/Domain/Entity/LocationBasedTourismInformationDTO.swift index f24143b..82895b5 100644 --- a/KoreaOneStep/Models/Network/LocationBasedTourismInformationModel.swift +++ b/KoreaOneStep/Domain/Entity/LocationBasedTourismInformationDTO.swift @@ -1,34 +1,34 @@ // -// LocationBasedTourismInformationModel.swift +// LocationBasedTourismInformationDTO.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 3/16/24. +// Created by SUCHAN CHANG on 10/27/24. // import Foundation -struct LocationBasedTourismInformationModel: Decodable { +struct LocationBasedTourismInformationDTO: DTOModelType { let response: LBResponse } -struct LBResponse: Decodable { +struct LBResponse: ResponseType { let header: LBHeader let body: LBBody } -struct LBHeader: Decodable { +struct LBHeader: HeaderType { let resultCode: String let resultMsg: String } -struct LBBody: Decodable { +struct LBBody: BodyType { let items: LBItems let numOfRows: Int let pageNo: Int let totalCount: Int } -struct LBItems: Decodable { +struct LBItems: ItemType { let item: [LBItem] } diff --git a/KoreaOneStep/Models/Network/ProvidedImpairmentAidServicesModel.swift b/KoreaOneStep/Domain/Entity/ProvidedImpairmentAidServicesDTO.swift similarity index 86% rename from KoreaOneStep/Models/Network/ProvidedImpairmentAidServicesModel.swift rename to KoreaOneStep/Domain/Entity/ProvidedImpairmentAidServicesDTO.swift index ad2aa6c..3f7e0a0 100644 --- a/KoreaOneStep/Models/Network/ProvidedImpairmentAidServicesModel.swift +++ b/KoreaOneStep/Domain/Entity/ProvidedImpairmentAidServicesDTO.swift @@ -1,34 +1,34 @@ // -// ProvidedImpairmentAidServicesModel.swift +// ProvidedImpairmentAidServicesDTO.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 3/17/24. +// Created by SUCHAN CHANG on 10/27/24. // import Foundation -struct ProvidedImpairmentAidServicesModel: Decodable { +struct ProvidedImpairmentAidServicesDTO: DTOModelType { let response: IASResponse } -struct IASResponse: Decodable { +struct IASResponse: ResponseType { let header: IASHeader let body: IASBody } -struct IASHeader: Decodable { +struct IASHeader: HeaderType { let resultCode: String let resultMsg: String } -struct IASBody: Decodable { +struct IASBody: BodyType { let items: IASItems let numOfRows: Int let pageNo: Int let totalCount: Int } -struct IASItems: Decodable { +struct IASItems: ItemType { let item: [IASItem] } diff --git a/KoreaOneStep/Models/Network/TouristDestionationCommonInformationModel.swift b/KoreaOneStep/Domain/Entity/TouristDestionationCommonInformationDTO.swift similarity index 83% rename from KoreaOneStep/Models/Network/TouristDestionationCommonInformationModel.swift rename to KoreaOneStep/Domain/Entity/TouristDestionationCommonInformationDTO.swift index 4bc5c6e..f2e0c10 100644 --- a/KoreaOneStep/Models/Network/TouristDestionationCommonInformationModel.swift +++ b/KoreaOneStep/Domain/Entity/TouristDestionationCommonInformationDTO.swift @@ -1,34 +1,34 @@ // -// TouristDestionationCommonInformationModel.swift +// TouristDestionationCommonInformationDTO.swift // KoreaOneStep // -// Created by SUCHAN CHANG on 3/17/24. +// Created by SUCHAN CHANG on 10/27/24. // import Foundation -struct TouristDestionationCommonInformationModel: Decodable { +struct TouristDestionationCommonInformationDTO: DTOModelType { let response: CIResponse } -struct CIResponse: Decodable { +struct CIResponse: ResponseType { let header: CIHeader let body: CIBody } -struct CIHeader: Decodable { +struct CIHeader: HeaderType { let resultCode: String let resultMsg: String } -struct CIBody: Decodable { +struct CIBody: BodyType { let items: CIItems let numOfRows: Int let pageNo: Int let totalCount: Int } -struct CIItems: Decodable { +struct CIItems: ItemType { let item: [CIItem] } diff --git a/KoreaOneStep/Domain/Usecase/KoreaTravelingUsecase.swift b/KoreaOneStep/Domain/Usecase/KoreaTravelingUsecase.swift new file mode 100644 index 0000000..24034e1 --- /dev/null +++ b/KoreaOneStep/Domain/Usecase/KoreaTravelingUsecase.swift @@ -0,0 +1,83 @@ +// +// KoreaTravelingUsecase2.swift +// KoreaOneStep +// +// Created by SUCHAN CHANG on 10/28/24. +// + +import Foundation +import RxSwift + +protocol KoreaTravelingUsecaseProtocol { + func fetchProvidedImpairmentAidServices(contentId: String) -> Single + func fetchTouristDestionationCommonInformation(contentId: String, contentTypeId: String) -> Single + func fetchLocationBasedTourismInformation(latitude: Double, longitude: Double, radius: Int, arrange: String, contentTypeId: String) -> Single<[LBItem]> + func fetchAreaCode(areaCode: String) -> Single<[ACItem]> + func fetchKeywordBasedSearching(keyword: String, areaCode: String, sigunguCode: String) -> Single<[KSItem]> +} + +final class KoreaTravelingUsecase: KoreaTravelingUsecaseProtocol { + private let repository: KoreaTravelingRepositoryProtocol + + init(repository: KoreaTravelingRepositoryProtocol) { + self.repository = repository + } + + + func fetchProvidedImpairmentAidServices(contentId: String) -> Single { + repository.fetchProvidedImpairmentAidServices( + api: .providedImpairmentAidServices( + contentId: contentId + ) + ) + } + + func fetchTouristDestionationCommonInformation( + contentId: String, + contentTypeId: String + ) -> Single { + repository.fetchTouristDestionationCommonInformation( + api: .touristDestionationCommonInformation( + contentId: contentId, + contentTypeId: contentTypeId + ) + ) + } + + func fetchLocationBasedTourismInformation( + latitude: Double, + longitude: Double, + radius: Int, + arrange: String, + contentTypeId: String + ) -> Single<[LBItem]> { + repository.fetchLocationBasedTourismInformation( + api: .locationBasedTourismInformation( + latitude: latitude, + longitude: longitude, + radius: radius, + arrange: arrange, + contentTypeId: contentTypeId + ) + ) + } + + func fetchAreaCode(areaCode: String) -> Single<[ACItem]> { + repository.fetchAreaCode(api: .areaCode(areaCode: areaCode)) + } + + func fetchKeywordBasedSearching( + keyword: String, + areaCode: String, + sigunguCode: String + ) -> Single<[KSItem]> { + repository.fetchKeywordBasedSearching( + api: .keywordBasedSearching( + keyword: keyword, + areaCode: areaCode, + sigunguCode: sigunguCode + ) + ) + } +} + diff --git a/KoreaOneStep/Enums/Scenes/Bookmark/BookmarkTableViewSection.swift b/KoreaOneStep/Enums/Scenes/Bookmark/BookmarkTableViewSection.swift deleted file mode 100644 index e651b67..0000000 --- a/KoreaOneStep/Enums/Scenes/Bookmark/BookmarkTableViewSection.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// BookmarkTableViewSection.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import Foundation - -// TODO: - 삭제하기 -enum BookmarkTableViewSection: String, CaseIterable { - case regionCategory = "지역 카테고리" - case bookmark = "북마크" - - static let bookmarkHeaderId = "BookmarkTableViewSectionHeader" -} diff --git a/KoreaOneStep/Enums/Scenes/Detail/DetailTableViewSection.swift b/KoreaOneStep/Enums/Scenes/Detail/DetailTableViewSection.swift deleted file mode 100644 index c1d1b24..0000000 --- a/KoreaOneStep/Enums/Scenes/Detail/DetailTableViewSection.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// DetailTableViewSection.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit - -enum DetailTableViewSection: Int, CaseIterable { - case descriptionSection = 0 - case serviceDetailSection = 1 - - enum DescriptionSection: CaseIterable { - case regionDetailCell - case phoneNumberCell - case addressCell - case serviceProvidedCell - } - - enum ServiceDetailSection: CaseIterable { - case physicalDisability - case visualImpairment - case hearingImpairment - case familiesWithInfantsAndToddlers - case elderlyPeople - - var providedServiceNumber: Int { - switch self { - case .physicalDisability: - return PhysicalDisability.allCases.count - case .visualImpairment: - return VisualImpairment.allCases.count - case .hearingImpairment: - return HearingImpairment.allCases.count - case .familiesWithInfantsAndToddlers: - return FamiliesWithInfantsAndToddlers.allCases.count - case .elderlyPeople: - return ElderlyPeople.allCases.count - } - } - - var serviceImage: UIImage { - switch self { - case .physicalDisability: - return UIImage.physicalDisability - case .visualImpairment: - return UIImage.visualImpairment - case .hearingImpairment: - return UIImage.hearingImpairment - case .familiesWithInfantsAndToddlers: - return UIImage.familiesWithInfantsAndToddlers - case .elderlyPeople: - return UIImage.elderlyPeople - } - } - - var serviceTitleList: [String] { - switch self { - case .physicalDisability: - return PhysicalDisability.allCases.map { $0.rawValue } - case .visualImpairment: - return VisualImpairment.allCases.map { $0.rawValue } - case .hearingImpairment: - return HearingImpairment.allCases.map { $0.rawValue } - case .familiesWithInfantsAndToddlers: - return FamiliesWithInfantsAndToddlers.allCases.map { $0.rawValue } - case .elderlyPeople: - return ElderlyPeople.allCases.map { $0.rawValue } - } - } - - enum PhysicalDisability: String, CaseIterable { - case parkingStatus = "주차여부" - case publicTransport = "대중교통" - case coreMovementLine = "핵심동선" - case ticketBox = "매표소" - case promotionalMaterial = "홍보물" - case wheelchair = "휠체어" - case elevator = "엘리베이터" - case restroom = "화장실" - case seat = "관람석(좌석)" - } - - enum VisualImpairment: String, CaseIterable { - case bailieBlock = "점자블록" - case guide = "안내요원" - case audioGuidance = "음성안내" - case LargePrintOrBraillePromotionalMaterials = "큰활자/점자 홍보물" - case brailleSign = "점자표지판" - case guidanceFacility = "유도안내설비" - } - - enum HearingImpairment: String, CaseIterable { - case signLanguageGuidance = "수어안내" - case subtitle = "자막" - } - - enum FamiliesWithInfantsAndToddlers: String, CaseIterable { - case strollerRent = "유아차 대여" - } - - enum ElderlyPeople: String, CaseIterable { - case wheelChairRent = "휠체어 대여" - case mobilityAidsRent = "이동보조도구 대여" - } - } -} diff --git a/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/ContentTableViewSection.swift b/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/ContentTableViewSection.swift deleted file mode 100644 index c16f42a..0000000 --- a/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/ContentTableViewSection.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// ContentTableViewSection.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/13/24. -// - -import Foundation - -enum ContentTableViewSection: Int, CaseIterable { - case ambientSettings - case searchResultList -} diff --git a/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/FilteringOrder.swift b/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/FilteringOrder.swift deleted file mode 100644 index 3f11b64..0000000 --- a/KoreaOneStep/Enums/Scenes/Main/FloatingPanel/FilteringOrder.swift +++ /dev/null @@ -1,46 +0,0 @@ -// -// FilteringOrder.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/14/24. -// - -import Foundation - -enum FilteringOrder: String, CaseIterable { - case title = "제목순" - case updatedDate = "수정일순" - case createdDate = "생성일순" - case distance = "거리순" - - var sortingCode: String { - switch self { - case .title: - return "O" - case .updatedDate: - return "Q" - case .createdDate: - return "R" - case .distance: - return "S" - } - } - - enum FilteringDistance: Int, CaseIterable { - case oneHundredMeter = 100 - case threeHundredMeters = 300 - case fiveHundredMeters = 500 - case oneKiloMeter = 1000 - case threeKiloMeters = 3000 - case fiveKiloMeters = 5000 - - var getDistanceStringWithUnit: String { - switch self { - case .oneHundredMeter, .threeHundredMeters, .fiveHundredMeters: - return "\(self.rawValue)m" - case .oneKiloMeter, .threeKiloMeters, .fiveKiloMeters: - return "\(self.rawValue/1000)km" - } - } - } -} diff --git a/KoreaOneStep/Enums/Scenes/Main/Search/Filter/FilterTableViewSection.swift b/KoreaOneStep/Enums/Scenes/Main/Search/Filter/FilterTableViewSection.swift deleted file mode 100644 index ac806dd..0000000 --- a/KoreaOneStep/Enums/Scenes/Main/Search/Filter/FilterTableViewSection.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// FilterTableViewSection.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import Foundation - -enum FilterTableViewSection: Int, CaseIterable { - case selectRegion - case selectSiGunGu - - var sectionTitle: String { - switch self { - case .selectRegion: - return "지역 선택" - case .selectSiGunGu: - return "시군구 선택" - } - } -} diff --git a/KoreaOneStep/Enums/Scenes/Main/TourType.swift b/KoreaOneStep/Enums/Scenes/Main/TourType.swift deleted file mode 100644 index ee57510..0000000 --- a/KoreaOneStep/Enums/Scenes/Main/TourType.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// TourType.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/15/24. -// - -import Foundation - -enum TourType: String, CaseIterable { - case touristAttractions = "관광지" - case culturalFacilities = "문화시설" - case festivalsOrConcerts = "축제공연행사" - case travelingCourses = "여행코스" - case leports = "레포츠" - case accommodations = "숙박 시설" - case shopping = "쇼핑" - case restaurants = "음식점" - - var tourTypeCode: String { - switch self { - case .touristAttractions: - return "12" - case .culturalFacilities: - return "14" - case .festivalsOrConcerts: - return "15" - case .travelingCourses: - return "25" - case .leports: - return "28" - case .accommodations: - return "32" - case .shopping: - return "38" - case .restaurants: - return "39" - } - } -} diff --git a/KoreaOneStep/Enums/Scenes/Setting/SettingTableViewCellTitle.swift b/KoreaOneStep/Enums/Scenes/Setting/SettingTableViewCellTitle.swift deleted file mode 100644 index 2e315b3..0000000 --- a/KoreaOneStep/Enums/Scenes/Setting/SettingTableViewCellTitle.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// SettingTableViewCellTitle.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import UIKit - -enum SettingTableViewCellTitle: String, CaseIterable { - case removeAllBookmarkRecords = "북마크 모두 삭제하기" - - var titleColor: UIColor { - switch self { - case .removeAllBookmarkRecords: - return .customRed - } - } -} diff --git a/KoreaOneStep/Enums/Scenes/ToastMessage.swift b/KoreaOneStep/Enums/Scenes/ToastMessage.swift deleted file mode 100644 index 15ef608..0000000 --- a/KoreaOneStep/Enums/Scenes/ToastMessage.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// ToastMessage.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/21/24. -// - -import Foundation - -enum ToastMessage { - enum Success { - static let removeAllBookmarks = "북마크가 성공적으로 모두 삭제되었습니다." - } - - enum Failure { - static let removeAllBookmarks = "북마크 기록 모두 삭제에 실패하였습니다." - static let noBookmarkContents = "삭제할 북마크 기록이 존재하지 않습니다." - static let noSearchingResults = "검색 결과가 존재하지 않습니다." - } -} diff --git a/KoreaOneStep/Extensions/String+Extension.swift b/KoreaOneStep/Extensions/String+Extension.swift deleted file mode 100644 index 49ac44e..0000000 --- a/KoreaOneStep/Extensions/String+Extension.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// String+Extension.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/16/24. -// - -import Foundation - -extension String { - var convertStringToDistanceWithIntType: Int { - return Int(Double(self) ?? 0.0) - } - - var htmlEscaped: String { - guard let encodedData = self.data(using: .utf8) else { - return self - } - - let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [ - .documentType: NSAttributedString.DocumentType.html, - .characterEncoding: String.Encoding.utf8.rawValue - ] - - do { - let attributed = try NSAttributedString(data: encodedData, - options: options, - documentAttributes: nil) - return attributed.string - } catch { - return self - } - } -} diff --git a/KoreaOneStep/Extensions/UICollectionReusableView+Extension.swift b/KoreaOneStep/Extensions/UICollectionReusableView+Extension.swift deleted file mode 100644 index 7184d6f..0000000 --- a/KoreaOneStep/Extensions/UICollectionReusableView+Extension.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// UICollectionReusableView+Extension.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit - -extension UICollectionReusableView: CellIdentifiable { - static var identifier: String { - return String(describing: self) - } -} diff --git a/KoreaOneStep/Extensions/UITableViewCell+Extension.swift b/KoreaOneStep/Extensions/UITableViewCell+Extension.swift deleted file mode 100644 index 638789c..0000000 --- a/KoreaOneStep/Extensions/UITableViewCell+Extension.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// UITableViewCell+Extension.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import UIKit - -extension UITableViewCell: CellIdentifiable { - static var identifier: String { - return String(describing: self) - } -} diff --git a/KoreaOneStep/Extensions/UITableViewHeaderFooterView+Extension.swift b/KoreaOneStep/Extensions/UITableViewHeaderFooterView+Extension.swift deleted file mode 100644 index 365f727..0000000 --- a/KoreaOneStep/Extensions/UITableViewHeaderFooterView+Extension.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// UITableViewHeaderFooterView+Extension.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit - -extension UITableViewHeaderFooterView: CellIdentifiable { - static var identifier: String { - return String(describing: self) - } -} diff --git a/KoreaOneStep/Extensions/UIViewController+Extension.swift b/KoreaOneStep/Extensions/UIViewController+Extension.swift deleted file mode 100644 index 6b31939..0000000 --- a/KoreaOneStep/Extensions/UIViewController+Extension.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// UIViewController+Extension.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/25/24. -// - -import UIKit - -extension UIViewController { - func showLocationSettingAlert() { - let alert = UIAlertController(title: "위치 정보 이용", message: "위치 서비스를 사용할 수 없습니다. 기기 '설정>개인정보 보호'에서 위치 서비스를 켜주세요", preferredStyle: .alert) - - let goSetting = UIAlertAction(title: "설정으로 이동", style: .default) { _ in - if let setting = URL(string: UIApplication.openSettingsURLString) { - UIApplication.shared.open(setting) - } else { - print("설정으로 이동 필요") - } - } - - let cancel = UIAlertAction(title: "취소", style: .cancel) - - alert.addAction(goSetting) - alert.addAction(cancel) - - present(alert, animated: true) - } -} diff --git a/KoreaOneStep/Managers/LocationManager.swift b/KoreaOneStep/Managers/LocationManager.swift deleted file mode 100644 index ed7f2de..0000000 --- a/KoreaOneStep/Managers/LocationManager.swift +++ /dev/null @@ -1,88 +0,0 @@ -// -// LocationManager.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/16/24. -// - -import Foundation -import CoreLocation - -final class LocationManager: CLLocationManager { - - static let shared = LocationManager() - - typealias FetchLocationCompletion = (CLLocationCoordinate2D?, Error?, Bool) -> Void - - private var fetchLocationCompletion: FetchLocationCompletion? - - private override init() { - super.init() - - self.delegate = self - - requestAuthorization() - } - - private func requestAuthorization() { - self.desiredAccuracy = kCLLocationAccuracyBest - self.requestWhenInUseAuthorization() - } -} - -extension LocationManager { - - override func startUpdatingLocation() { - super.startUpdatingLocation() - } - - override func stopUpdatingLocation() { - super.stopUpdatingLocation() - } - - func fetchLocation(completion: @escaping FetchLocationCompletion) { - self.fetchLocationCompletion = completion - } -} - -extension LocationManager: CLLocationManagerDelegate { - func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { - print(locations) - - if let coordinate = locations.last?.coordinate { - fetchLocationCompletion?(coordinate, nil, false) - } - - stopUpdatingLocation() - } - - func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { - fetchLocationCompletion?(nil, error, false) - } - - func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) { - DispatchQueue.global().async { [weak self] in - guard let weakSelf = self else { return } - - if CLLocationManager.locationServicesEnabled() { - DispatchQueue.main.async { - switch manager.authorizationStatus { - case .authorizedAlways, .authorizedWhenInUse: - print("Location Auth: Allow") - weakSelf.startUpdatingLocation() - case .notDetermined: - print("notDetermined") - weakSelf.requestAuthorization() - case .restricted: - print("restricted") - case .denied: - print("denied") - weakSelf.fetchLocationCompletion?(nil, nil, true) - @unknown default: - print("Error") - } - } - } - } - } -} diff --git a/KoreaOneStep/Managers/Network/KoreaTravelingManager.swift b/KoreaOneStep/Managers/Network/KoreaTravelingManager.swift deleted file mode 100644 index 8077551..0000000 --- a/KoreaOneStep/Managers/Network/KoreaTravelingManager.swift +++ /dev/null @@ -1,146 +0,0 @@ -// -// KoreaTravelingManager.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/16/24. -// - -import Foundation -import Alamofire -import RxSwift - -final class KoreaTravelingManager { - static let shared = KoreaTravelingManager() - - private init() {} - - func fetchProvidedImpairmentAidServices( - api: KoreaTravelingAPI, - completionHandler: @escaping (IASItem) -> Void - ) { - - // TODO: - 네트워크 에러 처리하기 - AF.request( - api.endpoint, - method: .get, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: ProvidedImpairmentAidServicesModel.self) { response in - switch response.result { - case .success(let success): - completionHandler(success.response.body.items.item[0]) - case .failure(let failure): - print(failure) - } - } - } - - func fetchTouristDestionationCommonInformation( - api: KoreaTravelingAPI, - completionHandler: @escaping (CIItem) -> Void - ) { - - // TODO: - 네트워크 에러 처리하기 - AF.request( - api.endpoint, - method: api.method, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: TouristDestionationCommonInformationModel.self) { response in - switch response.result { - case .success(let success): - completionHandler(success.response.body.items.item[0]) - case .failure(let failure): - print(failure) - } - } - } - - func fetchLocationBasedTourismInformation( - api: KoreaTravelingAPI, - completionHandler: @escaping ([LBItem]) -> Void - ) { - - // TODO: - 네트워크 에러 처리하기 - AF.request( - api.endpoint, - method: api.method, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: LocationBasedTourismInformationModel.self) { response in - switch response.result { - case .success(let success): - completionHandler(success.response.body.items.item) - case .failure(let failure): - completionHandler([]) - print(failure) - } - } - } - - func fetchAreaCode( - api: KoreaTravelingAPI, - completionHandler: @escaping ([ACItem]) -> Void - ) { - - // TODO: - 네트워크 에러 처리하기 - AF.request( - api.endpoint, - method: api.method, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: AreaCodeModel.self) { response in - switch response.result { - case .success(let success): - completionHandler(success.response.body.items.item) - case .failure(let failure): - print(failure) - } - } - } - - func fetchKeywordBasedSearching( - api: KoreaTravelingAPI, - completionHandler: @escaping ([KSItem]) -> Void - ) { - - // TODO: - 네트워크 에러 처리하기 - AF.request( - api.endpoint, - method: api.method, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: KeywordBasedSearchgingModel.self) { response in - switch response.result { - case .success(let success): - completionHandler(success.response.body.items.item) - case .failure(let failure): - print(failure) - completionHandler([]) - } - } - } - -} - - -extension KoreaTravelingManager { - func fetchLocationBasedTourismInformation(api: KoreaTravelingAPI) -> Single<[LBItem]>{ - return Single<[LBItem]>.create { singleObserver in - AF.request( - api.endpoint, - method: api.method, - parameters: api.parameters, - encoder: URLEncodedFormParameterEncoder(destination: .queryString) - ).responseDecodable(of: LocationBasedTourismInformationModel.self) { response in - switch response.result { - case .success(let success): - singleObserver(.success(success.response.body.items.item)) - case .failure(let failure): - singleObserver(.failure(failure)) - } - } - return Disposables.create() - } - } -} diff --git a/KoreaOneStep/Managers/Realm/RealmManager.swift b/KoreaOneStep/Managers/Realm/RealmManager.swift deleted file mode 100644 index d9101fe..0000000 --- a/KoreaOneStep/Managers/Realm/RealmManager.swift +++ /dev/null @@ -1,68 +0,0 @@ -// -// RealmManager.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/18/24. -// - -import Foundation -import RealmSwift - -final class RealmManager { - static let shared = RealmManager() - - private let realm: Realm - - private init() { - self.realm = try! Realm() - } - - func getLocationOfDefaultRealm() { - print("Realm is located at:", realm.configuration.fileURL!) - } - - func read(_ object: T.Type) -> Results { - return realm.objects(object) - } - - func write(_ object: T) { - do { - try realm.write { - realm.add(object) - print("새로운 북마크가 추가 되었습니다.") - } - } catch { - // TODO: - 에러 처리하기 - print(error) - } - } - - func delete(_ object: T) { - do { - try realm.write { - realm.delete(object) - print("해당 북마크가 정상적으로 삭제되었습니다.") - } - } catch { - // TODO: - 에러 처리하기 - print(error) - } - } - - func deleteAll() -> String { - var toastMessage: String = "" - do { - try realm.write { - realm.deleteAll() - print("북마크가 모두 삭제되었습니다.") - toastMessage = ToastMessage.Success.removeAllBookmarks - } - } catch { - // TODO: - 에러 처리하기 - print(error) - toastMessage = ToastMessage.Failure.removeAllBookmarks - } - - return toastMessage - } -} diff --git a/KoreaOneStep/Models/Realm/Bookmark.swift b/KoreaOneStep/Models/Realm/Bookmark.swift deleted file mode 100644 index e1e6998..0000000 --- a/KoreaOneStep/Models/Realm/Bookmark.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// Bookmark.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/18/24. -// - -import RealmSwift -import Foundation - -final class Bookmark: Object { - @Persisted(primaryKey: true) var contentId: String - @Persisted var contentTypeId: String - @Persisted var title: String - @Persisted var imageURL: String - @Persisted var region: String - @Persisted var regDate: Date - - convenience init(contentId: String, contentTypeId: String, title: String, imageURL: String, region: String) { - self.init() - - self.contentId = contentId - self.contentTypeId = contentTypeId - self.title = title - self.imageURL = imageURL - self.region = region - self.regDate = Date() - } -} diff --git a/KoreaOneStep/Models/Realm/RecentKeyword.swift b/KoreaOneStep/Models/Realm/RecentKeyword.swift deleted file mode 100644 index cb17753..0000000 --- a/KoreaOneStep/Models/Realm/RecentKeyword.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// RecentKeyword.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/20/24. -// - -import Foundation -import RealmSwift - -final class RecentKeyword: Object { - @Persisted(primaryKey: true) var _id: ObjectId - @Persisted var keyword: String - @Persisted var regDate: Date - - convenience init(keyword: String) { - self.init() - - self.keyword = keyword - self.regDate = Date() - } -} diff --git a/KoreaOneStep/Presentation/ViewController.swift b/KoreaOneStep/Presentation/ViewController.swift new file mode 100644 index 0000000..d44ba7f --- /dev/null +++ b/KoreaOneStep/Presentation/ViewController.swift @@ -0,0 +1,109 @@ +// +// ViewController.swift +// KoreaOneStep +// +// Created by SUCHAN CHANG on 10/30/24. +// + +import UIKit +import PinLayout +import SnapKit +import NMapsMap + +final class ViewController: UIViewController { + + private lazy var mapView: NMFMapView = { + let mapView = NMFMapView() + mapView.logoAlign = .leftTop + mapView.positionMode = .direction + return mapView + }() + + private lazy var bottomSheetView: BottomSheetView = { + let view = BottomSheetView() + return view + }() + + override func viewDidLoad() { + super.viewDidLoad() + + view.addSubview(mapView) + view.addSubview(bottomSheetView) + + mapView.pin.all() + bottomSheetView.snp.makeConstraints { + $0.edges.equalToSuperview() + } + } +} + +final class BottomSheetView: UIView { + + final class ContentView: UIView { + + private lazy var lineBarView: UIView = { + let view = UIView() + view.backgroundColor = .lightGray + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + addSubview(lineBarView) + + lineBarView.snp.makeConstraints { + $0.centerX.equalToSuperview() + $0.top.equalToSuperview().inset(8) + $0.width.equalTo(40) + $0.height.equalTo(6) + } + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + } + + private lazy var contentView: ContentView = { + let view = ContentView() + view.backgroundColor = .white + view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] + view.layer.cornerRadius = 24.0 + view.clipsToBounds = true + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + + setLayout() + setupGestures() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setLayout() { + + addSubview(contentView) + + contentView.snp.makeConstraints { + $0.leading.trailing.bottom.equalToSuperview() + $0.height.equalTo(400) + } + } + + private func setupGestures() { + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) + panGesture.delaysTouchesBegan = false + panGesture.delaysTouchesEnded = false + self.addGestureRecognizer(panGesture) + } + + @objc func handlePanGesture(_ gesture: UIPanGestureRecognizer) { + let translation = gesture.translation(in: self) + print("translation", translation.y) + } +} diff --git a/KoreaOneStep/Protocols/CellIdentifiable.swift b/KoreaOneStep/Protocols/CellIdentifiable.swift deleted file mode 100644 index 46f08c4..0000000 --- a/KoreaOneStep/Protocols/CellIdentifiable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// CellIdentifiable.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import Foundation - -protocol CellIdentifiable { - static var identifier: String { get } -} diff --git a/KoreaOneStep/Protocols/UICollectionViewCellConfiguration.swift b/KoreaOneStep/Protocols/UICollectionViewCellConfiguration.swift deleted file mode 100644 index 25570a3..0000000 --- a/KoreaOneStep/Protocols/UICollectionViewCellConfiguration.swift +++ /dev/null @@ -1,16 +0,0 @@ -// -// UICollectionVIewCellConfiguration.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import Foundation - -protocol UICollectionViewCellConfiguration { - associatedtype BindElementType - - func configureConstraints() - func configureUI() - func bind(element: BindElementType) -} diff --git a/KoreaOneStep/Protocols/UICollectionViewConfiguration.swift b/KoreaOneStep/Protocols/UICollectionViewConfiguration.swift deleted file mode 100644 index 62a44ec..0000000 --- a/KoreaOneStep/Protocols/UICollectionViewConfiguration.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// UICollectionViewConfiguration.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit - -protocol UICollectionViewConfiguration { - - func configureCollectionViewLayout() -> UICollectionViewFlowLayout -} diff --git a/KoreaOneStep/Protocols/UITableViewCellConfiguration.swift b/KoreaOneStep/Protocols/UITableViewCellConfiguration.swift deleted file mode 100644 index e5277bd..0000000 --- a/KoreaOneStep/Protocols/UITableViewCellConfiguration.swift +++ /dev/null @@ -1,13 +0,0 @@ -// -// UITableViewCellConfiguration.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import Foundation - -protocol UITableViewCellConfiguration { - func configureConstraints() - func configureUI() -} diff --git a/KoreaOneStep/Protocols/UIViewControllerConfiguration.swift b/KoreaOneStep/Protocols/UIViewControllerConfiguration.swift deleted file mode 100644 index 28f8cc3..0000000 --- a/KoreaOneStep/Protocols/UIViewControllerConfiguration.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// UIViewControllerConfiguration.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/8/24. -// - -import Foundation - -protocol UIViewControllerConfiguration { - func configureNavigationBar() - func configureConstraints() - func configureUI() - func bind() -} diff --git a/KoreaOneStep/Protocols/ViewModelType.swift b/KoreaOneStep/Protocols/ViewModelType.swift deleted file mode 100644 index 9fc4959..0000000 --- a/KoreaOneStep/Protocols/ViewModelType.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// ViewModelType.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 8/17/24. -// - -import Foundation -import RxSwift - -protocol ViewModelType { - associatedtype Input - associatedtype Output - - var disposeBag: DisposeBag { get set } - - func transform(input: Input) -> Output -} diff --git a/KoreaOneStep/Scenes/Bookmark/BookmarkCollectionViewCell.swift b/KoreaOneStep/Scenes/Bookmark/BookmarkCollectionViewCell.swift deleted file mode 100644 index c8e77d9..0000000 --- a/KoreaOneStep/Scenes/Bookmark/BookmarkCollectionViewCell.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// BookmarkSecondSectionTableViewCellCollectionViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit -import SnapKit -import RxSwift -import RxCocoa - -final class BookmarkCollectionViewCell: UICollectionViewCell { - - let thumnailImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "photo") - imageView.contentMode = .scaleToFill - imageView.tintColor = .customDarkGray - imageView.backgroundColor = .lightGray - return imageView - }() - - let nameLabel: UILabel = { - let label = UILabel() - label.text = "우가우가 한식당 한식당" - label.textColor = .customWhite - label.font = .systemFont(ofSize: 20.0, weight: .bold) - label.numberOfLines = 2 - return label - }() - - let bookmarkIconButton: UIButton = { - let button = UIButton() - let buttonImage = UIImage(systemName: "bookmark.fill")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - button.setImage(buttonImage, for: .normal) - let symbolConfig = UIImage.SymbolConfiguration(pointSize: 28) - button.setPreferredSymbolConfiguration(symbolConfig, forImageIn: .normal) - return button - }() - - private lazy var coverView: UIView = { - let view = UIView() - DispatchQueue.main.async { - let gradientLayer = CAGradientLayer() - gradientLayer.frame = view.bounds - gradientLayer.colors = [ - UIColor.customBlack.withAlphaComponent(0.0).cgColor, - UIColor.customBlack.withAlphaComponent(0.6).cgColor, - UIColor.customBlack.withAlphaComponent(0.8).cgColor - ] - gradientLayer.startPoint = CGPoint(x: 0.5, y: 0.0) - gradientLayer.endPoint = CGPoint(x: 0.5, y: 1.0) - gradientLayer.locations = [0.5, 0.7] - view.layer.addSublayer(gradientLayer) - } - return view - }() - - weak var viewModel: BookmarkViewModel? - - private var disposeBag = DisposeBag() - - override init(frame: CGRect) { - super.init(frame: frame) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func layoutSubviews() { - super.layoutSubviews() - - layer.cornerRadius = 16.0 - clipsToBounds = true - } - - override func prepareForReuse() { - super.prepareForReuse() - - disposeBag = DisposeBag() - } -} - -extension BookmarkCollectionViewCell: UICollectionViewCellConfiguration { - func configureConstraints() { - [ - thumnailImageView, - coverView, - nameLabel, - bookmarkIconButton, - ].forEach { contentView.addSubview($0) } - - thumnailImageView.snp.makeConstraints { - $0.edges.equalTo(contentView.safeAreaLayoutGuide) - } - - coverView.snp.makeConstraints { - $0.edges.equalTo(contentView.safeAreaLayoutGuide) - } - - nameLabel.snp.makeConstraints { - $0.horizontalEdges.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - bookmarkIconButton.snp.makeConstraints { - $0.top.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - } - - func configureUI() { - - } - - func bind(element: Bookmark) { - bookmarkIconButton.rx.tap - .bind(with: self) { owner, _ in - guard let viewModel = owner.viewModel else { return } - viewModel.bookmarkIconButtonTapped.onNext(element) - } - .disposed(by: disposeBag) - } -} diff --git a/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderView.swift b/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderView.swift deleted file mode 100644 index 69cd674..0000000 --- a/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderView.swift +++ /dev/null @@ -1,105 +0,0 @@ -// -// BookmarkHeaderView.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit -import SnapKit - -final class BookmarkHeaderView: UICollectionReusableView { - - let headerLabel: UILabel = { - let label = UILabel() - label.text = "지역 카테고리" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 24.0, weight: .bold) - return label - }() - - lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: configureCollectionViewLayout()) - collectionView.dataSource = self - collectionView.delegate = self - - collectionView.register(BookmarkHeaderViewCell.self, forCellWithReuseIdentifier: BookmarkHeaderViewCell.identifier) - - collectionView.showsHorizontalScrollIndicator = false - - return collectionView - }() - - override init(frame: CGRect) { - super.init(frame: frame) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension BookmarkHeaderView: UICollectionViewCellConfiguration { - func configureConstraints() { - [ - headerLabel, - collectionView - ].forEach { addSubview($0) } - - headerLabel.snp.makeConstraints { - $0.top.equalToSuperview().inset(8.0) - $0.horizontalEdges.equalToSuperview().inset(16.0) - } - - collectionView.snp.makeConstraints { - $0.top.equalTo(headerLabel.snp.bottom).offset(8.0) - $0.horizontalEdges.bottom.equalToSuperview() - } - } - - func configureUI() { - - } - - typealias BindElementType = Bookmark - - func bind(element: Bookmark) { - - } -} - -extension BookmarkHeaderView: UICollectionViewConfiguration { - func configureCollectionViewLayout() -> UICollectionViewFlowLayout { - let layout = UICollectionViewFlowLayout() - layout.itemSize = CGSize(width: 100, height: 140) - layout.minimumLineSpacing = 12 - layout.minimumInteritemSpacing = 0 - layout.sectionInset = UIEdgeInsets(top: 0, left: 16, bottom: 0, right: 16) - - layout.scrollDirection = .horizontal - - return layout - } -} - -extension BookmarkHeaderView: UICollectionViewDelegate { - - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - print(indexPath.item) - } -} - -extension BookmarkHeaderView: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return 10 - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BookmarkHeaderViewCell.identifier, for: indexPath) as? BookmarkHeaderViewCell else { return UICollectionViewCell() } - - return cell - } -} diff --git a/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderViewCell.swift b/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderViewCell.swift deleted file mode 100644 index 219908c..0000000 --- a/KoreaOneStep/Scenes/Bookmark/BookmarkFirstSectionTableViewCell/BookmarkHeaderViewCell.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// BookmarkHeaderViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit -import SnapKit - -final class BookmarkHeaderViewCell: UICollectionViewCell { - - let regionImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "photo") - imageView.tintColor = .black - imageView.contentMode = .scaleToFill - imageView.backgroundColor = .brown - - return imageView - }() - - let regionLabel: UILabel = { - let label = UILabel() - label.text = "서울" - label.textAlignment = .center - label.textColor = .customDarkGray - label.font = .systemFont(ofSize: 18.0, weight: .semibold) - return label - }() - - override init(frame: CGRect) { - super.init(frame: frame) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func draw(_ rect: CGRect) { - super.draw(rect) - - regionImageView.clipsToBounds = true - regionImageView.layer.cornerRadius = regionImageView.frame.height / 2 - } -} - -extension BookmarkHeaderViewCell: UICollectionViewCellConfiguration { - func configureConstraints() { - [ - regionImageView, - regionLabel - ].forEach { contentView.addSubview($0) } - - regionImageView.snp.makeConstraints { - $0.size.equalTo(80.0) - $0.centerX.equalTo(contentView.safeAreaLayoutGuide) - } - - regionLabel.snp.makeConstraints { - $0.top.equalTo(regionImageView.snp.bottom).offset(8.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide) - } - } - - func configureUI() { - - } - - typealias BindElementType = Bookmark - - func bind(element: Bookmark) { - - } -} diff --git a/KoreaOneStep/Scenes/Bookmark/BookmarkViewController.swift b/KoreaOneStep/Scenes/Bookmark/BookmarkViewController.swift deleted file mode 100644 index b9007db..0000000 --- a/KoreaOneStep/Scenes/Bookmark/BookmarkViewController.swift +++ /dev/null @@ -1,210 +0,0 @@ -// -// BookmarkViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/8/24. -// - -import UIKit -import SnapKit -import Kingfisher -import RxSwift -import RxCocoa -import RxDataSources - -final class BookmarkViewController: UIViewController { - - lazy var searchBar: UISearchBar = { - let searchBar = UISearchBar() - searchBar.searchBarStyle = .minimal - searchBar.placeholder = "검색어를 입력해주세요" - return searchBar - }() - - lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: configureCollectionViewLayout()) - - collectionView.register(BookmarkHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: BookmarkHeaderView.identifier) - collectionView.register(BookmarkCollectionViewCell.self, forCellWithReuseIdentifier: BookmarkCollectionViewCell.identifier) - - return collectionView - }() - - private let noBookmarkLabel: UILabel = { - let label = UILabel() - label.text = "데이터가 존재하지 않습니다\n북마크를 추가해주세요!!" - label.textColor = .customBlack - label.numberOfLines = 2 - label.font = .boldSystemFont(ofSize: 20) - label.backgroundColor = .customWhite - label.textAlignment = .center - return label - }() - - private let viewModel = BookmarkViewModel() - - private lazy var dataSource = RxCollectionViewSectionedReloadDataSource { [weak self] dataSource, collectionView, indexPath, bookmark in - guard let self else { return UICollectionViewCell() } - - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: BookmarkCollectionViewCell.identifier, for: indexPath) as? BookmarkCollectionViewCell else { return UICollectionViewCell() } - - let buttonImageURL = URL(string: bookmark.imageURL) - let placeholderImage = UIImage(systemName: "photo") - cell.thumnailImageView.kf.setImage(with: buttonImageURL, placeholder: placeholderImage) - cell.nameLabel.text = bookmark.title - - cell.viewModel = viewModel - cell.bind(element: bookmark) - return cell - - } configureSupplementaryView: { dataSource, collectionView, kind, indexPath in - switch kind { - case UICollectionView.elementKindSectionHeader: - guard let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: BookmarkHeaderView.identifier, for: indexPath) as? BookmarkHeaderView else { return UICollectionViewCell() } - return header - default: - return UICollectionReusableView() - } - } - - private let disposeBag = DisposeBag() - - private var bookmarkList: [Bookmark] = [] - - private var isSearchingMode: Bool = false - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - bind() - addUserEvents() - } - - private func addUserEvents() { - let tap = UITapGestureRecognizer(target: self, action: #selector(backgroundViewTapped)) - tap.cancelsTouchesInView = false - view.addGestureRecognizer(tap) - } -} - -extension BookmarkViewController { - @objc func backgroundViewTapped(_ gestureRecognizer: UIGestureRecognizer) { - print("바탕화면 터치됨") - view.endEditing(true) - } -} - -extension BookmarkViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - navigationItem.title = navigationController?.tabBarItem.title - navigationItem.backButtonTitle = "" - } - - func configureConstraints() { - [ - searchBar, - collectionView, - noBookmarkLabel - ].forEach { view.addSubview($0) } - - searchBar.snp.makeConstraints { - $0.top.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - collectionView.snp.makeConstraints { - $0.top.equalTo(searchBar.snp.bottom).offset(16.0) - $0.horizontalEdges.bottom.equalTo(view.safeAreaLayoutGuide) - } - - noBookmarkLabel.snp.makeConstraints{ - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - } - - func bind() { - - let textDidBeginEditing = searchBar.rx.textDidBeginEditing - .withUnretained(self) - .map { owner, _ in - let updatedLayout = owner.configureCollectionViewLayout() - updatedLayout.headerReferenceSize = .zero - owner.collectionView.collectionViewLayout = updatedLayout - } - - let input = BookmarkViewModel.Input( - viewWillAppear: rx.viewWillAppear, - searchText: searchBar.rx.text.orEmpty, - textDidBeginEditing: textDidBeginEditing - ) - - let output = viewModel.transform(input: input) - - output.section - .drive(collectionView.rx.items(dataSource: dataSource)) - .disposed(by: disposeBag) - - output.viewWillAppear - .drive(with: self) { owner, bookmarkCount in - if !owner.isSearchingMode { - if bookmarkCount >= 1 { - owner.noBookmarkLabel.isHidden = true - } else { - owner.noBookmarkLabel.isHidden = false - } - } - } - .disposed(by: disposeBag) - - collectionView.rx.modelSelected(Bookmark.self) - .bind(with: self) { owner, bookmark in - let detailVC = DetailViewController() - - detailVC.isFromBookmarkVC = true - - detailVC.contentTitle = bookmark.title - detailVC.contentId = bookmark.contentId - detailVC.contentTypeId = bookmark.contentTypeId - owner.navigationController?.pushViewController(detailVC, animated: true) - } - .disposed(by: disposeBag) - - searchBar.rx.textDidEndEditing - .bind(with: self) { owner, _ in - owner.isSearchingMode = false - } - .disposed(by: disposeBag) - - searchBar.rx.searchButtonClicked - .bind(with: self) { owner, _ in - owner.view.endEditing(true) - - owner.isSearchingMode = false - } - .disposed(by: disposeBag) - } -} - -extension BookmarkViewController: UICollectionViewConfiguration { - func configureCollectionViewLayout() -> UICollectionViewFlowLayout { - let spacing: CGFloat = 16 - - let layout = UICollectionViewFlowLayout() - let itemSize = UIScreen.main.bounds.width - (spacing * 3) - layout.itemSize = CGSize(width: itemSize / 2, height: (itemSize / 2)) - layout.minimumLineSpacing = spacing - layout.minimumInteritemSpacing = spacing - layout.sectionInset = UIEdgeInsets(top: 0, left: spacing, bottom: 0, right: spacing) - // TODO: - 배포 후, 주석 해제(추가 개발 예정) -// layout.headerReferenceSize = .init(width: view.frame.width, height: 190) - layout.headerReferenceSize = .init(width: view.frame.width, height: .zero) - - return layout - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/AddressTableViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/AddressTableViewCell.swift deleted file mode 100644 index 4761ab7..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/AddressTableViewCell.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// AddressTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -final class AddressTableViewCell: UITableViewCell { - - private let mapIconImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "map") - imageView.tintColor = .customBlack - return imageView - }() - - private let titleLabel: UILabel = { - let label = UILabel() - label.text = "주소" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 18.0, weight: .bold) - return label - }() - - let addressLabel: UILabel = { - let label = UILabel() - label.text = "" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 16.0) - return label - }() - - let chevronRightIconImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "chevron.right") - imageView.tintColor = .customBlack - return imageView - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension AddressTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - mapIconImageView, - titleLabel, - addressLabel, - chevronRightIconImageView - ].forEach { contentView.addSubview($0) } - - mapIconImageView.snp.makeConstraints { - $0.size.equalTo(20.0) - $0.top.leading.equalTo(contentView.safeAreaLayoutGuide).offset(16.0) - } - - titleLabel.snp.makeConstraints { - $0.centerY.equalTo(mapIconImageView) - $0.leading.equalTo(mapIconImageView.snp.trailing).offset(8.0) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - addressLabel.snp.makeConstraints { - $0.top.equalTo(mapIconImageView.snp.bottom).offset(8.0) - $0.leading.equalTo(mapIconImageView) - $0.trailing.equalTo(chevronRightIconImageView.snp.leading).inset(8.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(8.0) - } - - chevronRightIconImageView.snp.makeConstraints { - $0.height.equalTo(22.0) - $0.width.equalTo(13.0) - $0.centerY.equalTo(addressLabel) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - } - - func configureUI() { - - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/PhoneNumberTableViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/PhoneNumberTableViewCell.swift deleted file mode 100644 index 73e2b0e..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/PhoneNumberTableViewCell.swift +++ /dev/null @@ -1,79 +0,0 @@ -// -// PhoneNumberTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -final class PhoneNumberTableViewCell: UITableViewCell { - - private let phoneIconImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "phone") - imageView.tintColor = .customBlack - return imageView - }() - - private let titleLabel: UILabel = { - let label = UILabel() - label.text = "전화번호" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 18.0, weight: .bold) - return label - }() - - let phoneNumberLabel: UILabel = { - let label = UILabel() - label.text = "" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 16.0, weight: .semibold) - return label - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -extension PhoneNumberTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - phoneIconImageView, - titleLabel, - phoneNumberLabel - ].forEach { contentView.addSubview($0) } - - phoneIconImageView.snp.makeConstraints { - $0.size.equalTo(20.0) - $0.top.equalTo(contentView.safeAreaLayoutGuide).offset(8.0) - $0.top.leading.equalTo(contentView.safeAreaLayoutGuide).offset(16.0) - } - - titleLabel.snp.makeConstraints { - $0.centerY.equalTo(phoneIconImageView) - $0.leading.equalTo(phoneIconImageView.snp.trailing).offset(8.0) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - phoneNumberLabel.snp.makeConstraints { - $0.top.equalTo(phoneIconImageView.snp.bottom).offset(8.0) - $0.leading.equalTo(phoneIconImageView) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(8.0) - } - } - - func configureUI() { - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/RegionDetailTableViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/RegionDetailTableViewCell.swift deleted file mode 100644 index 3963191..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/RegionDetailTableViewCell.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// RegionDetailTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -final class RegionDetailTableViewCell: UITableViewCell { - - let regionImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "photo") - imageView.contentMode = .scaleToFill - imageView.tintColor = .customDarkGray - imageView.backgroundColor = .lightGray - return imageView - }() - - let regionNameLabel: UILabel = { - let label = UILabel() - label.text = "" - label.textAlignment = .center - label.textColor = .customBlack - label.font = .systemFont(ofSize: 20.0, weight: .bold) - label.numberOfLines = 2 - return label - }() - - let regionDescriptionLabel: UILabel = { - let label = UILabel() - label.text = "" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 14.0) - label.numberOfLines = 0 - return label - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -extension RegionDetailTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - regionImageView, - regionNameLabel, - regionDescriptionLabel - ].forEach { contentView.addSubview($0) } - - regionImageView.snp.makeConstraints { - $0.top.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide) - $0.height.equalTo(200) - } - - regionNameLabel.snp.makeConstraints { - $0.top.equalTo(regionImageView.snp.bottom).offset(16.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - regionDescriptionLabel.snp.makeConstraints { - $0.top.equalTo(regionNameLabel.snp.bottom).offset(16.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - } - - func configureUI() { - - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedInternalCollectionViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedInternalCollectionViewCell.swift deleted file mode 100644 index 6f2323e..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedInternalCollectionViewCell.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// ServiceProvidedInternalCollectionViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -final class ServiceProvidedInternalCollectionViewCell: UICollectionViewCell { - - let serviceImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "photo") - imageView.tintColor = .customBlack - imageView.contentMode = .scaleAspectFit - return imageView - }() - - override init(frame: CGRect) { - super.init(frame: frame) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension ServiceProvidedInternalCollectionViewCell: UICollectionViewCellConfiguration { - - - func configureConstraints() { - contentView.addSubview(serviceImageView) - - serviceImageView.snp.makeConstraints { - $0.edges.equalTo(contentView.safeAreaLayoutGuide) - } - } - - func configureUI() { - - } - - typealias BindElementType = Bookmark - - func bind(element: Bookmark) { - - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedTableViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedTableViewCell.swift deleted file mode 100644 index d93f244..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvided/ServiceProvidedTableViewCell.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// ServiceProvidedTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -protocol ServiceProvidedTableViewCellDelegate: AnyObject { - func transferSelectedService(selectedService: DetailTableViewSection.ServiceDetailSection) -} - -final class ServiceProvidedTableViewCell: UITableViewCell { - - lazy var collectionView: UICollectionView = { - let collectionView = UICollectionView(frame: .zero, collectionViewLayout: configureCollectionViewLayout()) - - collectionView.delegate = self - collectionView.dataSource = self - - collectionView.register(ServiceProvidedInternalCollectionViewCell.self, forCellWithReuseIdentifier: ServiceProvidedInternalCollectionViewCell.identifier) - - return collectionView - }() - - weak var delegate: ServiceProvidedTableViewCellDelegate? - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension ServiceProvidedTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - contentView.addSubview(collectionView) - - collectionView.snp.makeConstraints { - $0.edges.equalTo(contentView.safeAreaLayoutGuide) - } - } - - func configureUI() { - - } -} - -extension ServiceProvidedTableViewCell: UICollectionViewConfiguration { - func configureCollectionViewLayout() -> UICollectionViewFlowLayout { - let spacing: CGFloat = 16 - - let layout = UICollectionViewFlowLayout() - let itemSize = UIScreen.main.bounds.width - (spacing * 6) - layout.itemSize = CGSize(width: itemSize / 5, height: (itemSize / 5)) - layout.minimumLineSpacing = spacing - layout.minimumInteritemSpacing = spacing - layout.sectionInset = UIEdgeInsets(top: 8, left: spacing, bottom: 0, right: spacing) - - return layout - } -} - -extension ServiceProvidedTableViewCell: UICollectionViewDelegate { - func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let selectedService = DetailTableViewSection.ServiceDetailSection.allCases[indexPath.item] - - delegate?.transferSelectedService(selectedService: selectedService) - } -} - -extension ServiceProvidedTableViewCell: UICollectionViewDataSource { - func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return DetailTableViewSection.ServiceDetailSection.allCases.count - } - - func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: ServiceProvidedInternalCollectionViewCell.identifier, for: indexPath) as? ServiceProvidedInternalCollectionViewCell else { return UICollectionViewCell() } - let service = DetailTableViewSection.ServiceDetailSection.allCases[indexPath.item] - cell.serviceImageView.image = service.serviceImage - return cell - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvidedDetailTableViewCell.swift b/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvidedDetailTableViewCell.swift deleted file mode 100644 index eee56f4..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailTableViewCells/ServiceProvidedDetailTableViewCell.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ServiceProvidedDetailTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/12/24. -// - -import UIKit -import SnapKit - -final class ServiceProvidedDetailTableViewCell: UITableViewCell { - - let serviceTitleLabel: UILabel = { - let label = UILabel() - label.text = "안녕" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 18.0, weight: .bold) - return label - }() - - let chevronImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "chevron.down") - imageView.tintColor = .customBlack - imageView.contentMode = .scaleAspectFit - return imageView - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension ServiceProvidedDetailTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - serviceTitleLabel, - chevronImageView - ].forEach { contentView.addSubview($0) } - - serviceTitleLabel.snp.makeConstraints { - $0.top.leading.equalTo(contentView.safeAreaLayoutGuide).offset(16.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - chevronImageView.snp.makeConstraints { - $0.centerY.equalTo(serviceTitleLabel) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - $0.width.equalTo(19.0) - $0.height.equalTo(22.0) - } - } - - func configureUI() { - - } -} diff --git a/KoreaOneStep/Scenes/Detail/DetailViewController.swift b/KoreaOneStep/Scenes/Detail/DetailViewController.swift deleted file mode 100644 index d1feea7..0000000 --- a/KoreaOneStep/Scenes/Detail/DetailViewController.swift +++ /dev/null @@ -1,337 +0,0 @@ -// -// DetailViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/11/24. -// - -import UIKit -import SnapKit -import Kingfisher - -struct cellData { - var opened: Bool - var sectionData: [String] -} - -final class DetailViewController: UIViewController { - - lazy var tableView: UITableView = { - let tableView = UITableView() - tableView.delegate = self - tableView.dataSource = self - - tableView.register(RegionDetailTableViewCell.self, forCellReuseIdentifier: RegionDetailTableViewCell.identifier) - tableView.register(PhoneNumberTableViewCell.self, forCellReuseIdentifier: PhoneNumberTableViewCell.identifier) - tableView.register(AddressTableViewCell.self, forCellReuseIdentifier: AddressTableViewCell.identifier) - tableView.register(ServiceProvidedTableViewCell.self, forCellReuseIdentifier: ServiceProvidedTableViewCell.identifier) - tableView.register(ServiceProvidedDetailTableViewCell.self, forCellReuseIdentifier: ServiceProvidedDetailTableViewCell.identifier) - tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) - - tableView.separatorStyle = .none - tableView.showsVerticalScrollIndicator = false - - tableView.isHidden = true - - return tableView - }() - - var contentTitle: String? - var contentId: String? - var contentTypeId: String? - - var isFromBookmarkVC: Bool = false - - private var viewModel = DetailViewModel() - var mainViewModel: MainViewModel? - - private var selectedService: DetailTableViewSection.ServiceDetailSection = .physicalDisability - - private var tableViewData = [cellData]() - - private var touristDestinationCommonInfo: CIItem? - - private var providedImpairmentAidServiceList: Dictionary = [:] - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - bind() - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - viewModel.inputIsBookmarked.value = contentId - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - if isFromBookmarkVC && touristDestinationCommonInfo == nil { - view.makeToastActivity(.center) - } - } - - private func updateTableViewData() { - if let providedImpairmentAidServiceDescriptionList = providedImpairmentAidServiceList[selectedService] { - tableViewData = selectedService.serviceTitleList.enumerated().map { index, _ in - cellData(opened: false, sectionData: [providedImpairmentAidServiceDescriptionList[index]]) - } - } else { - tableViewData = selectedService.serviceTitleList.map { _ in - cellData(opened: false, sectionData: ["없음"]) - } - } - } -} - -extension DetailViewController { - @objc func leftBarButtonItemTapped() { - if isFromBookmarkVC { - navigationController?.popViewController(animated: true) - return - } - - guard let mainViewModel = mainViewModel else { return } - mainViewModel.inputDetailVCLeftBarButtonItemTappedTrigger.value = () - - navigationController?.popViewController(animated: true) - } - - @objc func bookmarkRightBarButtonItemTapped() { - guard let touristDestinationCommonInfo = touristDestinationCommonInfo else { return } - - viewModel.inputBookmarkButtonTrigger.value = ( - contentId, - contentTypeId, - touristDestinationCommonInfo.title, - touristDestinationCommonInfo.firstimage, - touristDestinationCommonInfo.areacode - ) - } - - @objc func shareRightBarButtonItemTapped() { - - } -} - -extension DetailViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - navigationController?.navigationBar.isHidden = false - - navigationItem.title = contentTitle - - navigationController?.navigationBar.tintColor = .customBlack - - let leftBarButtonImage = UIImage(systemName: "chevron.left")?.withTintColor(.customBlack, renderingMode: .alwaysOriginal) - navigationItem.leftBarButtonItem = UIBarButtonItem(image: leftBarButtonImage, style: .plain, target: self, action: #selector(leftBarButtonItemTapped)) - - let bookmarkImage = UIImage(systemName: "bookmark")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - let bookmarkRightBarButtonItem = UIBarButtonItem(image: bookmarkImage, style: .plain, target: self, action: #selector(bookmarkRightBarButtonItemTapped)) - - let shareImage = UIImage(systemName: "square.and.arrow.up")?.withTintColor(.customBlack, renderingMode: .alwaysOriginal) - let shareImageRightBarButtonItem = UIBarButtonItem(image: shareImage, style: .plain, target: self, action: #selector(shareRightBarButtonItemTapped)) - - // TODO: - 배포 후, 주석 해제(추가 개발 예정) -// navigationItem.rightBarButtonItems = [shareImageRightBarButtonItem, bookmarkRightBarButtonItem] - navigationItem.rightBarButtonItems = [bookmarkRightBarButtonItem] - } - - func configureConstraints() { - view.addSubview(tableView) - - tableView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - } - - func bind() { - if isFromBookmarkVC { - viewModel.inputAcitivityIndicatorStartTrigger.value = () - } - - viewModel.inputViewDidLoadTrigger.value = (self.contentId, self.contentTypeId) - viewModel.inputIsBookmarked.value = self.contentId - - viewModel.outputDetailTableViewData.bind { [weak self] - touristDestinationCommonInfo, dictionary in - guard let weakSelf = self else { return } - - weakSelf.touristDestinationCommonInfo = touristDestinationCommonInfo - - weakSelf.providedImpairmentAidServiceList = dictionary - weakSelf.updateTableViewData() - weakSelf.tableView.reloadData() - - if touristDestinationCommonInfo != nil { - weakSelf.tableView.isHidden = false - } - - if weakSelf.isFromBookmarkVC { - weakSelf.view.hideToastActivity() - } else { - guard - let _ = touristDestinationCommonInfo else { return } - guard let mainViewModel = weakSelf.mainViewModel else { return } - mainViewModel.outputActivityIndicatorStopTrigger.value = () - } - } - - viewModel.outputIsBookmarked.bind { [weak self] isBookmarked in - guard let weakSelf = self else { return } - - if isBookmarked { - weakSelf.navigationItem.rightBarButtonItems?[0].image = UIImage(systemName: "bookmark.fill")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - } else { - weakSelf.navigationItem.rightBarButtonItems?[0].image = UIImage(systemName: "bookmark")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - } - } - - viewModel.outputAcitivityIndicatorStartTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.view.makeToastActivity(.center) - } - - guard let mainViewModel = mainViewModel else { return } - mainViewModel.inputDetailVCViewDidLoadTrigger.value = () - } -} - -extension DetailViewController: UITableViewDelegate { - - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if indexPath.section == DetailTableViewSection.descriptionSection.rawValue { - if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .addressCell { - if !isFromBookmarkVC { - guard - let mainViewModel = mainViewModel, - let touristDestinationCommonInfo = touristDestinationCommonInfo - else { return } - - guard - let latitude = Double(touristDestinationCommonInfo.mapy), - let longitude = Double(touristDestinationCommonInfo.mapx) - else { return } - - mainViewModel.inputDetailVCAddressCellTappTrigger.value = (latitude, longitude) - } - } - } else { - tableView.deselectRow(at: indexPath, animated: true) - - if indexPath.row == 0 { - tableViewData[indexPath.section - 1].opened = !tableViewData[indexPath.section - 1].opened - - tableView.reloadSections([indexPath.section], with: .none) - - if indexPath.section == selectedService.providedServiceNumber && tableViewData[selectedService.providedServiceNumber - 1].opened { - tableView.scrollToRow(at: IndexPath(row: 1, section: selectedService.providedServiceNumber), at: .bottom, animated: true) - } - } - } - } - - func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - if indexPath.section == DetailTableViewSection.descriptionSection.rawValue { - if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .serviceProvidedCell { - return 80 - } - return UITableView.automaticDimension - } else { - return UITableView.automaticDimension - } - } -} - -extension DetailViewController: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - return DetailTableViewSection.allCases.count - 1 + selectedService.providedServiceNumber - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if section == DetailTableViewSection.descriptionSection.rawValue { - return DetailTableViewSection.DescriptionSection.allCases.count - } else { - if tableViewData[section - 1].opened { - return tableViewData[section - 1].sectionData.count + 1 - } else { - return 1 - } - } - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - if indexPath.section == DetailTableViewSection.descriptionSection.rawValue { - if let touristDestinationCommonInfo = touristDestinationCommonInfo { - if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .regionDetailCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: RegionDetailTableViewCell.identifier, for: indexPath) as? RegionDetailTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - - let regionImageURL = URL(string: touristDestinationCommonInfo.firstimage) - let placeHolderImage = UIImage(systemName: "photo") - cell.regionImageView.kf.setImage(with: regionImageURL, placeholder: placeHolderImage) - cell.regionNameLabel.text = touristDestinationCommonInfo.title - cell.regionDescriptionLabel.text = touristDestinationCommonInfo.overview.htmlEscaped - return cell - } else if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .phoneNumberCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: PhoneNumberTableViewCell.identifier, for: indexPath) as? PhoneNumberTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - - cell.phoneNumberLabel.text = touristDestinationCommonInfo.tel == "" ? "없음" : touristDestinationCommonInfo.tel - return cell - } else if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .addressCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AddressTableViewCell.identifier, for: indexPath) as? AddressTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - - cell.addressLabel.text = touristDestinationCommonInfo.addr1 == "" ? "없음" : touristDestinationCommonInfo.addr1 - cell.chevronRightIconImageView.isHidden = isFromBookmarkVC - return cell - } else if DetailTableViewSection.DescriptionSection.allCases[indexPath.row] == .serviceProvidedCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: ServiceProvidedTableViewCell.identifier, for: indexPath) as? ServiceProvidedTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - cell.delegate = self - return cell - } - } - } else { - if indexPath.row == 0 { - guard let cell = tableView.dequeueReusableCell(withIdentifier: ServiceProvidedDetailTableViewCell.identifier, for: indexPath) as? ServiceProvidedDetailTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - cell.serviceTitleLabel.text = selectedService.serviceTitleList[indexPath.section - 1] - if tableViewData[indexPath.section - 1].opened { - cell.chevronImageView.image = UIImage(systemName: "chevron.up") - } else { - cell.chevronImageView.image = UIImage(systemName: "chevron.down") - } - return cell - } else { - guard let cell = tableView.dequeueReusableCell(withIdentifier: UITableViewCell.identifier) else { return UITableViewCell() } - let providedImpairmentAidServiceDescription = tableViewData[indexPath.section - 1].sectionData[indexPath.row - 1] - cell.textLabel?.text = providedImpairmentAidServiceDescription == "" ? "없음" : providedImpairmentAidServiceDescription - cell.textLabel?.numberOfLines = 0 - return cell - } - } - return UITableViewCell() - } -} - -extension DetailViewController: ServiceProvidedTableViewCellDelegate { - func transferSelectedService(selectedService: DetailTableViewSection.ServiceDetailSection) { - self.selectedService = selectedService - updateTableViewData() - tableView.reloadData() - tableView.scrollToRow(at: IndexPath(row: 0, section: selectedService.providedServiceNumber), at: .bottom, animated: true) - } -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/AccordionTableViewCell.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/AccordionTableViewCell.swift deleted file mode 100644 index 1e618a1..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/AccordionTableViewCell.swift +++ /dev/null @@ -1,75 +0,0 @@ -// -// AccordionTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/13/24. -// - -import UIKit -import SnapKit - -final class AccordionTableViewCell: UITableViewCell { - - let filteringDistanceStackView = FilteringDistanceStackView() - - let ambienDistanceSliderView: UISlider = { - let slider = UISlider() - slider.minimumValue = 0.0 - slider.maximumValue = 10.0 - slider.value = 6.0 - return slider - }() - - let filteringCategoryStackView = FilteringCategoryStackView() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension AccordionTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - filteringDistanceStackView, - ambienDistanceSliderView, - filteringCategoryStackView - ].forEach { contentView.addSubview($0) } - - filteringDistanceStackView.snp.makeConstraints { - $0.top.equalTo(contentView.safeAreaLayoutGuide).offset(16.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - ambienDistanceSliderView.snp.makeConstraints { - $0.top.equalTo(filteringDistanceStackView.snp.bottom).offset(8.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - filteringCategoryStackView.snp.makeConstraints { - $0.top.equalTo(ambienDistanceSliderView.snp.bottom).offset(8.0) - $0.leading.equalTo(filteringDistanceStackView) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide) - } - } - - func configureUI() { - backgroundColor = .customWhite - contentView.backgroundColor = .customWhite - - // TODO: - 리팩토링(UIViewController Extension으로 이동) - guard let filteringCategoryButton = filteringCategoryStackView.arrangedSubviews[0] as? UIButton else { return } - guard let buttonTitle = filteringCategoryButton.title(for: .normal) else { return } - let attributedString = NSMutableAttributedString(string: buttonTitle) - attributedString.addAttribute(.foregroundColor, value: UIColor.customLightBlue, range: ((buttonTitle) as NSString).range(of: "•")) - let filteringCategoryName = buttonTitle.components(separatedBy: " • ")[1] - attributedString.addAttribute(.foregroundColor, value: UIColor.customBlack, range: ((buttonTitle) as NSString).range(of: filteringCategoryName)) - filteringCategoryButton.setAttributedTitle(attributedString, for: .normal) - } -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringCategoryStackView.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringCategoryStackView.swift deleted file mode 100644 index fc9e85c..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringCategoryStackView.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// FilteringCategoryStackView.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/14/24. -// - -import UIKit - -final class FilteringCategoryStackView: UIStackView { - - private let filteringCategoryList = FilteringOrder.allCases - - override init(frame: CGRect) { - super.init(frame: frame) - - configureUI() - } - - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func configureUI() { - axis = .horizontal - distribution = .equalSpacing - alignment = .center - spacing = 8.0 - filteringCategoryList.forEach { addArrangedSubview(generateButton(with: $0.rawValue)) } - } - - private func generateButton(with title: String) -> UIButton { - let button = UIButton() - button.setTitle(" • \(title)", for: .normal) - button.setTitleColor(.customDarkGray, for: .normal) - button.titleLabel?.font = .systemFont(ofSize: 16.0) - return button - } - -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringDistanceStackView.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringDistanceStackView.swift deleted file mode 100644 index 1ef702f..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AccordionTableViewCell/SubViews/FilteringDistanceStackView.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// FilteringDistanceStackView.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/14/24. -// - -import UIKit - -final class FilteringDistanceStackView: UIStackView { - - private let searchDistance = FilteringOrder.FilteringDistance.allCases - - override init(frame: CGRect) { - super.init(frame: frame) - - configureUI() - } - - required init(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func configureUI() { - axis = .horizontal - distribution = .equalCentering - alignment = .center - - searchDistance.forEach { addArrangedSubview(labelGenerator(text: $0.getDistanceStringWithUnit)) } - } - - private func labelGenerator(text: String) -> UILabel { - let label = UILabel() - label.text = text - label.textColor = .customBlack - label.font = .systemFont(ofSize: 14.0) - return label - } -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AmbientSettingsTableViewCell.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AmbientSettingsTableViewCell.swift deleted file mode 100644 index 51ed7d3..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/AmbientSettings/AmbientSettingsTableViewCell.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// AmbientSettingsTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/13/24. -// - -import UIKit -import SnapKit - -final class AmbientSettingsTableViewCell: UITableViewCell { - - let ambientLabel: UILabel = { - let label = UILabel() - label.text = "주변" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 18.0, weight: .bold) - return label - }() - - let settingButton: UIButton = { - let button = UIButton() - button.setTitle("\(FilteringOrder.FilteringDistance.allCases[3].rawValue) 반경 \(FilteringOrder.allCases[0].rawValue)", for: .normal) - button.titleLabel?.font = .systemFont(ofSize: 16.0) - button.setTitleColor(.customBlack, for: .normal) - return button - }() - - let chevronImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "chevron.down") - imageView.tintColor = .customBlack - imageView.contentMode = .scaleAspectFit - return imageView - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension AmbientSettingsTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - ambientLabel, - settingButton, - chevronImageView - ].forEach { contentView.addSubview($0) } - - ambientLabel.snp.makeConstraints { - $0.top.equalTo(contentView.safeAreaLayoutGuide).offset(8.0) - $0.leading.equalTo(contentView.safeAreaLayoutGuide).offset(14.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide) - } - - settingButton.snp.makeConstraints { - $0.centerY.equalTo(chevronImageView) - $0.trailing.equalTo(chevronImageView.snp.leading) - } - - chevronImageView.snp.makeConstraints { - $0.size.equalTo(14.0) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - $0.centerY.equalTo(ambientLabel) - } - } - - func configureUI() { - backgroundColor = .customWhite - contentView.backgroundColor = .customWhite - } - - func setSettingButtonAttributedTitle(_ selectedFilteringDistance: FilteringOrder.FilteringDistance, _ selectedFilteringCategory: FilteringOrder) { - guard let buttonTitle = settingButton.title(for: .normal) else { return } - let attributedString = NSMutableAttributedString(string: buttonTitle) - attributedString.addAttribute(.foregroundColor, value: UIColor.customLightBlue, range: ((buttonTitle) as NSString).range(of: selectedFilteringDistance.getDistanceStringWithUnit)) - attributedString.addAttribute(.foregroundColor, value: UIColor.customLightBlue, range: ((buttonTitle) as NSString).range(of: selectedFilteringCategory.rawValue)) - settingButton.setAttributedTitle(attributedString, for: .normal) - } -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/ContentViewController.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/ContentViewController.swift deleted file mode 100644 index f02aec2..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/ContentViewController.swift +++ /dev/null @@ -1,322 +0,0 @@ -// -// ContentViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/13/24. -// - -import UIKit -import SnapKit -import Kingfisher -import CoreLocation -import RxSwift -import RxCocoa - -final class ContentViewController: UIViewController { - - lazy var tableView: UITableView = { - let tableView = UITableView() - - tableView.register(AmbientSettingsTableViewCell.self, forCellReuseIdentifier: AmbientSettingsTableViewCell.identifier) - tableView.register(AccordionTableViewCell.self, forCellReuseIdentifier: AccordionTableViewCell.identifier) - tableView.register(SearchResultListTableViewCell.self, forCellReuseIdentifier: SearchResultListTableViewCell.identifier) - - tableView.separatorStyle = .none - - tableView.showsVerticalScrollIndicator = true - - return tableView - }() - - let noContentLabel: UILabel = { - let label = UILabel() - label.text = "검색결과가 존재하지 않습니다." - label.textAlignment = .center - label.textColor = .customBlack - label.font = .boldSystemFont(ofSize: 20.0) - label.isHidden = true - return label - }() - - private var isAmbientSettingsSectionOpened = false - - private var filteringButtonList: [UIButton] = [] - - private var mainViewModel: MainViewModel - private var mainViewModel2: MainViewModel2 - - private let disposeBag = DisposeBag() - - var selectedFilteringDistance: FilteringOrder.FilteringDistance = FilteringOrder.FilteringDistance.allCases[3] - var selectedFilteringCategory: FilteringOrder = FilteringOrder.allCases[0] - var userLocationInfo: CLLocationCoordinate2D? - var selectedTourType: TourType? - - init( - mainViewModel: MainViewModel, - mainViewModel2: MainViewModel2 - ) { - self.mainViewModel = mainViewModel - self.mainViewModel2 = mainViewModel2 - - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - bind() - } - - private func selectFilteringDistance(slider: UISlider) -> FilteringOrder.FilteringDistance { - let value = slider.value - - if value <= 1.0 { - slider.value = 0.0 - return FilteringOrder.FilteringDistance.allCases[0] - } else if value > 1.0 && value <= 3.0 { - slider.value = 2.0 - return FilteringOrder.FilteringDistance.allCases[1] - } else if value > 3.0 && value <= 5.0 { - slider.value = 4.0 - return FilteringOrder.FilteringDistance.allCases[2] - } else if value > 5.0 && value <= 7.0 { - slider.value = 6.0 - return FilteringOrder.FilteringDistance.allCases[3] - } else if value > 7.0 && value <= 9.0 { - slider.value = 8.0 - return FilteringOrder.FilteringDistance.allCases[4] - } else { - slider.value = 10.0 - return FilteringOrder.FilteringDistance.allCases[5] - } - } -} - -extension ContentViewController { - @objc func settingButtonTapped(_ button: UIButton) { - isAmbientSettingsSectionOpened = !isAmbientSettingsSectionOpened - - tableView.reloadSections([ContentTableViewSection.ambientSettings.rawValue], with: .none) - } - - @objc func sliderValueChanged(_ slider: UISlider) { - self.selectedFilteringDistance = selectFilteringDistance(slider: slider) - - print(slider.value) - - tableView.reloadRows(at: [IndexPath(row: 0, section: ContentTableViewSection.allCases[0].rawValue)], with: .none) - - mainViewModel.inputActivityIndicatorStartTrigger.value = () - mainViewModel.inputForTableViewUpdate.value = (self.userLocationInfo, self.selectedFilteringDistance, self.selectedFilteringCategory) - } - - @objc func filteringCategoryTapped(_ button: UIButton) { - filteringButtonList.forEach { - if $0 == button { - // TODO: - 리팩토링하기(UIViewController Extension으로 이동) - guard let buttonTitle = button.title(for: .normal) else { return } - let attributedString = NSMutableAttributedString(string: buttonTitle) - attributedString.addAttribute(.foregroundColor, value: UIColor.customLightBlue, range: ((buttonTitle) as NSString).range(of: "•")) - let filteringCategoryName = buttonTitle.components(separatedBy: " • ")[1] - attributedString.addAttribute(.foregroundColor, value: UIColor.customBlack, range: ((buttonTitle) as NSString).range(of: filteringCategoryName)) - button.setAttributedTitle(attributedString, for: .normal) - - if let selectedFilteringCategory = FilteringOrder(rawValue: filteringCategoryName) { - self.selectedFilteringCategory = selectedFilteringCategory - } - } else { - guard let buttonTitle = $0.title(for: .normal) else { return } - let attributedString = NSMutableAttributedString(string: buttonTitle) - attributedString.addAttribute(.foregroundColor, value: UIColor.customDarkGray, range: ((buttonTitle) as NSString).range(of: buttonTitle)) - $0.setAttributedTitle(attributedString, for: .normal) - } - } - - tableView.reloadRows(at: [IndexPath(row: 0, section: ContentTableViewSection.ambientSettings.rawValue)], with: .none) - - mainViewModel.inputActivityIndicatorStartTrigger.value = () - mainViewModel.inputForTableViewUpdate.value = (self.userLocationInfo, self.selectedFilteringDistance, self.selectedFilteringCategory) - } - - @objc func bookmarkIconButtonTapped(_ button: UIButton) { - guard let cell = tableView.cellForRow(at: IndexPath(row: button.tag, section: ContentTableViewSection.searchResultList.rawValue)) as? SearchResultListTableViewCell else { return } - - let locationBasedTouristDestination = mainViewModel2.locationBasedTouristDestinationListRelay.value[button.tag].locationBasedTouristDestination - - if cell.isBookmarked { - mainViewModel.inputRemoveBookmark.value = locationBasedTouristDestination - } else { - mainViewModel.inputAddNewBookmark.value = locationBasedTouristDestination - } - - cell.isBookmarked.toggle() - } -} - -extension ContentViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - - navigationItem.backButtonTitle = "" - } - - func configureConstraints() { - [ - tableView, - noContentLabel - ].forEach { view.addSubview($0) } - - tableView.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide).offset(20.0) - $0.bottom.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - noContentLabel.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide).offset(220.0) - $0.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - // TODO: - Floating Panel Top 부분 둥글게 만들기 - } - - func bind() { - - rx.viewWillAppear - .bind(with: self) { owner, _ in - owner.navigationController?.navigationBar.isHidden = true - - owner.mainViewModel2.indicatorTriggerRelay.accept(true) - - owner.mainViewModel.inputForTableViewUpdate.value = (self.userLocationInfo, self.selectedFilteringDistance, self.selectedFilteringCategory) - } - .disposed(by: disposeBag) - - tableView.rx.setDelegate(self) - .disposed(by: disposeBag) - - tableView.rx.setDataSource(self) - .disposed(by: disposeBag) - - mainViewModel2 - .locationBasedTouristDestinationListRelay.asDriver() - .drive(with: self) { owner, locationBasedTouristDestinationList in - owner.tableView.reloadSections([ContentTableViewSection.searchResultList.rawValue], with: .none) - - owner.mainViewModel2.indicatorTriggerRelay.accept(false) - - if locationBasedTouristDestinationList.count < 1 { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { - owner.noContentLabel.isHidden = false - } - } else { - owner.noContentLabel.isHidden = true - } - } - .disposed(by: disposeBag) - - mainViewModel2.userLocationInfoRelay.asDriver() - .drive(with: self) { owner, coordinate in - owner.userLocationInfo = coordinate - } - .disposed(by: disposeBag) - } -} - -extension ContentViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if ContentTableViewSection.allCases[indexPath.section] == .searchResultList { - let touristDestination = mainViewModel2.locationBasedTouristDestinationListRelay.value[indexPath.row].locationBasedTouristDestination - - mainViewModel.inputContentVCTableViewDidSelectRowAtTrigger.value = touristDestination - - let detailVC = DetailViewController() - - detailVC.isFromBookmarkVC = false - detailVC.mainViewModel = mainViewModel - - detailVC.contentTitle = touristDestination.title - detailVC.contentId = touristDestination.contentid - detailVC.contentTypeId = touristDestination.contenttypeid - navigationController?.pushViewController(detailVC, animated: true) - - mainViewModel.inputActivityIndicatorStartTrigger.value = () - } - } -} - -extension ContentViewController: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - return ContentTableViewSection.allCases.count - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - - if ContentTableViewSection.allCases[section] == .ambientSettings { - if isAmbientSettingsSectionOpened { - return 1 + 1 - } else { - return 1 - } - } - return mainViewModel2.locationBasedTouristDestinationListRelay.value.count - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - if ContentTableViewSection.allCases[indexPath.section] == .ambientSettings { - if indexPath.row == 0 { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AmbientSettingsTableViewCell.identifier) as? AmbientSettingsTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - cell.settingButton.addTarget(self, action: #selector(settingButtonTapped), for: .touchUpInside) - let title = "\(selectedFilteringDistance.getDistanceStringWithUnit) 반경 \(selectedFilteringCategory.rawValue)" - cell.settingButton.setTitle(title, for: .normal) - cell.setSettingButtonAttributedTitle(selectedFilteringDistance, selectedFilteringCategory) - if isAmbientSettingsSectionOpened { - cell.chevronImageView.image = UIImage(systemName: "chevron.up") - } else { - cell.chevronImageView.image = UIImage(systemName: "chevron.down") - } - return cell - } else { - guard let cell = tableView.dequeueReusableCell(withIdentifier: AccordionTableViewCell.identifier) as? AccordionTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - cell.ambienDistanceSliderView.addTarget(self, action: #selector(sliderValueChanged), for: .touchUpInside) - cell.filteringCategoryStackView.arrangedSubviews.forEach { - guard let filteringButton = $0 as? UIButton else { return } - filteringButton.addTarget(self, action: #selector(filteringCategoryTapped), for: .touchUpInside) - filteringButtonList.append(filteringButton) - } - return cell - } - } - guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchResultListTableViewCell.identifier) as? SearchResultListTableViewCell else { return UITableViewCell() } - cell.selectionStyle = .none - - cell.bookmarkIconButton.addTarget(self, action: #selector(bookmarkIconButtonTapped), for: .touchUpInside) - cell.bookmarkIconButton.tag = indexPath.row - - let locationBasedTouristDestination = mainViewModel2.locationBasedTouristDestinationListRelay.value[indexPath.row].locationBasedTouristDestination - - let isBookmarked = mainViewModel2.locationBasedTouristDestinationListRelay.value[indexPath.row].isBookmarked - - let regionImageURL = URL(string: locationBasedTouristDestination.firstimage) - let placeholderImage = UIImage(systemName: "photo") - cell.regionImageView.kf.setImage(with: regionImageURL, placeholder: placeholderImage) - cell.regionNameLabel.text = locationBasedTouristDestination.title - cell.distanceLabel.text = "\(locationBasedTouristDestination.dist.convertStringToDistanceWithIntType)m" - cell.telephoneNumberLabel.text = locationBasedTouristDestination.tel == "" ? "전화번호 미기재" : locationBasedTouristDestination.tel - - cell.isBookmarked = isBookmarked - - return cell - } -} diff --git a/KoreaOneStep/Scenes/Main/FloatingPanel/SearchList/SearchResultListTableViewCell.swift b/KoreaOneStep/Scenes/Main/FloatingPanel/SearchList/SearchResultListTableViewCell.swift deleted file mode 100644 index 89d8a09..0000000 --- a/KoreaOneStep/Scenes/Main/FloatingPanel/SearchList/SearchResultListTableViewCell.swift +++ /dev/null @@ -1,127 +0,0 @@ -// -// SearchResultListTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/13/24. -// - -import UIKit -import SnapKit - -final class SearchResultListTableViewCell: UITableViewCell { - - let regionImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "photo") - imageView.contentMode = .scaleToFill - imageView.tintColor = .customDarkGray - imageView.backgroundColor = .lightGray - return imageView - }() - - let regionNameLabel: UILabel = { - let label = UILabel() - label.text = "우가우가 한식당" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 20.0, weight: .bold) - return label - }() - - let bookmarkIconButton: UIButton = { - let button = UIButton() - let buttonImage = UIImage(systemName: "bookmark")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - button.setImage(buttonImage, for: .normal) - let symbolConfig = UIImage.SymbolConfiguration(pointSize: 20) - button.setPreferredSymbolConfiguration(symbolConfig, forImageIn: .normal) - return button - }() - - let distanceLabel: UILabel = { - let label = UILabel() - // TODO: - 거리에 따른 다른 단위 보여주기 - // 예) 1000m 이상일 때는 ~km로 단위 변경하여 표시 - label.text = "997m" - label.textColor = .customOrange - label.font = .systemFont(ofSize: 16.0, weight: .semibold) - return label - }() - - let telephoneNumberLabel: UILabel = { - let label = UILabel() - label.text = "02-752-1945" - label.textColor = .customBlack - label.font = .systemFont(ofSize: 16.0, weight: .semibold) - return label - }() - - lazy var isBookmarked: Bool = false { - didSet { - if isBookmarked { - let buttonImage = UIImage(systemName: "bookmark.fill")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - bookmarkIconButton.setImage(buttonImage, for: .normal) - } else { - let buttonImage = UIImage(systemName: "bookmark")?.withTintColor(.systemGreen, renderingMode: .alwaysOriginal) - bookmarkIconButton.setImage(buttonImage, for: .normal) - } - } - } - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension SearchResultListTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - regionImageView, - regionNameLabel, - bookmarkIconButton, - distanceLabel, - telephoneNumberLabel - ].forEach { contentView.addSubview($0) } - - regionImageView.snp.makeConstraints { - $0.top.equalTo(contentView.safeAreaLayoutGuide).offset(16.0) - $0.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - $0.height.equalTo(200.0) - } - - regionNameLabel.snp.makeConstraints { - $0.leading.equalTo(regionImageView) - $0.top.equalTo(regionImageView.snp.bottom).offset(12.0) - } - - distanceLabel.snp.makeConstraints { - $0.leading.equalTo(regionNameLabel) - $0.top.equalTo(regionNameLabel.snp.bottom).offset(10.0) - $0.bottom.equalTo(contentView.safeAreaLayoutGuide) - } - - bookmarkIconButton.snp.makeConstraints { - $0.leading.equalTo(regionNameLabel.snp.trailing).offset(8.0) - $0.centerY.equalTo(regionNameLabel) - $0.trailing.equalTo(regionImageView) - } - bookmarkIconButton.setContentCompressionResistancePriority(.required, for: .horizontal) - bookmarkIconButton.setContentHuggingPriority(.required, for: .horizontal) - - telephoneNumberLabel.snp.makeConstraints { - $0.centerY.equalTo(distanceLabel) - $0.trailing.equalTo(bookmarkIconButton) - } - - } - - func configureUI() { - backgroundColor = .customWhite - contentView.backgroundColor = .customWhite - } -} diff --git a/KoreaOneStep/Scenes/Main/MainViewController.swift b/KoreaOneStep/Scenes/Main/MainViewController.swift deleted file mode 100644 index b081de9..0000000 --- a/KoreaOneStep/Scenes/Main/MainViewController.swift +++ /dev/null @@ -1,415 +0,0 @@ -// -// MainViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/8/24. -// - -import UIKit -import SnapKit -import FloatingPanel -import TTGTags -import NMapsMap -import RxSwift -import RxCocoa - -final class MainViewController: UIViewController { - - lazy var searchBar: UISearchBar = { - let searchBar = UISearchBar() - searchBar.placeholder = "원하는 지역을 검색해주세요." - searchBar.searchBarStyle = .minimal - - searchBar.delegate = self - - return searchBar - }() - - // TODO: - Floating Panel 위치에 따라 안보이게 처리하기 - lazy var tourTypeListView: TTGTextTagCollectionView = { - let tourTypeListView = TTGTextTagCollectionView() - - tourTypeListView.alignment = .left - tourTypeListView.delegate = self - - tourTypeListView.selectionLimit = 1 - - tourTypeListView.scrollDirection = .horizontal - tourTypeListView.showsHorizontalScrollIndicator = false - - tourTypeListView.contentInset = UIEdgeInsets(top: 2, left: 16, bottom: 2, right: 16) - - return tourTypeListView - }() - - private let mapView: NMFMapView = { - let mapView = NMFMapView() - mapView.logoAlign = .leftTop - mapView.positionMode = .direction - return mapView - }() - - private lazy var floatingPanelController: FloatingPanelController = { - let floatingPC = FloatingPanelController() - - floatingPC.delegate = self - - let contentVC = ContentViewController(mainViewModel: viewModel, mainViewModel2: viewModel2) - let contentNav = UINavigationController(rootViewController: contentVC) - floatingPC.set(contentViewController: contentNav) - floatingPC.track(scrollView: contentVC.tableView) - floatingPC.addPanel(toParent: self) - floatingPC.isRemovalInteractionEnabled = false - return floatingPC - }() - - private lazy var moveToUserButtonContainerView: UIView = { - let view = UIView() - view.backgroundColor = .white - view.layer.cornerRadius = 32 / 2 - view.clipsToBounds = true - view.addSubview(moveToUserButton) - return view - }() - - private lazy var moveToUserButton: UIButton = { - let button = UIButton() - let buttonImage = UIImage(systemName: "location.circle")?.withTintColor(.customLightBlue, renderingMode: .alwaysOriginal) - button.setImage(buttonImage, for: .normal) - let symbolConfig = UIImage.SymbolConfiguration(pointSize: 28) - button.setPreferredSymbolConfiguration(symbolConfig, forImageIn: .normal) - return button - }() - - private let viewModel = MainViewModel() - private let viewModel2 = MainViewModel2() - - private let disposeBag = DisposeBag() - - private var userLocationInfo: CLLocationCoordinate2D? - - private let marker = NMFMarker() - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - addTourType() - showFloatingPanel() - bind() - addUserEvents() - } - - // TODO: - 이 부분이 어떻게 동작하는지 다시 살펴보기 - private func showFloatingPanel() { - floatingPanelController.show(animated: false) { - self.floatingPanelController.didMove(toParent: self) - } - } - - private func addTourType() { - TourType.allCases.forEach { tourType in - let content = TTGTextTagStringContent() - content.text = tourType.rawValue - content.textColor = .customBlack - content.textFont = .systemFont(ofSize: 16.0) - - let style = TTGTextTagStyle() - style.cornerRadius = 16.0 - style.backgroundColor = .customWhite - style.extraSpace = .init(width: 16.0, height: 16.0) - - let textTag = TTGTextTag(content: content, style: style) - - tourTypeListView.addTag(textTag) - } - - tourTypeListView.reload() - } - - private func addUserEvents() { - moveToUserButton.addTarget(self, action: #selector(moveToButtonTapped), for: .touchUpInside) - } -} - -extension MainViewController { - @objc func moveToButtonTapped(_ button: UIButton) { - guard let userLocationInfo = userLocationInfo else { - showLocationSettingAlert() - return - } - - viewModel.inputUpdateUserCurrentLocationTrigger.value = () - } -} - -// MARK: - 네이버 지도 관련 메서드 -extension MainViewController { - func configureCamera(lat: Double, lng: Double) { - let cameraUpdate = NMFCameraUpdate(scrollTo: NMGLatLng(lat: lat, lng: lng)) - cameraUpdate.animation = .easeOut - cameraUpdate.animationDuration = 1.5 - mapView.moveCamera(cameraUpdate) - } - - func confiugreMarker(lat: Double, lng: Double) { - marker.mapView = nil - marker.position = NMGLatLng(lat: lat, lng: lng) - marker.mapView = mapView - } -} - -extension MainViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - navigationController?.navigationBar.isHidden = true - - navigationItem.backButtonTitle = "" - } - - func configureConstraints() { - [ - mapView, - searchBar, - tourTypeListView, - moveToUserButtonContainerView - ].forEach { view.addSubview($0) } - - mapView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - - searchBar.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide).offset(40.0) - $0.horizontalEdges.equalTo(view.safeAreaLayoutGuide).inset(16.0) - } - - tourTypeListView.snp.makeConstraints { - $0.top.equalTo(searchBar.snp.bottom) - $0.horizontalEdges.equalTo(view.safeAreaLayoutGuide) - } - - moveToUserButtonContainerView.snp.makeConstraints { - $0.top.equalTo(view.safeAreaLayoutGuide).inset(8.0) - $0.trailing.equalTo(view.safeAreaLayoutGuide).inset(12.0) - $0.size.equalTo(36.0) - } - - moveToUserButton.snp.makeConstraints { - $0.center.equalTo(moveToUserButtonContainerView) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - } - - func bind() { - - let input = MainViewModel2.Input( - viewDidLoad: Observable.just(()) - ) - - let output = viewModel2.transform(input: input) - - output.showAlertTriggerForAuthorization - .drive(with: self) { owner, isDenied in - if isDenied { - owner.showLocationSettingAlert() - - owner.mapView.locationOverlay.location = NMGLatLng() - owner.mapView.locationOverlay.hidden = true - - // TODO: - mainViewModel2에서 작업 예정 -// owner.viewModel.outputUserCurrentLocationInfoToMainVC.value = nil -// owner.viewModel.outputUserCurrentLocationInfoToContentVC.value = nil -// -// owner.viewModel.outputLocationBasedTouristDestinationList.value = [] - } - -// weakSelf.viewModel.outputActivityIndicatorStopTrigger.value = () - } - .disposed(by: disposeBag) - - viewModel2.userLocationInfoRelay.asDriver() - .drive(with: self) { owner, coordinate in - guard let coordinate = coordinate else { return } - - owner.mapView.locationOverlay.location = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) - owner.mapView.locationOverlay.hidden = false - - owner.configureCamera(lat: coordinate.latitude, lng: coordinate.longitude) - } - .disposed(by: disposeBag) - -// viewModel.inputSearchUserCurrentLocationTrigger.value = () - -// viewModel.outputUserCurrentLocationInfoToMainVC.bind { [weak self] coordinate in -// guard let weakSelf = self else { return } -// -// weakSelf.userLocationInfo = coordinate -// -// guard let coordinate = coordinate else { return } -// -// weakSelf.mapView.locationOverlay.location = NMGLatLng(lat: coordinate.latitude, lng: coordinate.longitude) -// weakSelf.mapView.locationOverlay.hidden = false -// -// weakSelf.configureCamera(lat: coordinate.latitude, lng: coordinate.longitude) -// } - - viewModel.outputSelectedTouristDestination.bind { [weak self] touristDestination in - guard let weakSelf = self else { return } - - guard let touristDestination = touristDestination else { return } - - weakSelf.confiugreMarker( - lat: Double(touristDestination.mapy) ?? 126.9783881, - lng: Double(touristDestination.mapx) ?? 37.5666102 - ) - - weakSelf.configureCamera( - lat: Double(touristDestination.mapy) ?? 126.9783881, - lng: Double(touristDestination.mapx) ?? 37.5666102 - ) - - weakSelf.floatingPanelController.move(to: .half, animated: true) - } - - viewModel.outputTappedTouristDestination.bind { [weak self] tappedTouristDestionation in - guard let weakSelf = self else { return } - - guard let tappedTouristDestionation = tappedTouristDestionation else { return } - - weakSelf.confiugreMarker( - lat: Double(tappedTouristDestionation.mapy) ?? 126.9783881, - lng: Double(tappedTouristDestionation.mapx) ?? 37.5666102 - ) - - weakSelf.configureCamera( - lat: Double(tappedTouristDestionation.mapy) ?? 126.9783881, - lng: Double(tappedTouristDestionation.mapx) ?? 37.5666102 - ) - - let detailVC = DetailViewController() - - detailVC.isFromBookmarkVC = false - detailVC.mainViewModel = weakSelf.viewModel - - detailVC.contentTitle = tappedTouristDestionation.title - detailVC.contentId = tappedTouristDestionation.contentid - detailVC.contentTypeId = tappedTouristDestionation.contenttypeid - - guard let contentNav = weakSelf.floatingPanelController.contentViewController as? UINavigationController else { return } - guard let contentVC = contentNav.viewControllers[0] as? ContentViewController else { return } - - contentVC.navigationController?.pushViewController(detailVC, animated: true) - - weakSelf.viewModel.inputActivityIndicatorStopTrigger.value = () - } - - viewModel.outputDetailVCLeftBarButtonItemTappedTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.searchBar.isHidden = false - weakSelf.tourTypeListView.isHidden = false - } - - viewModel.outputDetailVCViewDidLoadTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.searchBar.isHidden = true - weakSelf.tourTypeListView.isHidden = true - } - - viewModel.outputDetailVCAddressCellTappTrigger.bind { [weak self] latitude, longitude in - guard let weakSelf = self else { return } - - guard - let latitude = latitude, - let longitude = longitude - else { return } - - weakSelf.configureCamera(lat: latitude, lng: longitude) - - if weakSelf.floatingPanelController.state == FloatingPanelState.full { - weakSelf.floatingPanelController.move(to: .tip, animated: true) - } - } - - viewModel2.indicatorTriggerRelay.asDriver() - .drive(with: self) { owner, indicatorTrigger in - if indicatorTrigger { - owner.view.makeToastActivity(.center) - } else { - owner.view.hideToastActivity() - } - } - .disposed(by: disposeBag) - -// viewModel.outputActivityIndicatorStopTrigger.bind { [weak self] trigger in -// guard let weakSelf = self else { return } -// -// guard let trigger = trigger else { return } -// -// } -// -// viewModel.outputActivityIndicatorStartTrigger.bind { [weak self] trigger in -// guard let weakSelf = self else { return } -// -// guard let trigger = trigger else { return } -// -// weakSelf.view.makeToastActivity(.center) -// } - -// viewModel.outputShowAlertTriggerForAuthorization.bind { [weak self] isDenied in -// guard let weakSelf = self else { return } -// -// if isDenied { -// weakSelf.showLocationSettingAlert() -// -// weakSelf.mapView.locationOverlay.location = NMGLatLng() -// weakSelf.mapView.locationOverlay.hidden = true -// -// weakSelf.viewModel.outputUserCurrentLocationInfoToMainVC.value = nil -// weakSelf.viewModel.outputUserCurrentLocationInfoToContentVC.value = nil -// -// weakSelf.viewModel.outputLocationBasedTouristDestinationList.value = [] -// } -// -// weakSelf.viewModel.outputActivityIndicatorStopTrigger.value = () -// } - } -} - -extension MainViewController: UISearchBarDelegate { - func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) { - let searchVC = SearchViewController(mainViewModel: viewModel) - let searchNav = UINavigationController(rootViewController: searchVC) - searchNav.modalPresentationStyle = .fullScreen - present(searchNav, animated: false) - } -} - -extension MainViewController: TTGTextTagCollectionViewDelegate { - func textTagCollectionView(_ textTagCollectionView: TTGTextTagCollectionView!, didTap tag: TTGTextTag!, at index: UInt) { - tag.selected = false - guard let content = tag.content as? TTGTextTagStringContent else { return } - print(index, content.text) - - guard let contentNav = floatingPanelController.contentViewController as? UINavigationController else { return } - guard let contentVC = contentNav.viewControllers.first as? ContentViewController else { return } - contentVC.selectedTourType = TourType(rawValue: content.text) - - view.makeToastActivity(.center) - viewModel.inputTourType.value = (contentVC.userLocationInfo, contentVC.selectedFilteringDistance, contentVC.selectedFilteringCategory, contentVC.selectedTourType) - } -} - -extension MainViewController: FloatingPanelControllerDelegate { - -} diff --git a/KoreaOneStep/Scenes/Main/Search/Filter/FilterTableViewCell.swift b/KoreaOneStep/Scenes/Main/Search/Filter/FilterTableViewCell.swift deleted file mode 100644 index d0bd838..0000000 --- a/KoreaOneStep/Scenes/Main/Search/Filter/FilterTableViewCell.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// FilterTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import UIKit -import SnapKit -import TTGTags - -final class FilterTableViewCell: UITableViewCell { - - lazy var tagListView: TTGTextTagCollectionView = { - let tagListView = TTGTextTagCollectionView() - - tagListView.alignment = .left - - tagListView.selectionLimit = 1 - - tagListView.showsVerticalScrollIndicator = false - tagListView.scrollView.scrollsToTop = true - - return tagListView - }() - - var tagList: [String] = [] { - didSet { - addTags() - } - } - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension FilterTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - tagListView, - ].forEach { contentView.addSubview($0) } - - tagListView.snp.makeConstraints { - $0.edges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - } - - func configureUI() { - } -} - -extension FilterTableViewCell { - private func addTags() { - tagListView.removeAllTags() - - for i in 0.. UIView? { - let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: "customHeader") - header?.textLabel?.text = FilterTableViewSection.allCases[section].sectionTitle - header?.textLabel?.textColor = .customBlack - header?.textLabel?.font = .systemFont(ofSize: 24.0, weight: .bold) - - return header - } -} - -extension FilterViewController: UITableViewDataSource { - func numberOfSections(in tableView: UITableView) -> Int { - return FilterTableViewSection.allCases.count - } - - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return 1 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - guard let cell = tableView.dequeueReusableCell(withIdentifier: FilterTableViewCell.identifier, for: indexPath) as? FilterTableViewCell else { return UITableViewCell() } - - cell.selectionStyle = .none - - cell.tagListView.delegate = self - - if FilterTableViewSection.allCases[indexPath.section] == .selectRegion { - cell.tagList = self.regionTagList.map { $0.name } - return cell - } - - cell.tagList = self.siGunGuTagList.map { $0.name } - return cell - } -} - -extension FilterViewController: TTGTextTagCollectionViewDelegate { - - func textTagCollectionView(_ textTagCollectionView: TTGTextTagCollectionView!, didTap tag: TTGTextTag!, at index: UInt) { - guard let searchViewModel = searchViewModel else { return } - - if tag.selected { - - let selectedTagName = tag.content.getAttributedString().string - - let selectedSiGunGuTagList = self.siGunGuTagList.filter { $0.name == selectedTagName } - - if selectedSiGunGuTagList.count >= 1 { - print("시군구", selectedSiGunGuTagList) - - searchViewModel.inputSelectedSiGunGuTag.value = selectedSiGunGuTagList[0] - } else { - view.makeToastActivity(.center) - - let selectedReigonTagList = self.regionTagList.filter { $0.name == selectedTagName } - print("지역", selectedReigonTagList) - - searchViewModel.inputSelectedRegionTag.value = selectedReigonTagList[0] - } - } - } -} - diff --git a/KoreaOneStep/Scenes/Main/Search/SearchTableViewCell.swift b/KoreaOneStep/Scenes/Main/Search/SearchTableViewCell.swift deleted file mode 100644 index 595cd38..0000000 --- a/KoreaOneStep/Scenes/Main/Search/SearchTableViewCell.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// SearchTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import UIKit -import SnapKit - -final class SearchTableViewCell: UITableViewCell { - - let magnifyingglassImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "magnifyingglass") - imageView.tintColor = .customBlack - imageView.contentMode = .scaleAspectFit - return imageView - }() - - let keywordLabel: UILabel = { - let label = UILabel() - label.text = "크리스탈 팰리스" - label.textColor = .customBlack - label.font = .boldSystemFont(ofSize: 18.0) - return label - }() - - let xmarkButton: UIButton = { - let button = UIButton() - let buttonImage = UIImage(systemName: "xmark")?.withTintColor(.customBlack, renderingMode: .alwaysOriginal) - button.setImage(buttonImage, for: .normal) - return button - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -extension SearchTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - magnifyingglassImageView, - keywordLabel, - xmarkButton - ].forEach { contentView.addSubview($0) } - - magnifyingglassImageView.snp.makeConstraints { - $0.size.equalTo(24.0) - $0.centerY.equalToSuperview() - $0.leading.equalToSuperview().inset(16.0) - } - - keywordLabel.snp.makeConstraints { - $0.centerY.equalToSuperview() - $0.leading.equalTo(magnifyingglassImageView.snp.trailing).offset(16.0) - $0.trailing.equalTo(xmarkButton.snp.leading).offset(-8.0) - } - - xmarkButton.snp.makeConstraints { - $0.size.equalTo(24.0) - $0.centerY.equalToSuperview() - $0.trailing.equalToSuperview().inset(16.0) - } - } - - func configureUI() { - - } -} diff --git a/KoreaOneStep/Scenes/Main/Search/SearchViewController.swift b/KoreaOneStep/Scenes/Main/Search/SearchViewController.swift deleted file mode 100644 index 0f1175f..0000000 --- a/KoreaOneStep/Scenes/Main/Search/SearchViewController.swift +++ /dev/null @@ -1,257 +0,0 @@ -// -// SearchViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/10/24. -// - -import UIKit -import SnapKit - -final class SearchViewController: UIViewController { - - lazy var searchBar: UISearchBar = { - let searchBar = UISearchBar() - searchBar.searchTextField.leftView = nil - searchBar.searchTextField.backgroundColor = .customWhite - searchBar.delegate = self - return searchBar - }() - - lazy var recentKeywordTableView: UITableView = { - let tableView = UITableView() - tableView.delegate = self - tableView.dataSource = self - - tableView.register(SearchTableViewCell.self, forCellReuseIdentifier: SearchTableViewCell.identifier) - - return tableView - }() - - lazy var searchedResultTableView: UITableView = { - let tableView = UITableView() - tableView.delegate = self - tableView.dataSource = self - - tableView.register(SearchedResultsTableViewTableViewCell.self, forCellReuseIdentifier: SearchedResultsTableViewTableViewCell.identifier) - - tableView.isHidden = true - - return tableView - }() - - private let viewModel = SearchViewModel() - private var mainViewModel: MainViewModel - - private var recentKeywordList: [RecentKeyword] = [] - private var searchedResultList: [KSItem] = [] - - private var selectedRegion: ACItem? - private var selectedSiGunGu: ACItem? - - init(mainViewModel: MainViewModel) { - self.mainViewModel = mainViewModel - - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - bind() - addUserEvents() - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - - searchBar.becomeFirstResponder() - } - - private func addUserEvents() { - let tap = UITapGestureRecognizer(target: self, action: #selector(backgroundViewTapped)) - tap.cancelsTouchesInView = false - view.addGestureRecognizer(tap) - } -} - -extension SearchViewController { - @objc func backgroundViewTapped(_ gestureRecognizer: UIGestureRecognizer) { - searchBar.resignFirstResponder() - print("바탕화면 터치됨") - view.endEditing(true) - } - - @objc func leftBarButtonItemTapped() { - dismiss(animated: false) - } - - @objc func rightBarButtonItemTapped() { - selectedRegion = nil - selectedSiGunGu = nil - - let filterVC = FilterViewController(searchViewModel: viewModel) - let filterNav = UINavigationController(rootViewController: filterVC) - present(filterNav, animated: true) - } - - @objc func xmarkButtonTapped(_ button: UIButton) { - let recentKeyword = recentKeywordList[button.tag] - - viewModel.inputXmarkButtonTapTrigger.value = recentKeyword - } -} - -extension SearchViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - navigationController?.navigationBar.tintColor = .customBlack - - navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "chevron.left"), style: .plain, target: self, action: #selector(leftBarButtonItemTapped)) - navigationItem.titleView = searchBar - navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "slider.horizontal.3"), style: .plain, target: self, action: #selector(rightBarButtonItemTapped)) - } - - func configureConstraints() { - view.addSubview(recentKeywordTableView) - view.addSubview(searchedResultTableView) - - recentKeywordTableView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - - searchedResultTableView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - } - - func bind() { - viewModel.inputViewDidLoadTrigger.value = () - - viewModel.outputSearchedResultList.bind { [weak self] searchedResultList in - guard let weakSelf = self else { return } - - guard let searchedResultList = searchedResultList else { return } - weakSelf.searchedResultList = searchedResultList - weakSelf.searchedResultTableView.reloadData() - - if searchedResultList.count >= 1 { - weakSelf.searchedResultTableView.isHidden = false - } else if searchedResultList.count < 1 { - weakSelf.searchedResultTableView.isHidden = true - weakSelf.view.makeToast(ToastMessage.Failure.noSearchingResults, position: .center) - } - weakSelf.view.hideToastActivity() - } - - viewModel.outputSelectedRegionTag.bind { [weak self] selectedRegion in - guard let weakSelf = self else { return } - - weakSelf.selectedRegion = selectedRegion - } - - viewModel.outputSelectedSiGunGu.bind { [weak self] selectedSiGunGu in - guard let weakSelf = self else { return } - - weakSelf.selectedSiGunGu = selectedSiGunGu - } - - viewModel.outputRecentKeywordList.bind { [weak self] recentKeywordList in - guard let weakSelf = self else { return } - - weakSelf.recentKeywordList = recentKeywordList - weakSelf.recentKeywordTableView.reloadData() - } - } -} - -extension SearchViewController: UITableViewDelegate { - func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if tableView == recentKeywordTableView { - view.makeToastActivity(.center) - - let recentKeyword = recentKeywordList[indexPath.row] - - searchBar.text = recentKeyword.keyword - - viewModel.inputSearchElements.value = (recentKeyword.keyword, selectedRegion, selectedSiGunGu) - } else if tableView == searchedResultTableView { - let selectedTouristDestination = searchedResultList[indexPath.row] - - mainViewModel.inputSearchVCTableViewDidSelectRowAt.value = selectedTouristDestination - - dismiss(animated: true) - - mainViewModel.inputActivityIndicatorStartTrigger.value = () - } - } -} - -extension SearchViewController: UITableViewDataSource { - func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - if tableView == recentKeywordTableView { - return recentKeywordList.count - } else if tableView == searchedResultTableView { - return searchedResultList.count - } - return 0 - } - - func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - - if tableView == recentKeywordTableView { - guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchTableViewCell.identifier, for: indexPath) as? SearchTableViewCell else { return UITableViewCell() } - - let recentKeyword = recentKeywordList[indexPath.row] - - cell.keywordLabel.text = recentKeyword.keyword - - cell.xmarkButton.tag = indexPath.row - cell.xmarkButton.addTarget(self, action: #selector(xmarkButtonTapped), for: .touchUpInside) - - return cell - } else if tableView == searchedResultTableView { - guard let cell = tableView.dequeueReusableCell(withIdentifier: SearchedResultsTableViewTableViewCell.identifier, for: indexPath) as? SearchedResultsTableViewTableViewCell else { return UITableViewCell() } - - let searchedResult = searchedResultList[indexPath.row] - - cell.nameLabel.text = searchedResult.title - cell.addressLabel.text = searchedResult.addr1 - - return cell - } - - return UITableViewCell() - } -} - -extension SearchViewController: UISearchBarDelegate { - func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { - guard searchBar.text! != "" else { return } - - view.makeToastActivity(.center) - - let trimmedsearchBarText = searchBar.text?.trimmingCharacters(in: .whitespacesAndNewlines) - - if trimmedsearchBarText != "" { - viewModel.inputSearchElements.value = (trimmedsearchBarText, selectedRegion, selectedSiGunGu) - searchBar.resignFirstResponder() - } - } - - func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { - if searchText.trimmingCharacters(in: .whitespacesAndNewlines) == "" { - searchedResultTableView.isHidden = true - } - } -} diff --git a/KoreaOneStep/Scenes/Main/Search/SearchedResultsTableViewTableViewCell.swift b/KoreaOneStep/Scenes/Main/Search/SearchedResultsTableViewTableViewCell.swift deleted file mode 100644 index 3a2280b..0000000 --- a/KoreaOneStep/Scenes/Main/Search/SearchedResultsTableViewTableViewCell.swift +++ /dev/null @@ -1,78 +0,0 @@ -// -// SearchedResultsTableViewTableViewCell.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/19/24. -// - -import UIKit -import SnapKit - -final class SearchedResultsTableViewTableViewCell: UITableViewCell { - - let nameLabel: UILabel = { - let label = UILabel() - label.font = .systemFont(ofSize: 18.0, weight: .semibold) - label.textColor = .customBlack - label.text = "테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트" - return label - }() - - private let mapImageView: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "map") - imageView.tintColor = .customBlack - return imageView - }() - - let addressLabel: UILabel = { - let label = UILabel() - label.font = .systemFont(ofSize: 16.0) - label.textColor = .customDarkGray - label.text = "테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트테스트" - return label - }() - - override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { - super.init(style: style, reuseIdentifier: reuseIdentifier) - - configureConstraints() - configureUI() - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - -} - -extension SearchedResultsTableViewTableViewCell: UITableViewCellConfiguration { - func configureConstraints() { - [ - nameLabel, - mapImageView, - addressLabel - ].forEach { contentView.addSubview($0) } - - nameLabel.snp.makeConstraints { - $0.top.horizontalEdges.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - mapImageView.snp.makeConstraints { - $0.size.equalTo(20.0) - $0.top.equalTo(nameLabel.snp.bottom).offset(4.0) - $0.leading.bottom.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - - addressLabel.snp.makeConstraints { - $0.leading.equalTo(mapImageView.snp.trailing).offset(4.0) - $0.centerY.equalTo(mapImageView) - $0.trailing.equalTo(contentView.safeAreaLayoutGuide).inset(16.0) - } - } - - func configureUI() { - backgroundColor = .customWhite - contentView.backgroundColor = .customWhite - } -} diff --git a/KoreaOneStep/Scenes/Setting/SettingViewController.swift b/KoreaOneStep/Scenes/Setting/SettingViewController.swift deleted file mode 100644 index abb63b3..0000000 --- a/KoreaOneStep/Scenes/Setting/SettingViewController.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// SettingViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/8/24. -// - -import UIKit -import SnapKit -import Toast -import RxSwift -import RxCocoa -import RxAppState - -final class SettingViewController: UIViewController { - - lazy var tableView: UITableView = { - let tableView = UITableView() - - tableView.register(UITableViewCell.self, forCellReuseIdentifier: UITableViewCell.identifier) - - return tableView - }() - - private var disposeBag = DisposeBag() - - private let viewModel = SettingViewModel() - - override func viewDidLoad() { - super.viewDidLoad() - - configureNavigationBar() - configureConstraints() - configureUI() - bind() - } -} - -extension SettingViewController: UIViewControllerConfiguration { - func configureNavigationBar() { - navigationItem.title = navigationController?.tabBarItem.title - } - - func configureConstraints() { - view.addSubview(tableView) - - tableView.snp.makeConstraints { - $0.edges.equalTo(view.safeAreaLayoutGuide) - } - } - - func configureUI() { - view.backgroundColor = .customWhite - } - - func bind() { - - let itemTapped = Observable.zip( - tableView.rx.modelSelected(SettingTableViewCellTitle.self), - tableView.rx.itemSelected - ) - - let input = SettingViewModel.Input(itemTapped: itemTapped) - let output = viewModel.transform(input: input) - - output.settingTableViewCellTitles - .drive(tableView.rx.items(cellIdentifier: UITableViewCell.identifier)) { row, element, cell in - - cell.selectionStyle = .none - - cell.textLabel?.text = element.rawValue - cell.textLabel?.textColor = element.titleColor - cell.textLabel?.font = .boldSystemFont(ofSize: 18.0) - - if !(element == .removeAllBookmarkRecords) { - cell.accessoryType = .disclosureIndicator - } - } - .disposed(by: disposeBag) - - output.removeAllBookmarksToastMessage - .drive(with: self) { owner, toastMessage in - owner.view.makeToast(toastMessage) - } - .disposed(by: disposeBag) - } -} diff --git a/KoreaOneStep/Scenes/TabBarViewController.swift b/KoreaOneStep/Scenes/TabBarViewController.swift deleted file mode 100644 index 157aa6b..0000000 --- a/KoreaOneStep/Scenes/TabBarViewController.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// TabBarViewController.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/8/24. -// - -import UIKit - -final class TabBarViewController: UITabBarController { - - override func viewDidLoad() { - super.viewDidLoad() - - let mainVC = MainViewController() - let bookmarkVC = BookmarkViewController() - let settingVC = SettingViewController() - - let bookmarkNav = UINavigationController(rootViewController: bookmarkVC) - let mainNav = UINavigationController(rootViewController: mainVC) - let settingNav = UINavigationController(rootViewController: settingVC) - - mainNav.tabBarItem = UITabBarItem( - title: "검색", - image: UIImage(systemName: "magnifyingglass"), - selectedImage: UIImage(systemName: "magnifyingglass") - ) - bookmarkNav.tabBarItem = UITabBarItem( - title: "북마크", - image: UIImage(systemName: "bookmark"), - selectedImage: UIImage(systemName: "bookmark.fill") - ) - settingNav.tabBarItem = UITabBarItem( - title: "설정", - image: UIImage(systemName: "gearshape"), - selectedImage: UIImage(systemName: "gearshape.fill") - ) - - tabBar.tintColor = .black - tabBar.backgroundColor = .customWhite - - setViewControllers([mainNav, bookmarkNav, settingNav], animated: false) - } -} diff --git a/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkSectionData.swift b/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkSectionData.swift deleted file mode 100644 index e39aa00..0000000 --- a/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkSectionData.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// BookmarkSectionData.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 8/18/24. -// - -import Foundation -import Differentiator - -struct BookmarkSectionData { - var items: [Item] -} - -extension BookmarkSectionData: SectionModelType { - typealias Item = Bookmark - - init(original: BookmarkSectionData, items: [Item]) { - self = original - self.items = items - } -} diff --git a/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkViewModel.swift b/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkViewModel.swift deleted file mode 100644 index d22a336..0000000 --- a/KoreaOneStep/ViewModels/BookmarkViewModel/BookmarkViewModel.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// BookmarkViewModel.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 8/18/24. -// - -import Foundation -import RxSwift -import RxCocoa - -final class BookmarkViewModel: ViewModelType { - - let bookmarkIconButtonTapped = PublishSubject() - - private let searchTextSubject = BehaviorSubject(value: "") - - var disposeBag = DisposeBag() - - struct Input { - let viewWillAppear: Observable - let searchText: ControlProperty - let textDidBeginEditing: Observable - } - - struct Output { - let viewWillAppear: Driver - let section: Driver<[BookmarkSectionData]> - } - - func transform(input: Input) -> Output { - let sectionRelay = PublishRelay<[BookmarkSectionData]>() - - let viewWillAppear = input.viewWillAppear - .map { trigger in - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - sectionRelay.accept([BookmarkSectionData(items: bookmarkList)]) - return bookmarkList.count - } - - input.searchText - .subscribe(with: self) { owner, searchText in - owner.searchTextSubject.onNext(searchText) - owner.collectionViewUpdate(with: searchText, sectionRelay: sectionRelay) - } - .disposed(by: disposeBag) - - input.textDidBeginEditing - .withLatestFrom(searchTextSubject) - .subscribe(with: self) { owner, searchText in - owner.collectionViewUpdate(with: searchText, sectionRelay: sectionRelay) - } - .disposed(by: disposeBag) - - bookmarkIconButtonTapped - .bind { bookmark in - RealmManager.shared.delete(bookmark) - - let updatedBookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - sectionRelay.accept([BookmarkSectionData(items: updatedBookmarkList)]) - } - .disposed(by: disposeBag) - - return Output( - viewWillAppear: viewWillAppear.asDriver(onErrorJustReturn: 0), - section: sectionRelay.asDriver(onErrorJustReturn: []) - ) - } -} - -// MARK: - Custom Methods -private extension BookmarkViewModel { - func collectionViewUpdate(with searchText: String, sectionRelay: PublishRelay<[BookmarkSectionData]>) { - let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines) - - if searchText.isEmpty { - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - sectionRelay.accept([BookmarkSectionData(items: bookmarkList)]) - } else { - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines) - let filteredBookmarkList = bookmarkList.filter { $0.title.contains(trimmedSearchText) } - sectionRelay.accept([BookmarkSectionData(items: filteredBookmarkList)]) - } - } -} diff --git a/KoreaOneStep/ViewModels/CommunicationBase/CustomObservable.swift b/KoreaOneStep/ViewModels/CommunicationBase/CustomObservable.swift deleted file mode 100644 index 80af2d9..0000000 --- a/KoreaOneStep/ViewModels/CommunicationBase/CustomObservable.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// CustomObservable.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/16/24. -// - -import Foundation - -final class CustomObservable { - - private var closure: ((T) -> Void)? - - var value: T { - didSet { - closure?(value) - } - } - - init(_ value: T) { - self.value = value - } - - func bind(_ closure: @escaping (T) -> Void) { - closure(value) - self.closure = closure - } -} diff --git a/KoreaOneStep/ViewModels/DetailViewModel.swift b/KoreaOneStep/ViewModels/DetailViewModel.swift deleted file mode 100644 index c7dbb2c..0000000 --- a/KoreaOneStep/ViewModels/DetailViewModel.swift +++ /dev/null @@ -1,171 +0,0 @@ -// -// DetailViewModel.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/17/24. -// - -import Foundation - -final class DetailViewModel { - - let inputViewDidLoadTrigger: CustomObservable<(String?, String?)> = CustomObservable((nil, nil)) - let inputIsBookmarked: CustomObservable = CustomObservable(nil) - let inputBookmarkButtonTrigger: CustomObservable<(String?, String?, String?, String?, String?)> = CustomObservable((nil, nil, nil, nil, nil)) - let inputAcitivityIndicatorStartTrigger: CustomObservable = CustomObservable(nil) - - let outputDetailTableViewData: CustomObservable<(CIItem?, Dictionary)> = CustomObservable((nil, [:])) - let outputIsBookmarked: CustomObservable = CustomObservable(false) - let outputAcitivityIndicatorStartTrigger: CustomObservable = CustomObservable(nil) - - init() { - inputViewDidLoadTrigger.bind { [weak self] contentId, contentTypeId in - guard let weakSelf = self else { return } - - guard - let contentId = contentId, - let contentTypeId = contentTypeId - else { return } - - let group = DispatchGroup() - - var touristDestinationCommonInformationTemp: CIItem? - - group.enter() - KoreaTravelingManager.shared.fetchTouristDestionationCommonInformation( - api: .touristDestionationCommonInformation(contentId: contentId, contentTypeId: contentTypeId) - ) { touristDestinationCommonInformation in - - touristDestinationCommonInformationTemp = touristDestinationCommonInformation - - group.leave() - } - - var providedImpairmentAidServiceList: Dictionary = [ - .physicalDisability:[], - .visualImpairment: [], - .hearingImpairment: [], - .familiesWithInfantsAndToddlers: [], - .elderlyPeople: [] - ] - - group.enter() - KoreaTravelingManager.shared.fetchProvidedImpairmentAidServices(api: .providedImpairmentAidServices(contentId: contentId) - ) { providedImpairmentAidServices in - // 지체장애 도움 서비스 - // 주차여부 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.parking) - // 대중교통 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.route) - // 핵심동선 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.publictransport) - // 매표소 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.ticketoffice) - // 홍보물 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.promotion) - // 휠체어 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.wheelchair) - // 엘리베이터 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.elevator) - // 화장실 - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.restroom) - // 관람석(좌석) - providedImpairmentAidServiceList[.physicalDisability]?.append(providedImpairmentAidServices.auditorium) - - // 시각장애 - // 점자블록 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.braileblock) - // 안내요원 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.guidehuman) - // 음성안내 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.audioguide) - // 큰활자/점자 홍보물 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.bigprint) - // 점자표지판 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.brailepromotion) - // 유도안내설비 - providedImpairmentAidServiceList[.visualImpairment]?.append(providedImpairmentAidServices.guidesystem) - - // 청각장애 - // 수어안내 - providedImpairmentAidServiceList[.hearingImpairment]?.append(providedImpairmentAidServices.signguide) - // 자막 - providedImpairmentAidServiceList[.hearingImpairment]?.append(providedImpairmentAidServices.videoguide) - - // 영유아 가족 - // 유아차 대여 - providedImpairmentAidServiceList[.familiesWithInfantsAndToddlers]?.append(providedImpairmentAidServices.stroller) - - // 고령자 - // 휠체어 대여 - providedImpairmentAidServiceList[.elderlyPeople]?.append(providedImpairmentAidServices.wheelchair) - // 이동보조도구 대여 - providedImpairmentAidServiceList[.elderlyPeople]?.append("") - - group.leave() - } - - group.notify(queue: .main) { - weakSelf.outputDetailTableViewData.value = (touristDestinationCommonInformationTemp, providedImpairmentAidServiceList) - } - } - - inputIsBookmarked.bind { [weak self] contentId in - guard let weakSelf = self else { return } - - guard let contentId = contentId else { return } - - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - - let filteredBookmarkList = bookmarkList.filter { $0.contentId == contentId } - - if filteredBookmarkList.count == 0 { - weakSelf.outputIsBookmarked.value = false - } else if filteredBookmarkList.count == 1 { - weakSelf.outputIsBookmarked.value = true - } - } - - inputBookmarkButtonTrigger.bind { [weak self] contentId, contentTypeId, title, imageURL, region in - - guard let weakSelf = self else { return } - - guard - let contentId = contentId, - let contentTypeId = contentTypeId, - let title = title, - let imageURL = imageURL, - let region = region - else { return } - - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - - let filteredBookmarkList = bookmarkList.filter { $0.contentId == contentId } - - if filteredBookmarkList.count < 1 { - // 추가 - let newBookmark = Bookmark( - contentId: contentId, - contentTypeId: contentTypeId, - title: title, - imageURL: imageURL, - region: region - ) - RealmManager.shared.write(newBookmark) - weakSelf.outputIsBookmarked.value = true - } else { - // 삭제 - RealmManager.shared.delete(filteredBookmarkList[0]) - weakSelf.outputIsBookmarked.value = false - } - } - - inputAcitivityIndicatorStartTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.outputAcitivityIndicatorStartTrigger.value = trigger - } - } -} diff --git a/KoreaOneStep/ViewModels/MainViewModel.swift b/KoreaOneStep/ViewModels/MainViewModel.swift deleted file mode 100644 index 5a6e30f..0000000 --- a/KoreaOneStep/ViewModels/MainViewModel.swift +++ /dev/null @@ -1,270 +0,0 @@ -// -// MainViewModel.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/16/24. -// - -import Foundation -import CoreLocation - -struct SearchResulData { - let locationBasedTouristDestination: LBItem - let isBookmarked: Bool -} - -final class MainViewModel { - let inputSearchLocationBasedTourismInformationTrigger: CustomObservable = CustomObservable(nil) - let inputForTableViewUpdate: CustomObservable<(CLLocationCoordinate2D?, FilteringOrder.FilteringDistance, FilteringOrder)> = CustomObservable((nil, .oneKiloMeter, .title)) - let inputTourType: CustomObservable<(CLLocationCoordinate2D?, FilteringOrder.FilteringDistance, FilteringOrder, TourType?)> = CustomObservable((nil, .oneKiloMeter, .title, nil)) - let inputAddNewBookmark: CustomObservable = CustomObservable(nil) - let inputRemoveBookmark: CustomObservable = CustomObservable(nil) - let inputContentVCTableViewDidSelectRowAtTrigger: CustomObservable = CustomObservable(nil) - let inputSearchVCTableViewDidSelectRowAt: CustomObservable = CustomObservable(nil) - let inputDetailVCLeftBarButtonItemTappedTrigger: CustomObservable = CustomObservable(nil) - let inputDetailVCViewDidLoadTrigger: CustomObservable = CustomObservable(nil) - let inputDetailVCAddressCellTappTrigger: CustomObservable<(Double?, Double?)> = CustomObservable((nil, nil)) - let inputActivityIndicatorStopTrigger: CustomObservable = CustomObservable(nil) - let inputActivityIndicatorStartTrigger: CustomObservable = CustomObservable(nil) - let inputSearchUserCurrentLocationTrigger: CustomObservable = CustomObservable(nil) - let inputUpdateUserCurrentLocationTrigger: CustomObservable = CustomObservable(nil) - - let outputLocationBasedTouristDestinationList: CustomObservable<[SearchResulData]?> = CustomObservable(nil) - let outputUserCurrentLocationInfoToMainVC: CustomObservable = CustomObservable(nil) - let outputUserCurrentLocationInfoToContentVC: CustomObservable = CustomObservable(nil) - let outputSelectedTouristDestination: CustomObservable = CustomObservable(nil) - let outputTappedTouristDestination: CustomObservable = CustomObservable(nil) - let outputDetailVCLeftBarButtonItemTappedTrigger: CustomObservable = CustomObservable(nil) - let outputDetailVCViewDidLoadTrigger: CustomObservable = CustomObservable(nil) - let outputDetailVCAddressCellTappTrigger: CustomObservable<(Double?, Double?)> = CustomObservable((nil, nil)) - let outputActivityIndicatorStopTrigger: CustomObservable = CustomObservable(nil) - let outputActivityIndicatorStartTrigger: CustomObservable = CustomObservable(nil) - let outputShowAlertTriggerForAuthorization: CustomObservable = CustomObservable(false) - - init() { - inputSearchUserCurrentLocationTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - LocationManager.shared.fetchLocation { [weak self] coordinate, error, isDenied in - guard let weakSelf = self else { return } - - guard error == nil else { - print("위치 찾기 에러 발생: ", error) - return - } - - guard !isDenied else { - print("Denied") - weakSelf.outputShowAlertTriggerForAuthorization.value = isDenied - return - } - - guard let coordinate = coordinate else { - print("Something is wrong.") - return - } - - weakSelf.outputUserCurrentLocationInfoToMainVC.value = coordinate - weakSelf.outputUserCurrentLocationInfoToContentVC.value = coordinate - } - } - - inputSearchLocationBasedTourismInformationTrigger.bind { coordinate in - - guard let coordinate = coordinate else { return } - - KoreaTravelingManager.shared.fetchLocationBasedTourismInformation( - api: .locationBasedTourismInformation( - latitude: coordinate.latitude, - longitude: coordinate.longitude, - radius: KoreaTravelingAPI.radiusDefaultValue, - arrange: KoreaTravelingAPI.arrageDefaultValue, - contentTypeId: KoreaTravelingAPI.contentTypeIdDefaultValue - ) - ) { [weak self] touristDestinationList in - guard let weakSelf = self else { return } - - let searchResulDataList = weakSelf.generateSearchResulDataList(touristDestinationList) - - weakSelf.outputLocationBasedTouristDestinationList.value = searchResulDataList - } - } - - inputForTableViewUpdate.bind { coordinate, filteringDistance, filteringOrder in - guard let coordinate = coordinate else { return } - - KoreaTravelingManager.shared.fetchLocationBasedTourismInformation( - api: .locationBasedTourismInformation( - latitude: coordinate.latitude, - longitude: coordinate.longitude, - radius: filteringDistance.rawValue, - arrange: filteringOrder.sortingCode, - contentTypeId: KoreaTravelingAPI.contentTypeIdDefaultValue - ) - ) { [weak self] touristDestinationList in - guard let weakSelf = self else { return } - - let searchResulDataList = weakSelf.generateSearchResulDataList(touristDestinationList) - - weakSelf.outputLocationBasedTouristDestinationList.value = searchResulDataList - } - } - - inputTourType.bind { coordinate, filteringDistance, filteringOrder, tourType in - guard - let coordinate = coordinate, - let tourType = tourType - else { return } - - KoreaTravelingManager.shared.fetchLocationBasedTourismInformation( - api: .locationBasedTourismInformation( - latitude: coordinate.latitude, - longitude: coordinate.longitude, - radius: filteringDistance.rawValue, - arrange: filteringOrder.sortingCode, - contentTypeId: tourType.tourTypeCode - ) - ) { [weak self] touristDestinationList in - guard let weakSelf = self else { return } - - let searchResulDataList = weakSelf.generateSearchResulDataList(touristDestinationList) - - weakSelf.outputLocationBasedTouristDestinationList.value = searchResulDataList - } - } - - inputAddNewBookmark.bind { locationBasedTouristDestination in - guard - let locationBasedTouristDestination = locationBasedTouristDestination - else { return } - - let filteredBookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).filter { bookmark in - bookmark.contentId == locationBasedTouristDestination.contentid - } - - if filteredBookmarkList.count >= 1 { - print("이미 존재하는 북마크입니다.") - return - } - - let newBookmark = Bookmark( - contentId: locationBasedTouristDestination.contentid, - contentTypeId: locationBasedTouristDestination.contenttypeid, - title: locationBasedTouristDestination.title, - imageURL: locationBasedTouristDestination.firstimage, - region: locationBasedTouristDestination.areacode - ) - - RealmManager.shared.write(newBookmark) - RealmManager.shared.getLocationOfDefaultRealm() - } - - inputRemoveBookmark.bind { locationBasedTouristDestination in - guard - let locationBasedTouristDestination = locationBasedTouristDestination - else { return } - - let bookmarkList = RealmManager.shared.read(Bookmark.self) - - let filteredBookmarkList: [Bookmark] = bookmarkList.filter { bookmark in - bookmark.contentId == locationBasedTouristDestination.contentid - } - - if filteredBookmarkList.count < 1 { - print("존재하지 않는 북마크입니다.") - return - } - - RealmManager.shared.delete(filteredBookmarkList[0]) - } - - inputContentVCTableViewDidSelectRowAtTrigger.bind { [weak self] touristDestination in - guard let weakSelf = self else { return } - - guard let touristDestination = touristDestination else { return } - - weakSelf.outputSelectedTouristDestination.value = touristDestination - } - - inputSearchVCTableViewDidSelectRowAt.bind { [weak self] selectedTouristDestination in - guard let weakSelf = self else { return } - - weakSelf.outputTappedTouristDestination.value = selectedTouristDestination - } - - inputDetailVCLeftBarButtonItemTappedTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.outputDetailVCLeftBarButtonItemTappedTrigger.value = trigger - } - - inputDetailVCViewDidLoadTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.outputDetailVCViewDidLoadTrigger.value = trigger - } - - inputDetailVCAddressCellTappTrigger.bind { [weak self] latitude, longitude in - guard let weakSelf = self else { return } - - guard - let latitude = latitude, - let longitude = longitude - else { return } - - weakSelf.outputDetailVCAddressCellTappTrigger.value = (latitude, longitude) - } - - inputActivityIndicatorStopTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.outputActivityIndicatorStopTrigger.value = trigger - } - - inputActivityIndicatorStartTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - weakSelf.outputActivityIndicatorStartTrigger.value = trigger - } - - inputUpdateUserCurrentLocationTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - LocationManager.shared.startUpdatingLocation() - } - } -} - -extension MainViewModel { - private func generateSearchResulDataList(_ touristDestinationList: [LBItem] = []) -> [SearchResulData] { - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - - return touristDestinationList.map { lbItem in - let filteredBookmarkList = bookmarkList.filter { lbItem.contentid == $0.contentId } - - if filteredBookmarkList.count >= 1 { - return SearchResulData( - locationBasedTouristDestination: lbItem, - isBookmarked: true - ) - } else { - return SearchResulData( - locationBasedTouristDestination: lbItem, - isBookmarked: false - ) - } - } - } -} diff --git a/KoreaOneStep/ViewModels/MainViewModel2.swift b/KoreaOneStep/ViewModels/MainViewModel2.swift deleted file mode 100644 index 770ae15..0000000 --- a/KoreaOneStep/ViewModels/MainViewModel2.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// MainViewModel2.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 8/21/24. -// - -import Foundation -import RxSwift -import RxCocoa -import CoreLocation - -final class MainViewModel2: ViewModelType { - - let indicatorTriggerRelay = BehaviorRelay(value: false) - let locationBasedTouristDestinationListRelay = BehaviorRelay<[SearchResulData]>(value: []) - let userLocationInfoRelay = BehaviorRelay(value: nil) - - var disposeBag = DisposeBag() - - struct Input { - let viewDidLoad: Observable - } - - struct Output { - let showAlertTriggerForAuthorization: Driver - } - - func transform(input: Input) -> Output { - let showAlertTriggerForAuthorizationRelay = PublishRelay() - let searchLocationBasedTourismInfosTrigger = PublishSubject() - - searchLocationBasedTourismInfosTrigger - .flatMap { coordinate in - KoreaTravelingManager.shared.fetchLocationBasedTourismInformation( - api: .locationBasedTourismInformation( - latitude: coordinate.latitude, - longitude: coordinate.longitude, - radius: KoreaTravelingAPI.radiusDefaultValue, - arrange: KoreaTravelingAPI.arrageDefaultValue, - contentTypeId: KoreaTravelingAPI.contentTypeIdDefaultValue - ) - ) - } - .bind(with: self) { owner, touristDestinationList in - owner.locationBasedTouristDestinationListRelay.accept(owner.generateSearchResulDataList(touristDestinationList)) - } - .disposed(by: disposeBag) - - input.viewDidLoad - .subscribe(with: self) { owner, _ in - LocationManager.shared.fetchLocation { [weak self] coordinate, error, isDenied in - guard let weakSelf = self else { return } - - guard error == nil else { - print("위치 찾기 에러 발생: ", error) - return - } - - guard !isDenied else { - print("Denied") - weakSelf.userLocationInfoRelay.accept(nil) - showAlertTriggerForAuthorizationRelay.accept(isDenied) - return - } - - guard let coordinate = coordinate else { - print("Something is wrong.") - return - } - - owner.userLocationInfoRelay.accept(coordinate) - searchLocationBasedTourismInfosTrigger.onNext(coordinate) - } - } - .disposed(by: disposeBag) - - return Output( - showAlertTriggerForAuthorization: showAlertTriggerForAuthorizationRelay.asDriver(onErrorJustReturn: false) - ) - } -} - -extension MainViewModel2 { - private func generateSearchResulDataList(_ touristDestinationList: [LBItem] = []) -> [SearchResulData] { - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - - return touristDestinationList.map { lbItem in - let filteredBookmarkList = bookmarkList.filter { lbItem.contentid == $0.contentId } - - if filteredBookmarkList.count >= 1 { - return SearchResulData( - locationBasedTouristDestination: lbItem, - isBookmarked: true - ) - } else { - return SearchResulData( - locationBasedTouristDestination: lbItem, - isBookmarked: false - ) - } - } - } -} diff --git a/KoreaOneStep/ViewModels/SearchViewModel.swift b/KoreaOneStep/ViewModels/SearchViewModel.swift deleted file mode 100644 index 36fd810..0000000 --- a/KoreaOneStep/ViewModels/SearchViewModel.swift +++ /dev/null @@ -1,118 +0,0 @@ -// -// SearchViewModel.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 3/18/24. -// - -import Foundation - -final class SearchViewModel { - let inputViewDidLoadTrigger: CustomObservable = CustomObservable(nil) - let inputFilterVCViewDidLoadTrigger: CustomObservable = CustomObservable(nil) - let inputSelectedRegionTag: CustomObservable = CustomObservable(nil) - let inputSelectedSiGunGuTag: CustomObservable = CustomObservable(nil) - let inputSearchElements: CustomObservable<(String?, ACItem?, ACItem?)> = CustomObservable((nil, nil, nil)) - let inputXmarkButtonTapTrigger: CustomObservable = CustomObservable(nil) - - let outputRecentKeywordList: CustomObservable<[RecentKeyword]> = CustomObservable([]) - let outputAreaCodeList: CustomObservable<[ACItem]> = CustomObservable([]) - let outputSiGunGuCodeList: CustomObservable<[ACItem]> = CustomObservable([]) - let outputSearchedResultList: CustomObservable<[KSItem]?> = CustomObservable(nil) - let outputSelectedRegionTag: CustomObservable = CustomObservable(nil) - let outputSelectedSiGunGu: CustomObservable = CustomObservable(nil) - - init() { - inputViewDidLoadTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - let recentKeywordList: [RecentKeyword] = RealmManager.shared.read(RecentKeyword.self).sorted(byKeyPath: "regDate", ascending: false).map { $0 } - weakSelf.outputRecentKeywordList.value = recentKeywordList - } - - inputFilterVCViewDidLoadTrigger.bind { [weak self] trigger in - guard let weakSelf = self else { return } - - guard let trigger = trigger else { return } - - KoreaTravelingManager.shared.fetchAreaCode( - api: .areaCode(areaCode: KoreaTravelingAPI.areaCodeDefaultValue) - ) { areaCodeList in - weakSelf.outputAreaCodeList.value = areaCodeList - } - } - - inputSelectedRegionTag.bind { [weak self] regionTag in - guard let weakSelf = self else { return } - - guard let regionTag = regionTag else { return } - - weakSelf.outputSelectedRegionTag.value = regionTag - - KoreaTravelingManager.shared.fetchAreaCode( - api: .areaCode(areaCode: regionTag.code) - ) { siGunGuCodeList in - - weakSelf.outputSiGunGuCodeList.value = siGunGuCodeList - } - } - - inputSelectedSiGunGuTag.bind { [weak self] siGunGuTag in - guard let weakSelf = self else { return } - - guard let siGunGuTag = siGunGuTag else { return } - - weakSelf.outputSelectedSiGunGu.value = siGunGuTag - } - - inputSearchElements.bind { [weak self] searchText, selectedRegion, selectedSiGunGu in - - guard let searchText = searchText else { return } - - print("searchText", searchText) - print("selectedRegion", selectedRegion) - print("selectedSiGunGu", selectedSiGunGu) - - KoreaTravelingManager.shared.fetchKeywordBasedSearching( - api: .keywordBasedSearching( - keyword: searchText, - areaCode: selectedRegion == nil ? "" : selectedRegion!.code, - sigunguCode: selectedSiGunGu == nil ? "" : selectedSiGunGu!.code - ) - ) { searchedResultList in - guard let weakSelf = self else { return } - - weakSelf.outputSearchedResultList.value = searchedResultList - - if searchedResultList.count >= 1 { - // 키워드가 중복 저장되었는지 확인 - let filteredRecentKeywordList: [RecentKeyword] = RealmManager.shared.read(RecentKeyword.self).filter { $0.keyword == searchText } - - if filteredRecentKeywordList.count < 1 { - let newRecentKeyword = RecentKeyword(keyword: searchText) - RealmManager.shared.getLocationOfDefaultRealm() - RealmManager.shared.write(newRecentKeyword) - - let recentKeywordList: [RecentKeyword] = RealmManager.shared.read(RecentKeyword.self).sorted(byKeyPath: "regDate", ascending: false).map { $0 } - - weakSelf.outputRecentKeywordList.value = recentKeywordList - } - } - } - } - - inputXmarkButtonTapTrigger.bind { [weak self] recentKeyword in - guard let weakSelf = self else { return } - - guard let recentKeyword = recentKeyword else { return } - - RealmManager.shared.delete(recentKeyword) - - let updatedRecentKeywordList: [RecentKeyword] = RealmManager.shared.read(RecentKeyword.self).sorted(byKeyPath: "regDate", ascending: false).map { $0 } - - weakSelf.outputRecentKeywordList.value = updatedRecentKeywordList - } - } -} diff --git a/KoreaOneStep/ViewModels/SettingViewModel.swift b/KoreaOneStep/ViewModels/SettingViewModel.swift deleted file mode 100644 index d0384d3..0000000 --- a/KoreaOneStep/ViewModels/SettingViewModel.swift +++ /dev/null @@ -1,49 +0,0 @@ -// -// SettingViewModel2.swift -// KoreaOneStep -// -// Created by SUCHAN CHANG on 8/17/24. -// - -import Foundation -import RxSwift -import RxCocoa - -typealias SettingTableViewCellTapType = (ControlEvent.Element, ControlEvent.Element) - -final class SettingViewModel: ViewModelType { - - var disposeBag = DisposeBag() - - struct Input { - let itemTapped: Observable - } - - struct Output { - let settingTableViewCellTitles: Driver<[SettingTableViewCellTitle]> - let removeAllBookmarksToastMessage: Driver - } - - func transform(input: Input) -> Output { - - let removeAllBookmarksToastMessage = PublishRelay() - - input.itemTapped - .bind { _ in - let bookmarkList: [Bookmark] = RealmManager.shared.read(Bookmark.self).map { $0 } - - if bookmarkList.count >= 1 { - let toastMessage = RealmManager.shared.deleteAll() - removeAllBookmarksToastMessage.accept(toastMessage) - return - } - removeAllBookmarksToastMessage.accept(ToastMessage.Failure.noBookmarkContents) - } - .disposed(by: disposeBag) - - return Output( - settingTableViewCellTitles: Observable.just(SettingTableViewCellTitle.allCases).asDriver(onErrorJustReturn: []), - removeAllBookmarksToastMessage: removeAllBookmarksToastMessage.asDriver(onErrorJustReturn: "") - ) - } -}