diff --git a/README.md b/README.md index c04306379..ac5153d87 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,8 @@ # sidedish -그룹프로젝트 #2 + + + +## 시퀀스 다이어그램(main 중 item 부분) + +![Sidedish(Team-01)-2](https://user-images.githubusercontent.com/62657991/116674117-35c77f80-a9df-11eb-950f-b36e531a9832.png) + diff --git a/Sidedish/Pods/Pods.xcodeproj/project.pbxproj b/Sidedish/Pods/Pods.xcodeproj/project.pbxproj index 09c5e894b..29f07f51c 100644 --- a/Sidedish/Pods/Pods.xcodeproj/project.pbxproj +++ b/Sidedish/Pods/Pods.xcodeproj/project.pbxproj @@ -193,7 +193,7 @@ 5546AC3CDDAF7DC0CFA3796AE0DA8677 /* KingfisherOptionsInfo.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = KingfisherOptionsInfo.swift; path = Sources/General/KingfisherOptionsInfo.swift; sourceTree = ""; }; 5A18CDCBC3FBFBFAC9A819B84F9CFB65 /* Alamofire.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.debug.xcconfig; sourceTree = ""; }; 5BEB8A00C858140301ECFBB90CBB4B68 /* DispatchQueue+Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "DispatchQueue+Alamofire.swift"; path = "Source/DispatchQueue+Alamofire.swift"; sourceTree = ""; }; - 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Alamofire.framework; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 5D797E9A5C5782CE845840781FA1CC81 /* Alamofire.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Alamofire.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 61CF2BA135DF65D19254FAB23927CE05 /* Toaster-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Toaster-Info.plist"; sourceTree = ""; }; 62FAA0A50F09BFD404E9D350559D4E8F /* ParameterEncoder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ParameterEncoder.swift; path = Source/ParameterEncoder.swift; sourceTree = ""; }; 684C336F2524AEFA1191AD5F30B0DF0C /* Pods-Sidedish-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-Sidedish-Info.plist"; sourceTree = ""; }; @@ -223,7 +223,7 @@ 997261987EDE5BE1A2280361D7C95E71 /* Alamofire-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Alamofire-Info.plist"; sourceTree = ""; }; 9BC532BC44D73F9DE3F0EF44AC20DC13 /* GIFAnimatedImage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = GIFAnimatedImage.swift; path = Sources/Image/GIFAnimatedImage.swift; sourceTree = ""; }; 9C4F6D1A3FD12E4C53D9709D96A9FBB7 /* MultipartUpload.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = MultipartUpload.swift; path = Source/MultipartUpload.swift; sourceTree = ""; }; - 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 9E3A56BADDEDBB209293D988CFD01216 /* RequestTaskMap.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RequestTaskMap.swift; path = Source/RequestTaskMap.swift; sourceTree = ""; }; 9E42319F10ABA3D99672EC9EF9819E39 /* Pods-Sidedish.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-Sidedish.modulemap"; sourceTree = ""; }; A040238BA51D7DF819BB51F69A89E1A1 /* ServerTrustEvaluation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ServerTrustEvaluation.swift; path = Source/ServerTrustEvaluation.swift; sourceTree = ""; }; @@ -243,13 +243,13 @@ B771D69358C190D0AB2DA3E6D9EE6519 /* ImageDataProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageDataProvider.swift; path = Sources/General/ImageSource/ImageDataProvider.swift; sourceTree = ""; }; BB94DF5CBF98397E51578A800B43E1EF /* DiskStorage.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = DiskStorage.swift; path = Sources/Cache/DiskStorage.swift; sourceTree = ""; }; BC20583584018C67D45C798EFD4AAD84 /* Source.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Source.swift; path = Sources/General/ImageSource/Source.swift; sourceTree = ""; }; - BD810337F4A305D60D9250A197AA1EF1 /* Toaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Toaster.framework; path = Toaster.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + BD810337F4A305D60D9250A197AA1EF1 /* Toaster.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Toaster.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C033C73405484F32AEF29BBD00E7B5E7 /* ImageBinder.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ImageBinder.swift; path = Sources/SwiftUI/ImageBinder.swift; sourceTree = ""; }; C0D58798A502D2E85339C27E6EF936EA /* Pods-Sidedish-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-Sidedish-dummy.m"; sourceTree = ""; }; C39A1975FAC8DD8A0B77DA0175884F60 /* Alamofire-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Alamofire-dummy.m"; sourceTree = ""; }; C39FB42703628A574936FED6212CF8AD /* Combine.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Combine.swift; path = Source/Combine.swift; sourceTree = ""; }; C3C4379B6A7CFEABF7AC18F1AA02E244 /* Session.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Session.swift; path = Source/Session.swift; sourceTree = ""; }; - C3F44C782D64D7EB20B61CE3844EBFAD /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Kingfisher.framework; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + C3F44C782D64D7EB20B61CE3844EBFAD /* Kingfisher.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Kingfisher.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C586303DC54D2EF1D453459E4B613B14 /* Toaster-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Toaster-prefix.pch"; sourceTree = ""; }; C610AAF08375AA97F81F6A47FA83FA21 /* SessionDelegate.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SessionDelegate.swift; path = Source/SessionDelegate.swift; sourceTree = ""; }; C763355C5B15DDD66703826D17E4FCF7 /* Validation.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Validation.swift; path = Source/Validation.swift; sourceTree = ""; }; @@ -279,7 +279,7 @@ F7CA3B74E19BDE99939A4D425AE39974 /* URLConvertible+URLRequestConvertible.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "URLConvertible+URLRequestConvertible.swift"; path = "Source/URLConvertible+URLRequestConvertible.swift"; sourceTree = ""; }; F7E967A235964DEBAA8A7FFE7531915B /* Kingfisher-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Kingfisher-Info.plist"; sourceTree = ""; }; FA2E1604B6CE42044FC7EB9980CB821D /* RedirectHandler.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = RedirectHandler.swift; path = Source/RedirectHandler.swift; sourceTree = ""; }; - FC47262A97C2A5D7D86D84DE91EB7EB8 /* Pods_Sidedish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_Sidedish.framework; path = "Pods-Sidedish.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + FC47262A97C2A5D7D86D84DE91EB7EB8 /* Pods_Sidedish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sidedish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; FD00DB24D8F615B552494FE0AA0EC7B0 /* Alamofire.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = Alamofire.swift; path = Source/Alamofire.swift; sourceTree = ""; }; FDE3F6EFAAE97D0E7AAC53E66679D94C /* String+MD5.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = "String+MD5.swift"; path = "Sources/Utility/String+MD5.swift"; sourceTree = ""; }; FE0FE30335745175CBE81E8E748272FE /* Alamofire.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Alamofire.release.xcconfig; sourceTree = ""; }; @@ -368,7 +368,6 @@ C763355C5B15DDD66703826D17E4FCF7 /* Validation.swift */, AA904C0E360C01CF6A64CD9C8EEF9D36 /* Support Files */, ); - name = Alamofire; path = Alamofire; sourceTree = ""; }; @@ -424,7 +423,6 @@ 2109DAFA24975C11DC1DA375998FB3AF /* UIApplication+Load.swift */, E7DBEFDE50EE6311046FF21D3EFF236F /* Support Files */, ); - name = Toaster; path = Toaster; sourceTree = ""; }; @@ -570,7 +568,6 @@ EE430722E5EE22546D304DE46B09BE4D /* WKInterfaceImage+Kingfisher.swift */, EE4FEAB3A75C042BC4AE38529B3AD491 /* Support Files */, ); - name = Kingfisher; path = Kingfisher; sourceTree = ""; }; @@ -695,7 +692,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1100; - LastUpgradeCheck = 1100; + LastUpgradeCheck = 1240; }; buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; compatibilityVersion = "Xcode 10.0"; @@ -917,7 +914,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Toaster/Toaster-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Toaster/Toaster-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -952,7 +949,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -987,7 +984,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Kingfisher/Kingfisher-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1151,7 +1148,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Kingfisher/Kingfisher-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Kingfisher/Kingfisher-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1186,7 +1183,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Alamofire/Alamofire-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Alamofire/Alamofire-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1258,7 +1255,7 @@ GCC_PREFIX_HEADER = "Target Support Files/Toaster/Toaster-prefix.pch"; INFOPLIST_FILE = "Target Support Files/Toaster/Toaster-Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/ador.xcuserdatad/xcschemes/xcschememanagement.plist b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/ador.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 04932d5b2..000000000 --- a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/ador.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,29 +0,0 @@ - - - - - SchemeUserState - - Alamofire.xcscheme_^#shared#^_ - - orderHint - 4 - - Kingfisher.xcscheme_^#shared#^_ - - orderHint - 0 - - Pods-Sidedish.xcscheme_^#shared#^_ - - orderHint - 1 - - Toaster.xcscheme_^#shared#^_ - - orderHint - 3 - - - - diff --git a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Alamofire.xcscheme b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Alamofire.xcscheme index bc06c13ed..120775c60 100644 --- a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Alamofire.xcscheme +++ b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Alamofire.xcscheme @@ -1,17 +1,17 @@ + buildForArchiving = "YES" + buildForAnalyzing = "YES"> - - + shouldUseLaunchSchemeArgsEnv = "YES"> + + - - + debugDocumentVersioning = "YES"> diff --git a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Kingfisher.xcscheme b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Kingfisher.xcscheme index 32d098a4a..0074170f7 100644 --- a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Kingfisher.xcscheme +++ b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Kingfisher.xcscheme @@ -1,17 +1,17 @@ + buildForArchiving = "YES" + buildForAnalyzing = "YES"> - - + shouldUseLaunchSchemeArgsEnv = "YES"> + + - - + debugDocumentVersioning = "YES"> diff --git a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Pods-Sidedish.xcscheme b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Pods-Sidedish.xcscheme index cef84a694..f28bc084a 100644 --- a/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Pods-Sidedish.xcscheme +++ b/Sidedish/Pods/Pods.xcodeproj/xcuserdata/issac.xcuserdatad/xcschemes/Pods-Sidedish.xcscheme @@ -1,6 +1,6 @@ + buildForArchiving = "YES" + buildForAnalyzing = "YES"> - - + shouldUseLaunchSchemeArgsEnv = "YES"> + + - - + debugDocumentVersioning = "YES"> diff --git a/Sidedish/Sidedish.xcodeproj/project.pbxproj b/Sidedish/Sidedish.xcodeproj/project.pbxproj index e837f38cd..74e4c638d 100644 --- a/Sidedish/Sidedish.xcodeproj/project.pbxproj +++ b/Sidedish/Sidedish.xcodeproj/project.pbxproj @@ -7,7 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 1C6FD9F8FE4DF3D612C7FC78 /* Pods_Sidedish.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 69AD776A2215C9567BE9325D /* Pods_Sidedish.framework */; }; + 220B06922637D7D5009FAEFF /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 220B06912637D7D5009FAEFF /* DetailViewModel.swift */; }; + 222E3DB32636936600CB8330 /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 222E3DB22636936600CB8330 /* Category.swift */; }; 2240D5AC262D74890077EF4D /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2240D5AB262D74890077EF4D /* DetailViewController.swift */; }; 2240D5B0262D7E500077EF4D /* ItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2240D5AF262D7E500077EF4D /* ItemViewModel.swift */; }; 22418742262D4D8300AC14C7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22418741262D4D8300AC14C7 /* AppDelegate.swift */; }; @@ -18,18 +19,25 @@ 2241874E262D4D8600AC14C7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2241874C262D4D8600AC14C7 /* LaunchScreen.storyboard */; }; 22418761262D5C8400AC14C7 /* HeaderCollectionReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22418760262D5C8400AC14C7 /* HeaderCollectionReusableView.swift */; }; 22418771262D6A5A00AC14C7 /* SidedishItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22418770262D6A5A00AC14C7 /* SidedishItem.swift */; }; - 22418774262D6B3400AC14C7 /* Category.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22418773262D6B3400AC14C7 /* Category.swift */; }; 2245D88E262D929600E0B637 /* SidedishNetworkCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2245D88D262D929600E0B637 /* SidedishNetworkCenter.swift */; }; 2260DDDA262D960E002DF3E7 /* SidedishProcessing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2260DDD9262D960E002DF3E7 /* SidedishProcessing.swift */; }; + 228295BC263780B000BBDACD /* DetailItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228295BB263780B000BBDACD /* DetailItem.swift */; }; + 228295C32637813C00BBDACD /* Detail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 228295C22637813C00BBDACD /* Detail.swift */; }; 2291A1A926326FD6007A1B72 /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2291A1A826326FD6007A1B72 /* Badge.swift */; }; + 22A61640263A536D00E78717 /* ImageCacheCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22A6163F263A536D00E78717 /* ImageCacheCenter.swift */; }; 22BBE64E262D9FCC001D06D5 /* SidedishOfCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22BBE64D262D9FCC001D06D5 /* SidedishOfCategory.swift */; }; 22D5024A2633049700325C5F /* String+NSAttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22D502492633049700325C5F /* String+NSAttributedString.swift */; }; 22EBA5662633B332009611CB /* HeaderViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22EBA5652633B332009611CB /* HeaderViewModel.swift */; }; + A46CDD6D47430A384FEB7271 /* Pods_Sidedish.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2266576CDB07714024C22C84 /* Pods_Sidedish.framework */; }; D06C739626310CD3002404A3 /* BadgeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D06C739526310CD3002404A3 /* BadgeLabel.swift */; }; D08A5398262D587700DD1CBE /* ItemCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08A5397262D587700DD1CBE /* ItemCollectionViewCell.swift */; }; + D0C1A7112637F95B005F6113 /* CustomView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D0C1A7102637F95B005F6113 /* CustomView.xib */; }; + D0C1A7142637F9A7005F6113 /* CustomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0C1A7132637F9A7005F6113 /* CustomView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 220B06912637D7D5009FAEFF /* DetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewModel.swift; sourceTree = ""; }; + 222E3DB22636936600CB8330 /* Category.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = ""; }; 2240D5AB262D74890077EF4D /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = ""; }; 2240D5AF262D7E500077EF4D /* ItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemViewModel.swift; sourceTree = ""; }; 2241873E262D4D8300AC14C7 /* Sidedish.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sidedish.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -42,18 +50,22 @@ 2241874F262D4D8600AC14C7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 22418760262D5C8400AC14C7 /* HeaderCollectionReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderCollectionReusableView.swift; sourceTree = ""; }; 22418770262D6A5A00AC14C7 /* SidedishItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidedishItem.swift; sourceTree = ""; }; - 22418773262D6B3400AC14C7 /* Category.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Category.swift; sourceTree = ""; }; 2245D88D262D929600E0B637 /* SidedishNetworkCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidedishNetworkCenter.swift; sourceTree = ""; }; 2260DDD9262D960E002DF3E7 /* SidedishProcessing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidedishProcessing.swift; sourceTree = ""; }; + 2266576CDB07714024C22C84 /* Pods_Sidedish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sidedish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 228295BB263780B000BBDACD /* DetailItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailItem.swift; sourceTree = ""; }; + 228295C22637813C00BBDACD /* Detail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Detail.swift; sourceTree = ""; }; 2291A1A826326FD6007A1B72 /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = ""; }; + 22A6163F263A536D00E78717 /* ImageCacheCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCacheCenter.swift; sourceTree = ""; }; 22BBE64D262D9FCC001D06D5 /* SidedishOfCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidedishOfCategory.swift; sourceTree = ""; }; 22D502492633049700325C5F /* String+NSAttributedString.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+NSAttributedString.swift"; sourceTree = ""; }; 22EBA5652633B332009611CB /* HeaderViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderViewModel.swift; sourceTree = ""; }; - 499E51924357C5AFEF94978D /* Pods-Sidedish.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sidedish.release.xcconfig"; path = "Target Support Files/Pods-Sidedish/Pods-Sidedish.release.xcconfig"; sourceTree = ""; }; - 69AD776A2215C9567BE9325D /* Pods_Sidedish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Sidedish.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 71EC35DD2BAA44FFBB2A183D /* Pods-Sidedish.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sidedish.debug.xcconfig"; path = "Target Support Files/Pods-Sidedish/Pods-Sidedish.debug.xcconfig"; sourceTree = ""; }; D06C739526310CD3002404A3 /* BadgeLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeLabel.swift; sourceTree = ""; }; D08A5397262D587700DD1CBE /* ItemCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemCollectionViewCell.swift; sourceTree = ""; }; - F6FF97B8F04F0E1EF7926092 /* Pods-Sidedish.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sidedish.debug.xcconfig"; path = "Target Support Files/Pods-Sidedish/Pods-Sidedish.debug.xcconfig"; sourceTree = ""; }; + D0C1A7102637F95B005F6113 /* CustomView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CustomView.xib; sourceTree = ""; }; + D0C1A7132637F9A7005F6113 /* CustomView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomView.swift; sourceTree = ""; }; + E7B0F586A26D135247BB28E9 /* Pods-Sidedish.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Sidedish.release.xcconfig"; path = "Target Support Files/Pods-Sidedish/Pods-Sidedish.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -61,7 +73,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1C6FD9F8FE4DF3D612C7FC78 /* Pods_Sidedish.framework in Frameworks */, + A46CDD6D47430A384FEB7271 /* Pods_Sidedish.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -71,8 +83,8 @@ 1D84EDAD3656EE6C148133E1 /* Pods */ = { isa = PBXGroup; children = ( - F6FF97B8F04F0E1EF7926092 /* Pods-Sidedish.debug.xcconfig */, - 499E51924357C5AFEF94978D /* Pods-Sidedish.release.xcconfig */, + 71EC35DD2BAA44FFBB2A183D /* Pods-Sidedish.debug.xcconfig */, + E7B0F586A26D135247BB28E9 /* Pods-Sidedish.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -81,6 +93,7 @@ isa = PBXGroup; children = ( 2240D5AF262D7E500077EF4D /* ItemViewModel.swift */, + 220B06912637D7D5009FAEFF /* DetailViewModel.swift */, 22EBA5652633B332009611CB /* HeaderViewModel.swift */, ); path = ViewModel; @@ -89,10 +102,13 @@ 2204E946262EC5A2008102BD /* View */ = { isa = PBXGroup; children = ( + D0C1A7102637F95B005F6113 /* CustomView.xib */, + D0C1A7132637F9A7005F6113 /* CustomView.swift */, 22418747262D4D8300AC14C7 /* Main.storyboard */, D08A5397262D587700DD1CBE /* ItemCollectionViewCell.swift */, 22418760262D5C8400AC14C7 /* HeaderCollectionReusableView.swift */, D06C739526310CD3002404A3 /* BadgeLabel.swift */, + 22E32CC226392CF200EE3AF8 /* extension */, ); path = View; sourceTree = ""; @@ -103,7 +119,7 @@ 22418740262D4D8300AC14C7 /* Sidedish */, 2241873F262D4D8300AC14C7 /* Products */, 1D84EDAD3656EE6C148133E1 /* Pods */, - C86369EE41D09B45D38A333E /* Frameworks */, + E82A359D5367162E8BD6F247 /* Frameworks */, ); sourceTree = ""; }; @@ -126,9 +142,9 @@ 2204E946262EC5A2008102BD /* View */, 2245D88C262D927200E0B637 /* UseCase */, 2245D890262D92D700E0B637 /* Repository */, + 22A6163E263A531600E78717 /* Persistence */, 22418776262D6C1700AC14C7 /* Entity */, 2241874C262D4D8600AC14C7 /* LaunchScreen.storyboard */, - 22D502492633049700325C5F /* String+NSAttributedString.swift */, 2241874F262D4D8600AC14C7 /* Info.plist */, 2241874A262D4D8600AC14C7 /* Assets.xcassets */, ); @@ -140,7 +156,8 @@ children = ( 22BBE64D262D9FCC001D06D5 /* SidedishOfCategory.swift */, 22418770262D6A5A00AC14C7 /* SidedishItem.swift */, - 22418773262D6B3400AC14C7 /* Category.swift */, + 228295BB263780B000BBDACD /* DetailItem.swift */, + 228295C22637813C00BBDACD /* Detail.swift */, 2291A1AB26326FDB007A1B72 /* Enum */, ); path = Entity; @@ -166,14 +183,31 @@ isa = PBXGroup; children = ( 2291A1A826326FD6007A1B72 /* Badge.swift */, + 222E3DB22636936600CB8330 /* Category.swift */, ); path = Enum; sourceTree = ""; }; - C86369EE41D09B45D38A333E /* Frameworks */ = { + 22A6163E263A531600E78717 /* Persistence */ = { + isa = PBXGroup; + children = ( + 22A6163F263A536D00E78717 /* ImageCacheCenter.swift */, + ); + path = Persistence; + sourceTree = ""; + }; + 22E32CC226392CF200EE3AF8 /* extension */ = { + isa = PBXGroup; + children = ( + 22D502492633049700325C5F /* String+NSAttributedString.swift */, + ); + path = extension; + sourceTree = ""; + }; + E82A359D5367162E8BD6F247 /* Frameworks */ = { isa = PBXGroup; children = ( - 69AD776A2215C9567BE9325D /* Pods_Sidedish.framework */, + 2266576CDB07714024C22C84 /* Pods_Sidedish.framework */, ); name = Frameworks; sourceTree = ""; @@ -185,11 +219,11 @@ isa = PBXNativeTarget; buildConfigurationList = 22418752262D4D8600AC14C7 /* Build configuration list for PBXNativeTarget "Sidedish" */; buildPhases = ( - 99EF1A974FC5FDCCD4C181F2 /* [CP] Check Pods Manifest.lock */, + D56790A355E7FA67B504169D /* [CP] Check Pods Manifest.lock */, 2241873A262D4D8300AC14C7 /* Sources */, 2241873B262D4D8300AC14C7 /* Frameworks */, 2241873C262D4D8300AC14C7 /* Resources */, - 51DCBCA7A1C26592512CB930 /* [CP] Embed Pods Frameworks */, + 022FB5EAFC5AD6AF8F5F1709 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -240,13 +274,14 @@ 2241874E262D4D8600AC14C7 /* LaunchScreen.storyboard in Resources */, 2241874B262D4D8600AC14C7 /* Assets.xcassets in Resources */, 22418749262D4D8300AC14C7 /* Main.storyboard in Resources */, + D0C1A7112637F95B005F6113 /* CustomView.xib in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 51DCBCA7A1C26592512CB930 /* [CP] Embed Pods Frameworks */ = { + 022FB5EAFC5AD6AF8F5F1709 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -263,7 +298,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Sidedish/Pods-Sidedish-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 99EF1A974FC5FDCCD4C181F2 /* [CP] Check Pods Manifest.lock */ = { + D56790A355E7FA67B504169D /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -294,17 +329,22 @@ files = ( D06C739626310CD3002404A3 /* BadgeLabel.swift in Sources */, 22D5024A2633049700325C5F /* String+NSAttributedString.swift in Sources */, + 228295C32637813C00BBDACD /* Detail.swift in Sources */, + D0C1A7142637F9A7005F6113 /* CustomView.swift in Sources */, 2291A1A926326FD6007A1B72 /* Badge.swift in Sources */, 22418746262D4D8300AC14C7 /* ViewController.swift in Sources */, 22EBA5662633B332009611CB /* HeaderViewModel.swift in Sources */, D08A5398262D587700DD1CBE /* ItemCollectionViewCell.swift in Sources */, + 220B06922637D7D5009FAEFF /* DetailViewModel.swift in Sources */, 22418742262D4D8300AC14C7 /* AppDelegate.swift in Sources */, 22418771262D6A5A00AC14C7 /* SidedishItem.swift in Sources */, 22418761262D5C8400AC14C7 /* HeaderCollectionReusableView.swift in Sources */, 2240D5B0262D7E500077EF4D /* ItemViewModel.swift in Sources */, + 228295BC263780B000BBDACD /* DetailItem.swift in Sources */, + 22A61640263A536D00E78717 /* ImageCacheCenter.swift in Sources */, 2245D88E262D929600E0B637 /* SidedishNetworkCenter.swift in Sources */, + 222E3DB32636936600CB8330 /* Category.swift in Sources */, 22418744262D4D8300AC14C7 /* SceneDelegate.swift in Sources */, - 22418774262D6B3400AC14C7 /* Category.swift in Sources */, 2240D5AC262D74890077EF4D /* DetailViewController.swift in Sources */, 22BBE64E262D9FCC001D06D5 /* SidedishOfCategory.swift in Sources */, 2260DDDA262D960E002DF3E7 /* SidedishProcessing.swift in Sources */, @@ -451,7 +491,7 @@ }; 22418753262D4D8600AC14C7 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = F6FF97B8F04F0E1EF7926092 /* Pods-Sidedish.debug.xcconfig */; + baseConfigurationReference = 71EC35DD2BAA44FFBB2A183D /* Pods-Sidedish.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -463,6 +503,7 @@ ); PRODUCT_BUNDLE_IDENTIFIER = kr.maylily.Sidedish; PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = singlefile; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -470,7 +511,7 @@ }; 22418754262D4D8600AC14C7 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 499E51924357C5AFEF94978D /* Pods-Sidedish.release.xcconfig */; + baseConfigurationReference = E7B0F586A26D135247BB28E9 /* Pods-Sidedish.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; diff --git a/Sidedish/Sidedish.xcworkspace/xcuserdata/ador.xcuserdatad/UserInterfaceState.xcuserstate b/Sidedish/Sidedish.xcworkspace/xcuserdata/ador.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 4154b7750..000000000 Binary files a/Sidedish/Sidedish.xcworkspace/xcuserdata/ador.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/Sidedish/Sidedish.xcworkspace/xcuserdata/issac.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/Sidedish/Sidedish.xcworkspace/xcuserdata/issac.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index 4e2ed4a8b..ed32b4376 100644 --- a/Sidedish/Sidedish.xcworkspace/xcuserdata/issac.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/Sidedish/Sidedish.xcworkspace/xcuserdata/issac.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -1,6 +1,6 @@ diff --git a/Sidedish/Sidedish/DetailViewController.swift b/Sidedish/Sidedish/DetailViewController.swift index 519ca8d00..15f63581f 100644 --- a/Sidedish/Sidedish/DetailViewController.swift +++ b/Sidedish/Sidedish/DetailViewController.swift @@ -6,16 +6,157 @@ // import UIKit +import Toaster class DetailViewController: UIViewController { + @IBOutlet weak var thumbnailScrollView: UIScrollView! + @IBOutlet weak var informationStackView: UIStackView! + @IBOutlet weak var thumbnailStackView: UIStackView! + + var customView: CustomView! + var detailViewModel: DetailViewModel! override func viewDidLoad() { super.viewDidLoad() - + self.bind() + NotificationCenter.default.addObserver(self, selector: #selector(increaseQuantity), name: .increaseQuntity, object: nil) + NotificationCenter.default.addObserver(self, selector: #selector(decreaseQuantity), name: .decreaseQuntity, object: nil) + } + + private func bind() { + detailViewModel.setTitleHandler = { title in + DispatchQueue.main.async { + self.setTitle(title) + } + } + + detailViewModel.setInformationHandler = { + DispatchQueue.main.async { + self.setInformationView() + } + } + + detailViewModel.imageFetchHandler = { + DispatchQueue.main.async { + self.clearImage() + self.setThumdnailImage() + self.setDetailScrollView() + } + } + + self.detailViewModel.errorHandler = { error in + Toast(text: error).show() + } + } + + @objc func increaseQuantity() { + detailViewModel.quantity += 1 + guard let price = detailViewModel.currentDetail.prices.first else { + return + } + let originalPrice = convertPriceToInteger(with: price) + let totalPrice = convertPriceToString(value: detailViewModel.quantity * originalPrice) + customView.configure(quantity: detailViewModel.quantity, totalPrice: totalPrice) + } + + @objc func decreaseQuantity() { + guard detailViewModel.quantity > 1 else { + return + } + detailViewModel.quantity -= 1 + guard let price = detailViewModel.currentDetail.prices.first else { + return + } + let originalPrice = convertPriceToInteger(with: price) + let totalPrice = convertPriceToString(value: detailViewModel.quantity * originalPrice) + customView.configure(quantity: detailViewModel.quantity, totalPrice: totalPrice) + } + + func convertPriceToString(value: Int) -> String{ + let numberFormatter = NumberFormatter() + numberFormatter.numberStyle = .decimal + let result = numberFormatter.string(from: NSNumber(value: value))! + "원" + return result + } + + func convertPriceToInteger(with price: String) -> Int { + let temp = price.replacingOccurrences(of: ",", with: "") + let price = temp.replacingOccurrences(of: "원", with: "") + return Int(price) ?? 0 } override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) self.navigationController?.isNavigationBarHidden = false } + + private func setTitle(_ title: String) { + self.title = title + } + + private func clearImage() { + for subview in self.informationStackView.subviews { + subview.removeFromSuperview() + } + + self.thumbnailStackView.subviews.forEach { (view) in + view.removeFromSuperview() + } + } + + private func setInformationView() { + if let view = Bundle.main.loadNibNamed("CustomView", owner: self, options: nil)?.first as? CustomView { + view.configure(productName: self.detailViewModel.currentItemTitle, item: detailViewModel.currentDetail) + view.configureBadge(for: detailViewModel.currentItemBadge) + view.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true + view.heightAnchor.constraint(equalToConstant: view.frame.height).isActive = true + self.informationStackView.insertArrangedSubview(view, at: 0) + customView = view + } + } + + private func setThumbnailScrollView() { + self.thumbnailScrollView.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.width) + } + + private func setThumdnailImage() { + let currentDetailItem = self.detailViewModel.currentDetail + for index in 0..<(currentDetailItem.thumbImagesData?.count ?? 0) + 1 { + let imageView = UIImageView() + imageView.translatesAutoresizingMaskIntoConstraints = false + imageView.contentMode = .scaleAspectFit + if index == 0 { + guard let imageData = currentDetailItem.topImageData else { continue } + imageView.image = UIImage(data: imageData) + } else { + guard let imageData = currentDetailItem.thumbImagesData?[index - 1] else { continue } + imageView.image = UIImage(data: imageData) + } + self.thumbnailStackView.addArrangedSubview(imageView) + imageView.widthAnchor.constraint(equalTo: self.thumbnailStackView.heightAnchor, multiplier: 1).isActive = true + } + } + + private func setDetailScrollView() { + guard let detailSectionData = self.detailViewModel.currentDetail.detailSectionData else { return } + for index in 0.. CGFloat { + guard let image = image else { return 0 } + return image.size.height / image.size.width + } +} + +extension Notification.Name { + static let increaseQuntity = Notification.Name("increaseQuntity") + static let decreaseQuntity = Notification.Name("decreaseQuntity") } diff --git a/Sidedish/Sidedish/Entity/Category.swift b/Sidedish/Sidedish/Entity/Category.swift deleted file mode 100644 index d2b377f97..000000000 --- a/Sidedish/Sidedish/Entity/Category.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Category.swift -// Sidedish -// -// Created by Issac on 2021/04/19. -// - -import Foundation - -struct Category: Codable { - var categoryId: String - var name: String - var item: [SidedishItem] - - enum Category: String, CodingKey { - case categoryId = "category_id" - case name - case item - } -} diff --git a/Sidedish/Sidedish/Entity/Detail.swift b/Sidedish/Sidedish/Entity/Detail.swift new file mode 100644 index 000000000..ec1f2cda0 --- /dev/null +++ b/Sidedish/Sidedish/Entity/Detail.swift @@ -0,0 +1,12 @@ +// +// Detail.swift +// Sidedish +// +// Created by Issac on 2021/04/27. +// + +import Foundation + +class Detail: Codable { + var data: DetailItem +} diff --git a/Sidedish/Sidedish/Entity/DetailItem.swift b/Sidedish/Sidedish/Entity/DetailItem.swift new file mode 100644 index 000000000..b10dd85b4 --- /dev/null +++ b/Sidedish/Sidedish/Entity/DetailItem.swift @@ -0,0 +1,50 @@ +// +// DetailItem.swift +// Sidedish +// +// Created by Issac on 2021/04/27. +// + +import Foundation + +struct DetailItem: Codable { + var topImageURL: String + var topImageData: Data? + var thumbImagesURL: [String] + var thumbImagesData: [Data?]? + var productDescription: String + var point: String + var deliveryInfo: String + var deleveryFee: String + var prices: [String] + var detailSectionURL: [String] + var detailSectionData: [Data?]? + + enum CodingKeys: String, CodingKey { + case topImageURL = "top_image" + case topImageData + case thumbImagesURL = "thumb_images" + case thumbImagesData + case productDescription = "product_description" + case point + case deliveryInfo = "delivery_info" + case deleveryFee = "delivery_fee" + case prices + case detailSectionURL = "detail_section" + case detailSectionData + } + + init() { + self.topImageURL = "" + self.topImageData = nil + self.thumbImagesURL = [String]() + self.thumbImagesData = nil + self.productDescription = "" + self.point = "" + self.deliveryInfo = "" + self.deleveryFee = "" + self.prices = [String]() + self.detailSectionURL = [String]() + self.detailSectionData = nil + } +} diff --git a/Sidedish/Sidedish/Entity/Enum/Category.swift b/Sidedish/Sidedish/Entity/Enum/Category.swift new file mode 100644 index 000000000..8218a8868 --- /dev/null +++ b/Sidedish/Sidedish/Entity/Enum/Category.swift @@ -0,0 +1,30 @@ +// +// Category.swift +// Sidedish +// +// Created by Issac on 2021/04/26. +// + +import Foundation + +enum Category: Int, CustomStringConvertible, CaseIterable { + case main = 0 + case soup = 1 + case side = 2 + + var description: String { + switch self { + case .main: return "main" + case .soup: return "soup" + case .side: return "side" + } + } + + var index: Int { + switch self { + case .main: return 0 + case .soup: return 1 + case .side: return 2 + } + } +} diff --git a/Sidedish/Sidedish/Entity/SidedishItem.swift b/Sidedish/Sidedish/Entity/SidedishItem.swift index 00b71a27f..6c2ca5133 100644 --- a/Sidedish/Sidedish/Entity/SidedishItem.swift +++ b/Sidedish/Sidedish/Entity/SidedishItem.swift @@ -21,7 +21,7 @@ struct SidedishItem: Codable { enum CodingKeys: String, CodingKey { case detailHash = "detail_hash" - case imageURL + case imageURL = "image" case alt case deliveryType = "delivery_type" case title diff --git a/Sidedish/Sidedish/Persistence/ImageCacheCenter.swift b/Sidedish/Sidedish/Persistence/ImageCacheCenter.swift new file mode 100644 index 000000000..ba5a96856 --- /dev/null +++ b/Sidedish/Sidedish/Persistence/ImageCacheCenter.swift @@ -0,0 +1,52 @@ +// +// ImageCacheCenter.swift +// Sidedish +// +// Created by Issac on 2021/04/29. +// + +import UIKit + +protocol ImageCacheable { + func cacheFileExists(fileName: String) -> Bool + func loadImageCache(fileName: String, completion: @escaping (Data) -> ()) + func cachingImage(named: String, imageData: Data) +} + +class ImageCacheCenter: ImageCacheable { + let imageCache: NSCache + var fileManager: FileManager { + return FileManager.default + } + + var cacheURL: URL { + return fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0] + } + + init() { + self.imageCache = NSCache() + } + + func cacheFileExists(fileName: String) -> Bool { + let path = cacheURL.appendingPathComponent(fileName).path + return fileManager.fileExists(atPath: path) + } + + func loadImageCache(fileName: String, completion: @escaping (Data) -> ()) { + let url = cacheURL.appendingPathComponent(fileName) + URLSession.shared.dataTask(with: url) { (data, _, _) in + guard let data = data else { return } + completion(data) + }.resume() + } + + func cachingImage(named: String, imageData: Data) { + let path = cacheURL.appendingPathComponent(named).path + let jpegData = UIImage(data: imageData)?.jpegData(compressionQuality: 0.4) + if !cacheFileExists(fileName: named) { + fileManager.createFile(atPath: path, + contents: jpegData, + attributes: nil) + } + } +} diff --git a/Sidedish/Sidedish/Repository/SidedishNetworkCenter.swift b/Sidedish/Sidedish/Repository/SidedishNetworkCenter.swift index 752519171..725ce02cd 100644 --- a/Sidedish/Sidedish/Repository/SidedishNetworkCenter.swift +++ b/Sidedish/Sidedish/Repository/SidedishNetworkCenter.swift @@ -9,31 +9,108 @@ import Foundation import Alamofire protocol Networkable { - func fetchItems(url: String, completion: @escaping (Result<[SidedishItem], AFError>) -> ()) + func fetchItem(url: String, completion: @escaping (Result) -> ()) func downloadImage(from url: URL, completion: @escaping ((Data) -> ()) ) } class SidedishNetworkCenter: Networkable { - func fetchItems(url: String, completion: @escaping (Result<[SidedishItem], AFError>) -> ()) { - AF.request(url).validate().responseDecodable(of: SidedishOfCategory.self) { (response) in + var imageCacheCenter: ImageCacheable + + init(imageCacheable: ImageCacheable) { + self.imageCacheCenter = imageCacheable + } + + func fetchItem(url: String, completion: @escaping (Result) -> ()) { + AF.request(url).validate().responseDecodable(of: T.self) { (response) in switch response.result { - case .success(let sidedishOfCategory): - self.fetchImage(sidedishOfCategory: sidedishOfCategory) { (sidedishOfCategory) in - completion(.success(sidedishOfCategory.body) ) + case .success(let value): + if ((value as? SidedishOfCategory) != nil) { + self.fetchImage(sidedishOfCategory: value) { (sidedishOfCategory) in + completion(.success(sidedishOfCategory)) + } + } else if ((value as? Detail) != nil) { + self.fetchDetailImgae(detail: value) { (detail) in + completion(.success(detail)) + } } + case .failure(let error): completion(.failure(error)) } } } - private func fetchImage(sidedishOfCategory: SidedishOfCategory, completion: @escaping (SidedishOfCategory) -> ()) { - var originalSidedishs = sidedishOfCategory - for (index, item) in sidedishOfCategory.body.enumerated() { + private func fetchImage(sidedishOfCategory: T, completion: @escaping (T) -> ()) { + guard var originalSidedishs = sidedishOfCategory as? SidedishOfCategory else { return } + for (index, item) in originalSidedishs.body.enumerated() { guard let url = URL(string: item.imageURL) else { return } + + if imageCacheCenter.cacheFileExists(fileName: url.lastPathComponent) { + imageCacheCenter.loadImageCache(fileName: url.lastPathComponent) { (data) in + originalSidedishs.body[index].imageData = data + guard let originalSidedishs = originalSidedishs as? T else { return } + completion(originalSidedishs) + } + } else { + self.downloadImage(from: url) { (data) in + originalSidedishs.body[index].imageData = data + guard let originalSidedishs = originalSidedishs as? T else { return } + completion(originalSidedishs) + } + } + } + } + + private func fetchDetailImgae(detail: T, completion: @escaping (T) -> ()) { + guard let originalDetail = detail as? Detail else { return } + guard let url = URL(string: originalDetail.data.topImageURL) else { return } + if imageCacheCenter.cacheFileExists(fileName: url.lastPathComponent) { + imageCacheCenter.loadImageCache(fileName: url.lastPathComponent) { (data) in + originalDetail.data.topImageData = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } + } else { self.downloadImage(from: url) { (data) in - originalSidedishs.body[index].imageData = data - completion(originalSidedishs) + originalDetail.data.topImageData = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } + } + + originalDetail.data.thumbImagesData = Array(repeating: nil, count: originalDetail.data.thumbImagesURL.count) + for (index, thumbImageURL) in originalDetail.data.thumbImagesURL.enumerated() { + guard let url = URL(string: thumbImageURL) else { return } + if imageCacheCenter.cacheFileExists(fileName: url.lastPathComponent) { + imageCacheCenter.loadImageCache(fileName: url.lastPathComponent) { (data) in + originalDetail.data.thumbImagesData?[index] = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } + } else { + self.downloadImage(from: url) { (data) in + originalDetail.data.thumbImagesData?[index] = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } + } + } + + originalDetail.data.detailSectionData = Array(repeating: nil, count: originalDetail.data.detailSectionURL.count) + for (index, detailSectionURL) in originalDetail.data.detailSectionURL.enumerated() { + guard let url = URL(string: detailSectionURL) else { return } + if imageCacheCenter.cacheFileExists(fileName: url.lastPathComponent) { + imageCacheCenter.loadImageCache(fileName: url.lastPathComponent) { (data) in + originalDetail.data.detailSectionData?[index] = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } + } else { + self.downloadImage(from: url) { (data) in + originalDetail.data.detailSectionData?[index] = data + guard let originalDetail = originalDetail as? T else { return } + completion(originalDetail) + } } } } @@ -49,6 +126,8 @@ class SidedishNetworkCenter: Networkable { guard let imageData = data else { return } + self.imageCacheCenter.cachingImage(named: url.lastPathComponent, + imageData: imageData) completion(imageData) }.resume() } diff --git a/Sidedish/Sidedish/UseCase/SidedishProcessing.swift b/Sidedish/Sidedish/UseCase/SidedishProcessing.swift index 63cbd15ec..4c6200549 100644 --- a/Sidedish/Sidedish/UseCase/SidedishProcessing.swift +++ b/Sidedish/Sidedish/UseCase/SidedishProcessing.swift @@ -10,6 +10,7 @@ import Alamofire protocol SidedishProcessable { func getItems(url: String, completion: @escaping (Result<[SidedishItem], AFError>) -> ()) + func getDetail(url: String, completion: @escaping (Result) -> ()) func getImage(url: URL, completion: @escaping ((Data) -> ())) } @@ -20,10 +21,21 @@ class SidedishProcessing: SidedishProcessable { } func getItems(url: String, completion: @escaping (Result<[SidedishItem], AFError>) -> ()) { - self.sideDishNetworkCenter.fetchItems(url: url) { (result) in + self.sideDishNetworkCenter.fetchItem(url: url) { (result: Result) in switch result { - case .success(let sidedishItems): - completion(.success(sidedishItems)) + case .success(let SidedishOfCategory): + completion(.success(SidedishOfCategory.body)) + case .failure(let error): + completion(.failure(error)) + } + } + } + + func getDetail(url: String, completion: @escaping (Result) -> ()) { + self.sideDishNetworkCenter.fetchItem(url: url) { (result: Result) in + switch result { + case .success(let detail): + completion(.success(detail.data)) case .failure(let error): completion(.failure(error)) } diff --git a/Sidedish/Sidedish/View/Base.lproj/Main.storyboard b/Sidedish/Sidedish/View/Base.lproj/Main.storyboard index 104ad7a3c..daf4d6e5c 100644 --- a/Sidedish/Sidedish/View/Base.lproj/Main.storyboard +++ b/Sidedish/Sidedish/View/Base.lproj/Main.storyboard @@ -47,7 +47,7 @@ - + - + -