diff --git a/Docs/Detail&SoldOut.gif b/Docs/Detail&SoldOut.gif
new file mode 100644
index 000000000..b712b6794
Binary files /dev/null and b/Docs/Detail&SoldOut.gif differ
diff --git a/Docs/MainView.gif b/Docs/MainView.gif
new file mode 100644
index 000000000..8ec370927
Binary files /dev/null and b/Docs/MainView.gif differ
diff --git a/Docs/Order.gif b/Docs/Order.gif
new file mode 100644
index 000000000..cc1f7ab73
Binary files /dev/null and b/Docs/Order.gif differ
diff --git a/Docs/Toast.gif b/Docs/Toast.gif
new file mode 100644
index 000000000..afca1dfc2
Binary files /dev/null and b/Docs/Toast.gif differ
diff --git a/README.md b/README.md
index 39a151d3f..be239841d 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,46 @@ Team 10
- Bongf
- K
+## μ€ν νλ©΄
+
+### Main View
+
+
+
+πββοΈ λ©μΈ νλ©΄μμ **λ©μΈμ리**, **κ΅λ¬Όμ리**, **λ°λ°μ°¬** μΌλ‘ λΆλ₯ λ μμλ€μ μ, νλ‘ μ€ν¬λ‘€ νμ¬ νμ νμ€ μ μμ΅λλ€.
+
+πββοΈ κ° λ°μ°¬λ€μ μ΄λ¦κ³Ό μ©λ, κ°λ¨ν μ€λͺ
, μ κ°, ν μΈ κ°κ²©, ν μΈ μ 보λ₯Ό νμΈνμ€ μ μμ΅λλ€.
+
+
+
+### Detail View & Sold Out
+
+
+
+πββοΈ λ©μΈ νλ©΄μμ μνλ λ°μ°¬μ ν΄λ¦νμλ©΄ ν΄λΉ λ°μ°¬μ λν μμΈν μ 보λ₯Ό μ»μΌμ€ μ μμ΅λλ€.
+
+πββοΈ ν΄λΉ λ°μ°¬μ μ¬κ³ κ° μμΌλ©΄ μμ½μ§λ§ `μ£Όλ¬Έ νκΈ°` λ²νΌμ `μΌμ νμ `λ‘ λ°λλ©° λ²νΌμ΄ λΉνμ±ν λμ΄ μ£Όλ¬Ένμ€ μ μμ΅λλ€.
+
+
+
+### Order
+
+
+
+πββοΈ μνμλ λ°μ°¬μ λν μ¬κ³ κ° μμΌλ©΄ `μ£Όλ¬ΈνκΈ°` λ²νΌμ΄ νμ±ν λμ΄μμΌλ©° μ£Όλ¬Έ μ§νμ΄ κ°λ₯ν©λλ€.
+
+πββοΈ νμ§λ§ νμ¬ μ¬κ³ λ³΄λ€ λ§μ μλμ μ£Όλ¬Ένλ € ν μμλ `Alert` λ‘ μ¬κ³ κ° λΆμ‘±ν¨μ μ립λλ€.
+
+πββοΈ μ¬κ³ μμμ μ£Όλ¬Έμ νμ€ κ²½μ°μλ μ±κ³΅μ μΌλ‘ μ£Όλ¬Έ μ μκ° μλ£λλ©° `μ£Όλ¬Έμ΄ μλ£λμμ΅λλ€` λΌλ μλ¦Όκ³Ό ν¨κ» Main νλ©΄μΌλ‘ μ΄λνκ² λ©λλ€.
+
+
+
+### Toast
+
+
+
+πββοΈ λ©μΈ νλ©΄μμ **λ©μΈμ리**, **κ΅λ¬Όμ리**, **λ°λ°μ°¬** μ½λμ ν€λλ₯Ό ννκ² λλ©΄ νλ©΄ μ€μ νλ¨μ Toastλ‘ ν΄λΉ μ½λμ λ±λ‘λ λ°μ°¬ μ’
λ₯μ κ°μλ₯Ό μ μ μμ΅λλ€.
+
## λΈλμΉ μ λ΅
ν΄λλ₯Ό iOSμ BE λκ°λ‘ λλμ΄μ κ΄λ¦¬ν©λλ€.
diff --git a/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
index 1d2b04313..cce3a9137 100644
--- a/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -11,6 +11,11 @@
orderHint
0
+ Kingfisher.xcscheme_^#shared#^_
+
+ orderHint
+ 5
+
Pods-SideDish-SideDishUITests.xcscheme
isShown
diff --git a/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/shim.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/shim.xcuserdatad/xcschemes/xcschememanagement.plist
index 08dd6efed..6bebd9c0f 100644
--- a/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/shim.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/iOS/SideDish/Pods/Pods.xcodeproj/xcuserdata/shim.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -16,7 +16,7 @@
isShown
orderHint
- 1
+ 2
Pods-SideDish-SideDishUITests.xcscheme
@@ -30,7 +30,7 @@
isShown
orderHint
- 2
+ 1
Pods-SideDishTests.xcscheme
diff --git a/iOS/SideDish/SideDish.xcodeproj/project.pbxproj b/iOS/SideDish/SideDish.xcodeproj/project.pbxproj
index 82e775dc2..4054e4659 100644
--- a/iOS/SideDish/SideDish.xcodeproj/project.pbxproj
+++ b/iOS/SideDish/SideDish.xcodeproj/project.pbxproj
@@ -12,6 +12,17 @@
5C69859173AA8099B0701A54 /* Pods_SideDishTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D7433C6120AE2C2B5B290532 /* Pods_SideDishTests.framework */; };
D4A2C9AC263273C10019AFB6 /* BadgeLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2C9AB263273C10019AFB6 /* BadgeLabel.swift */; };
D4A2CA37263417B60019AFB6 /* ImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CA36263417B60019AFB6 /* ImageView+Extension.swift */; };
+ D4A2CABA2637A6890019AFB6 /* EndPoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAB92637A6890019AFB6 /* EndPoint.swift */; };
+ D4A2CAC32637A8970019AFB6 /* BanchanListRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAC22637A8970019AFB6 /* BanchanListRepository.swift */; };
+ D4A2CACC2637AFCA0019AFB6 /* BanchanSceneFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CACB2637AFCA0019AFB6 /* BanchanSceneFlowCoordinator.swift */; };
+ D4A2CAD32637B1250019AFB6 /* BanchanSceneDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAD22637B1250019AFB6 /* BanchanSceneDIContainer.swift */; };
+ D4A2CAD92637B25B0019AFB6 /* AppDIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAD82637B25B0019AFB6 /* AppDIContainer.swift */; };
+ D4A2CADE2637B3D90019AFB6 /* AppFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CADD2637B3D90019AFB6 /* AppFlowCoordinator.swift */; };
+ D4A2CAF42637D7B10019AFB6 /* BanchanDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAF32637D7B10019AFB6 /* BanchanDetailViewModel.swift */; };
+ D4A2CAF92637D7E70019AFB6 /* BanchanDetailDTO+Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAF82637D7E70019AFB6 /* BanchanDetailDTO+Mapping.swift */; };
+ D4A2CAFE2637D8210019AFB6 /* BanchanDetailRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CAFD2637D8210019AFB6 /* BanchanDetailRepository.swift */; };
+ D4A2CB062637D8EF0019AFB6 /* FetchBanchanDetailUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CB052637D8EF0019AFB6 /* FetchBanchanDetailUseCase.swift */; };
+ D4A2CB2526392FFC0019AFB6 /* OrderBanchanUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4A2CB2426392FFC0019AFB6 /* OrderBanchanUseCase.swift */; };
D4BFBAD3262E989000D68297 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BFBAD2262E989000D68297 /* AppDelegate.swift */; };
D4BFBAD5262E989000D68297 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BFBAD4262E989000D68297 /* SceneDelegate.swift */; };
D4BFBAD7262E989000D68297 /* BanchanListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BFBAD6262E989000D68297 /* BanchanListViewController.swift */; };
@@ -29,10 +40,12 @@
D4BFBB3C262EC8DB00D68297 /* BanchanCustomCellHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4BFBB3A262EC8DB00D68297 /* BanchanCustomCellHeader.swift */; };
D4BFBB3D262EC8DB00D68297 /* BanchanCustomCellHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = D4BFBB3B262EC8DB00D68297 /* BanchanCustomCellHeader.xib */; };
FFA10EB1262FC6CD00D584B6 /* BanchanCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFA10EB0262FC6CD00D584B6 /* BanchanCollectionView.swift */; };
+ FFB6F9D3263A569F00BDB164 /* CoreDataStorage.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = FFB6F9D1263A569F00BDB164 /* CoreDataStorage.xcdatamodeld */; };
+ FFB6F9D5263A574500BDB164 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB6F9D4263A574500BDB164 /* CoreDataManager.swift */; };
+ FFB6F9D7263A576600BDB164 /* BanchanListStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFB6F9D6263A576600BDB164 /* BanchanListStorage.swift */; };
FFEF70F926310FB400189376 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF70F826310FB400189376 /* NetworkService.swift */; };
FFEF70FF2631104B00189376 /* BanchanListDTO+Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF70FE2631104B00189376 /* BanchanListDTO+Mapping.swift */; };
FFEF71042631107200189376 /* FetchBanchanListUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF71032631107200189376 /* FetchBanchanListUseCase.swift */; };
- FFEF71092631128900189376 /* FetchImageUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF71082631128900189376 /* FetchImageUseCase.swift */; };
FFEF7123263119C500189376 /* BanchanListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FFEF7122263119C500189376 /* BanchanListViewModel.swift */; };
/* End PBXBuildFile section */
@@ -63,6 +76,17 @@
8C36659865215502B6A787B2 /* Pods_SideDish.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SideDish.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D4A2C9AB263273C10019AFB6 /* BadgeLabel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BadgeLabel.swift; sourceTree = ""; };
D4A2CA36263417B60019AFB6 /* ImageView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ImageView+Extension.swift"; sourceTree = ""; };
+ D4A2CAB92637A6890019AFB6 /* EndPoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EndPoint.swift; sourceTree = ""; };
+ D4A2CAC22637A8970019AFB6 /* BanchanListRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanListRepository.swift; sourceTree = ""; };
+ D4A2CACB2637AFCA0019AFB6 /* BanchanSceneFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanSceneFlowCoordinator.swift; sourceTree = ""; };
+ D4A2CAD22637B1250019AFB6 /* BanchanSceneDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanSceneDIContainer.swift; sourceTree = ""; };
+ D4A2CAD82637B25B0019AFB6 /* AppDIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDIContainer.swift; sourceTree = ""; };
+ D4A2CADD2637B3D90019AFB6 /* AppFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppFlowCoordinator.swift; sourceTree = ""; };
+ D4A2CAF32637D7B10019AFB6 /* BanchanDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BanchanDetailViewModel.swift; path = SideDish/Data/Repositories/BanchanDetailViewModel.swift; sourceTree = SOURCE_ROOT; };
+ D4A2CAF82637D7E70019AFB6 /* BanchanDetailDTO+Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BanchanDetailDTO+Mapping.swift"; sourceTree = ""; };
+ D4A2CAFD2637D8210019AFB6 /* BanchanDetailRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanDetailRepository.swift; sourceTree = ""; };
+ D4A2CB052637D8EF0019AFB6 /* FetchBanchanDetailUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FetchBanchanDetailUseCase.swift; sourceTree = ""; };
+ D4A2CB2426392FFC0019AFB6 /* OrderBanchanUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderBanchanUseCase.swift; sourceTree = ""; };
D4BFBACF262E989000D68297 /* SideDish.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SideDish.app; sourceTree = BUILT_PRODUCTS_DIR; };
D4BFBAD2262E989000D68297 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
D4BFBAD4262E989000D68297 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
@@ -88,10 +112,12 @@
D7433C6120AE2C2B5B290532 /* Pods_SideDishTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SideDishTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E5121CE39396F4ECB1937C15 /* Pods-SideDish-SideDishUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SideDish-SideDishUITests.debug.xcconfig"; path = "Target Support Files/Pods-SideDish-SideDishUITests/Pods-SideDish-SideDishUITests.debug.xcconfig"; sourceTree = ""; };
FFA10EB0262FC6CD00D584B6 /* BanchanCollectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanCollectionView.swift; sourceTree = ""; };
+ FFB6F9D2263A569F00BDB164 /* CoreDataStorage.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreDataStorage.xcdatamodel; sourceTree = ""; };
+ FFB6F9D4263A574500BDB164 /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = ""; };
+ FFB6F9D6263A576600BDB164 /* BanchanListStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanListStorage.swift; sourceTree = ""; };
FFEF70F826310FB400189376 /* NetworkService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; };
FFEF70FE2631104B00189376 /* BanchanListDTO+Mapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BanchanListDTO+Mapping.swift"; sourceTree = ""; };
FFEF71032631107200189376 /* FetchBanchanListUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchBanchanListUseCase.swift; sourceTree = ""; };
- FFEF71082631128900189376 /* FetchImageUseCase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchImageUseCase.swift; sourceTree = ""; };
FFEF7122263119C500189376 /* BanchanListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BanchanListViewModel.swift; sourceTree = ""; };
/* End PBXFileReference section */
@@ -155,6 +181,43 @@
path = Utills;
sourceTree = "";
};
+ D4A2CAC12637A8810019AFB6 /* Repositories */ = {
+ isa = PBXGroup;
+ children = (
+ D4A2CAC22637A8970019AFB6 /* BanchanListRepository.swift */,
+ D4A2CAFD2637D8210019AFB6 /* BanchanDetailRepository.swift */,
+ );
+ path = Repositories;
+ sourceTree = "";
+ };
+ D4A2CACA2637AFB20019AFB6 /* Flow */ = {
+ isa = PBXGroup;
+ children = (
+ D4A2CACB2637AFCA0019AFB6 /* BanchanSceneFlowCoordinator.swift */,
+ );
+ path = Flow;
+ sourceTree = "";
+ };
+ D4A2CAD02637B0EB0019AFB6 /* Application */ = {
+ isa = PBXGroup;
+ children = (
+ D4A2CAD12637B10B0019AFB6 /* DIContainer */,
+ D4BFBAD2262E989000D68297 /* AppDelegate.swift */,
+ D4BFBAD4262E989000D68297 /* SceneDelegate.swift */,
+ D4A2CADD2637B3D90019AFB6 /* AppFlowCoordinator.swift */,
+ );
+ path = Application;
+ sourceTree = "";
+ };
+ D4A2CAD12637B10B0019AFB6 /* DIContainer */ = {
+ isa = PBXGroup;
+ children = (
+ D4A2CAD22637B1250019AFB6 /* BanchanSceneDIContainer.swift */,
+ D4A2CAD82637B25B0019AFB6 /* AppDIContainer.swift */,
+ );
+ path = DIContainer;
+ sourceTree = "";
+ };
D4BFBAC6262E989000D68297 = {
isa = PBXGroup;
children = (
@@ -180,6 +243,7 @@
D4BFBAD1262E989000D68297 /* SideDish */ = {
isa = PBXGroup;
children = (
+ D4A2CAD02637B0EB0019AFB6 /* Application */,
D4BFBB1B262EA76300D68297 /* Data */,
D4BFBB1A262EA75D00D68297 /* Domain */,
D4BFBB19262EA74E00D68297 /* Presentaion */,
@@ -211,6 +275,7 @@
D4BFBB19262EA74E00D68297 /* Presentaion */ = {
isa = PBXGroup;
children = (
+ D4A2CACA2637AFB20019AFB6 /* Flow */,
D4A2C9AA263273A60019AFB6 /* Utills */,
D4BFBB1F262EA7AC00D68297 /* DetailBanchan */,
D4BFBB1E262EA7A100D68297 /* BanchanList */,
@@ -230,6 +295,7 @@
D4BFBB1B262EA76300D68297 /* Data */ = {
isa = PBXGroup;
children = (
+ D4A2CAC12637A8810019AFB6 /* Repositories */,
D4BFBB27262EA80B00D68297 /* PersistentStorages */,
D4BFBB26262EA7F500D68297 /* Network */,
);
@@ -287,6 +353,7 @@
D4BFBB23262EA7C700D68297 /* ViewModel */ = {
isa = PBXGroup;
children = (
+ D4A2CAF32637D7B10019AFB6 /* BanchanDetailViewModel.swift */,
);
path = ViewModel;
sourceTree = "";
@@ -304,7 +371,8 @@
isa = PBXGroup;
children = (
FFEF71032631107200189376 /* FetchBanchanListUseCase.swift */,
- FFEF71082631128900189376 /* FetchImageUseCase.swift */,
+ D4A2CB052637D8EF0019AFB6 /* FetchBanchanDetailUseCase.swift */,
+ D4A2CB2426392FFC0019AFB6 /* OrderBanchanUseCase.swift */,
);
path = UseCases;
sourceTree = "";
@@ -313,6 +381,7 @@
isa = PBXGroup;
children = (
FFEF70F826310FB400189376 /* NetworkService.swift */,
+ D4A2CAB92637A6890019AFB6 /* EndPoint.swift */,
FFEF70FD2631103C00189376 /* DataMapping */,
);
path = Network;
@@ -321,25 +390,36 @@
D4BFBB27262EA80B00D68297 /* PersistentStorages */ = {
isa = PBXGroup;
children = (
+ FFB6F9D6263A576600BDB164 /* BanchanListStorage.swift */,
+ FFB6F9D0263A569300BDB164 /* CoreDataStorage */,
);
- path = PersistentStorages;
+ name = PersistentStorages;
+ path = ../PersistentStorages;
sourceTree = "";
};
D4BFBB28262EA83E00D68297 /* SupportingFiles */ = {
isa = PBXGroup;
children = (
- D4BFBAD2262E989000D68297 /* AppDelegate.swift */,
- D4BFBAD4262E989000D68297 /* SceneDelegate.swift */,
D4BFBADB262E989300D68297 /* Assets.xcassets */,
D4BFBADD262E989300D68297 /* LaunchScreen.storyboard */,
);
path = SupportingFiles;
sourceTree = "";
};
+ FFB6F9D0263A569300BDB164 /* CoreDataStorage */ = {
+ isa = PBXGroup;
+ children = (
+ FFB6F9D4263A574500BDB164 /* CoreDataManager.swift */,
+ FFB6F9D1263A569F00BDB164 /* CoreDataStorage.xcdatamodeld */,
+ );
+ path = CoreDataStorage;
+ sourceTree = "";
+ };
FFEF70FD2631103C00189376 /* DataMapping */ = {
isa = PBXGroup;
children = (
FFEF70FE2631104B00189376 /* BanchanListDTO+Mapping.swift */,
+ D4A2CAF82637D7E70019AFB6 /* BanchanDetailDTO+Mapping.swift */,
);
path = DataMapping;
sourceTree = "";
@@ -585,21 +665,34 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
+ D4A2CB062637D8EF0019AFB6 /* FetchBanchanDetailUseCase.swift in Sources */,
FFEF70F926310FB400189376 /* NetworkService.swift in Sources */,
D4BFBAD7262E989000D68297 /* BanchanListViewController.swift in Sources */,
D4BFBAD3262E989000D68297 /* AppDelegate.swift in Sources */,
+ D4A2CAF42637D7B10019AFB6 /* BanchanDetailViewModel.swift in Sources */,
+ D4A2CAC32637A8970019AFB6 /* BanchanListRepository.swift in Sources */,
+ FFB6F9D5263A574500BDB164 /* CoreDataManager.swift in Sources */,
+ D4A2CB2526392FFC0019AFB6 /* OrderBanchanUseCase.swift in Sources */,
+ D4A2CAF92637D7E70019AFB6 /* BanchanDetailDTO+Mapping.swift in Sources */,
D4BFBAD5262E989000D68297 /* SceneDelegate.swift in Sources */,
FFA10EB1262FC6CD00D584B6 /* BanchanCollectionView.swift in Sources */,
+ D4A2CABA2637A6890019AFB6 /* EndPoint.swift in Sources */,
+ FFB6F9D7263A576600BDB164 /* BanchanListStorage.swift in Sources */,
D4BFBB0A262E9A8200D68297 /* BanchanDetailViewController.swift in Sources */,
D4BFBB2F262EAD1C00D68297 /* BanchanDetail.swift in Sources */,
+ FFB6F9D3263A569F00BDB164 /* CoreDataStorage.xcdatamodeld in Sources */,
FFEF71042631107200189376 /* FetchBanchanListUseCase.swift in Sources */,
FFEF70FF2631104B00189376 /* BanchanListDTO+Mapping.swift in Sources */,
D4BFBB35262EB89800D68297 /* BanchanCustomCell.swift in Sources */,
D4A2CA37263417B60019AFB6 /* ImageView+Extension.swift in Sources */,
D4BFBB2A262EA93700D68297 /* Banchan.swift in Sources */,
- FFEF71092631128900189376 /* FetchImageUseCase.swift in Sources */,
+ D4A2CAD92637B25B0019AFB6 /* AppDIContainer.swift in Sources */,
+ D4A2CACC2637AFCA0019AFB6 /* BanchanSceneFlowCoordinator.swift in Sources */,
D4A2C9AC263273C10019AFB6 /* BadgeLabel.swift in Sources */,
+ D4A2CAFE2637D8210019AFB6 /* BanchanDetailRepository.swift in Sources */,
+ D4A2CAD32637B1250019AFB6 /* BanchanSceneDIContainer.swift in Sources */,
FFEF7123263119C500189376 /* BanchanListViewModel.swift in Sources */,
+ D4A2CADE2637B3D90019AFB6 /* AppFlowCoordinator.swift in Sources */,
D4BFBB3C262EC8DB00D68297 /* BanchanCustomCellHeader.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -939,6 +1032,19 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCVersionGroup section */
+ FFB6F9D1263A569F00BDB164 /* CoreDataStorage.xcdatamodeld */ = {
+ isa = XCVersionGroup;
+ children = (
+ FFB6F9D2263A569F00BDB164 /* CoreDataStorage.xcdatamodel */,
+ );
+ currentVersion = FFB6F9D2263A569F00BDB164 /* CoreDataStorage.xcdatamodel */;
+ path = CoreDataStorage.xcdatamodeld;
+ sourceTree = "";
+ versionGroupType = wrapper.xcdatamodel;
+ };
+/* End XCVersionGroup section */
};
rootObject = D4BFBAC7262E989000D68297 /* Project object */;
}
diff --git a/iOS/SideDish/SideDish.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist b/iOS/SideDish/SideDish.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
index 03b6a757e..6a2e741db 100644
--- a/iOS/SideDish/SideDish.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
+++ b/iOS/SideDish/SideDish.xcodeproj/xcuserdata/jibook.xcuserdatad/xcschemes/xcschememanagement.plist
@@ -7,7 +7,7 @@
SideDish.xcscheme_^#shared#^_
orderHint
- 5
+ 6
diff --git a/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/UserInterfaceState.xcuserstate
index 14146f759..33f3ede3e 100644
Binary files a/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/UserInterfaceState.xcuserstate and b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
new file mode 100644
index 000000000..2850884d4
--- /dev/null
+++ b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/jibook.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
@@ -0,0 +1,6 @@
+
+
+
diff --git a/iOS/SideDish/SideDish.xcworkspace/xcuserdata/shim.xcuserdatad/UserInterfaceState.xcuserstate b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/shim.xcuserdatad/UserInterfaceState.xcuserstate
index bbd758f88..4be10ab4d 100644
Binary files a/iOS/SideDish/SideDish.xcworkspace/xcuserdata/shim.xcuserdatad/UserInterfaceState.xcuserstate and b/iOS/SideDish/SideDish.xcworkspace/xcuserdata/shim.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/iOS/SideDish/SideDish/SupportingFiles/AppDelegate.swift b/iOS/SideDish/SideDish/Application/AppDelegate.swift
similarity index 99%
rename from iOS/SideDish/SideDish/SupportingFiles/AppDelegate.swift
rename to iOS/SideDish/SideDish/Application/AppDelegate.swift
index 6d4fad651..f81d70bf4 100644
--- a/iOS/SideDish/SideDish/SupportingFiles/AppDelegate.swift
+++ b/iOS/SideDish/SideDish/Application/AppDelegate.swift
@@ -7,6 +7,7 @@
import UIKit
+
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
diff --git a/iOS/SideDish/SideDish/Application/AppFlowCoordinator.swift b/iOS/SideDish/SideDish/Application/AppFlowCoordinator.swift
new file mode 100644
index 000000000..94059ab30
--- /dev/null
+++ b/iOS/SideDish/SideDish/Application/AppFlowCoordinator.swift
@@ -0,0 +1,24 @@
+//
+// AppFlowCoordinator.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import UIKit
+
+class AppFlowCoordinator {
+ private var navigationController: UINavigationController
+ private let appDIContainer: AppDIContainer
+
+ init(navigationController: UINavigationController, appDIContainer: AppDIContainer) {
+ self.navigationController = navigationController
+ self.appDIContainer = appDIContainer
+ }
+
+ func start() {
+ let banchanSceneDIContainer = appDIContainer.makeBanchanSceneDIContainer()
+ let flow = banchanSceneDIContainer.makeBanchanSceneFlowCoordinator(navigationController: navigationController)
+ flow.start()
+ }
+}
diff --git a/iOS/SideDish/SideDish/Application/DIContainer/AppDIContainer.swift b/iOS/SideDish/SideDish/Application/DIContainer/AppDIContainer.swift
new file mode 100644
index 000000000..eb0624374
--- /dev/null
+++ b/iOS/SideDish/SideDish/Application/DIContainer/AppDIContainer.swift
@@ -0,0 +1,17 @@
+//
+// AppDIContainer.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import Foundation
+
+class AppDIContainer {
+ lazy var apiNetworkService = DefaultNetworkSerivce()
+
+ func makeBanchanSceneDIContainer() -> BanchanSceneDIContainer {
+ let dependencies = BanchanSceneDIContainer.Dependencies.init(apiNetworkService: apiNetworkService)
+ return BanchanSceneDIContainer(dependencies: dependencies)
+ }
+}
diff --git a/iOS/SideDish/SideDish/Application/DIContainer/BanchanSceneDIContainer.swift b/iOS/SideDish/SideDish/Application/DIContainer/BanchanSceneDIContainer.swift
new file mode 100644
index 000000000..07e6a2e15
--- /dev/null
+++ b/iOS/SideDish/SideDish/Application/DIContainer/BanchanSceneDIContainer.swift
@@ -0,0 +1,73 @@
+//
+// BanchanSceneDIContainer.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import UIKit
+
+class BanchanSceneDIContainer: BanchanSceneFlowCoordinatorDependencies {
+
+ struct Dependencies {
+ let apiNetworkService: NetworkService
+ }
+
+ private let dependencies: Dependencies
+
+ init(dependencies: Dependencies) {
+ self.dependencies = dependencies
+ }
+
+ // MARK: - Persistent Storages
+ private func makeBanchanListStroage() -> CoreDataBanchanListStorage {
+ return CoreDataBanchanListStorage()
+ }
+
+ // MARK: - Repositories
+ private func makeBanchanListRepository() -> BanchanListRepository {
+ return DefaultBanchanListRepository(network: dependencies.apiNetworkService, storage: makeBanchanListStroage())
+ }
+
+ // MARK: - Use Cases
+ private func makeFetchBanchanListUseCase() -> FetchBanchanListUseCase{
+ return DefaultFetchBanchanListUseCase(banchanListRepository: makeBanchanListRepository())
+ }
+
+ // MARK: - ViewModel
+ private func makeBanchanListViewModel(action: BanchanListViewModelAction) -> BanchanListViewModel {
+ return BanchanListViewModel(fetchBanchanListUseCase: makeFetchBanchanListUseCase(), action: action)
+ }
+
+ // MARK: - ViewController
+ internal func makeBanchanListViewController(action: BanchanListViewModelAction) -> BanchanListViewController {
+ return BanchanListViewController.create(with: makeBanchanListViewModel(action: action))
+ }
+
+ // MARK: - Flow Coordinator
+ func makeBanchanSceneFlowCoordinator(navigationController: UINavigationController) -> BanchanSceneFlowCoordinator {
+ return BanchanSceneFlowCoordinator(navigationController: navigationController, dependencies: self)
+ }
+}
+
+extension BanchanSceneDIContainer {
+ private func makeBanchanDetailRepository() -> BanchanDetailRepository {
+ return DefaultBanchanDetailRepository(network: dependencies.apiNetworkService)
+ }
+
+ // MARK: - Use Cases
+ private func makeFetchBanchanDetailUseCase() -> FetchBanchanDetailUseCase{
+ return DefaultFetchBanchanDetailUseCase(banchanDetailRepository: makeBanchanDetailRepository())
+ }
+
+ // MARK: - ViewModel
+ private func makeBanchanDetailViewModel(hash: Int, action: BanchanDetailViewModelAction) -> BanchanDetailViewModel {
+ return BanchanDetailViewModel(hash: hash, fetchBanchanDetailUseCase: makeFetchBanchanDetailUseCase(), action: action)
+ }
+
+ // MARK: - ViewController
+ internal func makeBanchanDetailViewController(hash: Int, action: BanchanDetailViewModelAction) -> BanchanDetailViewController {
+ return BanchanDetailViewController.create(with: makeBanchanDetailViewModel(hash: hash, action: action))
+ }
+}
+
diff --git a/iOS/SideDish/SideDish/Application/SceneDelegate.swift b/iOS/SideDish/SideDish/Application/SceneDelegate.swift
new file mode 100644
index 000000000..a80c06257
--- /dev/null
+++ b/iOS/SideDish/SideDish/Application/SceneDelegate.swift
@@ -0,0 +1,26 @@
+//
+// SceneDelegate.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/20.
+//
+
+import UIKit
+
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+
+ var window: UIWindow?
+ private let appDIContainer = AppDIContainer()
+ private var flowCoordinator: AppFlowCoordinator?
+
+ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
+ guard let windowScene = (scene as? UIWindowScene) else { return }
+ window = UIWindow(windowScene: windowScene)
+ let navigationController = UINavigationController()
+ window?.rootViewController = navigationController
+ flowCoordinator = AppFlowCoordinator(navigationController: navigationController, appDIContainer: appDIContainer)
+ flowCoordinator?.start()
+ window?.makeKeyAndVisible()
+ }
+}
+
diff --git a/iOS/SideDish/SideDish/Base.lproj/Main.storyboard b/iOS/SideDish/SideDish/Base.lproj/Main.storyboard
index 36ba0373c..839e44bad 100644
--- a/iOS/SideDish/SideDish/Base.lproj/Main.storyboard
+++ b/iOS/SideDish/SideDish/Base.lproj/Main.storyboard
@@ -1,8 +1,9 @@
-
-
+
+
-
+
+
@@ -12,7 +13,7 @@
-
+
@@ -60,27 +61,370 @@
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanDetailDTO+Mapping.swift b/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanDetailDTO+Mapping.swift
new file mode 100644
index 000000000..40ba9ae6a
--- /dev/null
+++ b/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanDetailDTO+Mapping.swift
@@ -0,0 +1,39 @@
+//
+// BanchanDetailDTO+Mapping.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import Foundation
+
+struct BanchanDetailDTO: Decodable {
+ private let hash: Int
+ private let data: BanchanDetailItemDTO
+
+}
+
+extension BanchanDetailDTO {
+ struct BanchanDetailItemDTO: Decodable {
+ private let topImage: String
+ private let thumbImage: [String]
+ private let detailSection: [String]
+ private let productDescription: String
+ private let title: String
+ private let deliveryInfo: String
+ private let deliveryFee: String
+ private let point: String
+ private let nPrice: String?
+ private let sPrice: String
+ private let badges: [String]
+ private let inStock: Bool
+
+ func toDomain() -> BanchanDetail {
+ return .init(topImage: topImage, thumbImages: thumbImage, detailSection: detailSection, productDescription: productDescription, title: title, deliveryInfo: deliveryInfo, deliveryFee: deliveryFee, point: point, nPrice: nPrice, sPrice: sPrice, badges: badges, inStock: inStock)
+ }
+ }
+
+ func toDomain() -> BanchanDetail {
+ return data.toDomain()
+ }
+}
diff --git a/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanListDTO+Mapping.swift b/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanListDTO+Mapping.swift
index 2ef49c0d8..7bcd73b82 100644
--- a/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanListDTO+Mapping.swift
+++ b/iOS/SideDish/SideDish/Data/Network/DataMapping/BanchanListDTO+Mapping.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import CoreData
struct BanchanListDTO: Decodable {
private let body: [BanchanListItemDTO]
@@ -21,10 +22,25 @@ extension BanchanListDTO {
private let title: String
private var nPrice: String?
private let sPrice: String
- private let badge: [String]?
+ private let badges: [String]?
func toDomain() -> Banchan {
- return .init(detailHash: detailHash, image: image, alt: alt, title: title, description: description, nPrice: nPrice, sPrice: sPrice, badges: badge, deliveryType: deliveryType)
+ return .init(detailHash: detailHash, image: image, alt: alt, title: title, description: description, nPrice: nPrice, sPrice: sPrice, badges: badges, deliveryType: deliveryType)
+ }
+
+ func toEntity(with context: NSManagedObjectContext) -> BanchanEntity {
+ let entity = BanchanEntity.init(context: context)
+ entity.detailHash = Int16(detailHash)
+ entity.image = image
+ entity.alt = alt
+ entity.deliveryType = deliveryType
+ entity.banchanDescription = description
+ entity.title = title
+ entity.nPrice = nPrice
+ entity.sPrice = sPrice
+ entity.badges = badges
+
+ return entity
}
}
@@ -34,4 +50,17 @@ extension BanchanListDTO {
}
return banchans
}
+
+ func toEntity(with context: NSManagedObjectContext) -> [BanchanEntity] {
+ let entities = body.map { banchanListItemDTO in
+ banchanListItemDTO.toEntity(with: context)
+ }
+ return entities
+ }
+}
+
+extension BanchanEntity {
+ func toDomain() -> Banchan {
+ return Banchan.init(detailHash: Int(detailHash), image: image!, alt: alt!, title: title!, description: banchanDescription!, nPrice: nPrice, sPrice: sPrice!, badges: badges, deliveryType: deliveryType!)
+ }
}
diff --git a/iOS/SideDish/SideDish/Data/Network/EndPoint.swift b/iOS/SideDish/SideDish/Data/Network/EndPoint.swift
new file mode 100644
index 000000000..099c7e56f
--- /dev/null
+++ b/iOS/SideDish/SideDish/Data/Network/EndPoint.swift
@@ -0,0 +1,90 @@
+//
+// EndPoint.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import Foundation
+
+public protocol Requestable {
+ var baseURL: String { get }
+ var path: String { get }
+ var httpMethod: HttpMethod { get }
+ func url() -> URL?
+}
+
+public struct BanchanAPIEndpoint: Requestable {
+
+ public let baseURL = "http://ec2-54-180-115-20.ap-northeast-2.compute.amazonaws.com:8080/"
+ public let path: String
+ public let httpMethod: HttpMethod
+
+ init(path: String, httpMethod: HttpMethod) {
+ self.path = path
+ self.httpMethod = httpMethod
+ }
+
+ public func url() -> URL? {
+ return URL(string: baseURL + path)
+ }
+}
+
+public struct BanchanDetailAPIEndpoint: Requestable {
+
+ public let baseURL = "http://ec2-54-180-115-20.ap-northeast-2.compute.amazonaws.com:8080/detail/"
+ public let path: String
+ public let httpMethod: HttpMethod
+
+ init(path: String, httpMethod: HttpMethod) {
+ self.path = path
+ self.httpMethod = httpMethod
+ }
+
+ public func url() -> URL? {
+ return URL(string: baseURL + path)
+ }
+}
+
+public struct OrderBanchanAPIEndPoint: Requestable {
+ public let baseURL = "http://ec2-54-180-115-20.ap-northeast-2.compute.amazonaws.com:8080/detail/"
+ public let path: String
+ public let httpMethod: HttpMethod
+
+ init(path: String, httpMethod: HttpMethod) {
+ self.path = path
+ self.httpMethod = httpMethod
+ }
+
+ public func url() -> URL? {
+ return URL(string: baseURL + path + "/order")
+ }
+}
+
+
+public enum Section: Int, CaseIterable {
+ case main = 0
+ case soup, side
+
+ func path() -> String {
+ switch self {
+ case .main:
+ return "main"
+ case .soup:
+ return "soup"
+ case .side:
+ return "side"
+ }
+ }
+
+ func description() -> String {
+ switch self {
+ case .main:
+ return "λͺ¨λκ° μ’μνλ λ λ ν λ©μΈμ리"
+ case .soup:
+ return "μ μ±μ΄ λ΄κΈ΄ λ¨λλ¨λ κ΅λ¬Όμ리"
+ case .side:
+ return "μνμ νμ±νκ² νλ μ κ°ν λ°λ°μ°¬"
+ }
+ }
+}
diff --git a/iOS/SideDish/SideDish/Data/Network/NetworkService.swift b/iOS/SideDish/SideDish/Data/Network/NetworkService.swift
index 4001745a2..6bda1cf8d 100644
--- a/iOS/SideDish/SideDish/Data/Network/NetworkService.swift
+++ b/iOS/SideDish/SideDish/Data/Network/NetworkService.swift
@@ -6,32 +6,33 @@
//
import Foundation
-import Alamofire
import Combine
-protocol NetworkRequest {
- func request(with url: String, dataType: T.Type, httpMethod: HTTPMethod)
+protocol NetworkService {
+ func request(with endPoint: Requestable, dataType: T.Type)
-> AnyPublisher
+ func post (with endPoint: Requestable, data: T)
+ -> AnyPublisher
func decode(data: Data, dataType: T.Type) -> AnyPublisher
+ func encode(anyData: T) -> Result
}
-class NetworkSerivce: NetworkRequest {
+public class DefaultNetworkSerivce: NetworkService {
- static let shared = NetworkSerivce()
-
- private init() {
+ init() {
}
- func request(with url: String, dataType: T.Type, httpMethod: HTTPMethod)
+ func request(with endPoint: Requestable, dataType: T.Type)
-> AnyPublisher {
- guard let url = URL(string: url) else {
+
+ guard let url = endPoint.url() else {
let error = NetworkError.network(description: "Couldn't create URL")
return Fail(error: error).eraseToAnyPublisher()
}
var request = URLRequest(url: url)
request.httpBody = nil
- request.httpMethod = httpMethod.rawValue
+ request.httpMethod = endPoint.httpMethod.rawValue
return URLSession.shared.dataTaskPublisher(for: request)
.mapError { error in
@@ -43,27 +44,72 @@ class NetworkSerivce: NetworkRequest {
.eraseToAnyPublisher()
}
+ func post (with endPoint: Requestable, data: T)
+ -> AnyPublisher {
+ guard let url = endPoint.url() else {
+ let error = NetworkError.network(description: "Couldn't create URL")
+ return Fail(error: error).eraseToAnyPublisher()
+ }
+
+ let result = encode(anyData: data)
+ let json: Data
+ switch result {
+ case .failure(let error):
+ print(error)
+ return Fail(error: error).eraseToAnyPublisher()
+ case .success(let data):
+ json = data
+ }
+
+ var request = URLRequest(url: url)
+ request.httpBody = json
+ request.httpMethod = endPoint.httpMethod.rawValue
+ request.addValue("application/json", forHTTPHeaderField: "Content-Type")
+ if json != nil { request.setValue(String(json.count), forHTTPHeaderField: "Content-Length") }
+
+ return URLSession.shared.dataTaskPublisher(for: request)
+ .tryMap { data, response -> Int in
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw NetworkError.network(description: "Couldn't create URL")
+ }
+ return httpResponse.statusCode
+ }
+ .mapError { error in
+ NetworkError.network(description: error.localizedDescription)
+ }
+ .eraseToAnyPublisher()
+ }
+
func decode(data: Data, dataType: T.Type) -> AnyPublisher {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
- // Justλ μ€μ§ νλμ κ°λ§μ μΆλ ₯νκ³ λλκ²λλ κ°μ₯ λ¨μν ννμ Publisher
- // Combine Framework μμ λΉνΈμΈννλ‘ μ 곡νλ Publisher
- // μΈμλ‘ λ°λ κ°μ νμ
μ Output νμ
μΌλ‘ μ€ν¨νμ
μ νμ Neverλ‘ λ¦¬ν΄
return Just(data)
.decode(type: T.self, decoder: decoder)
- // κΈ°λ³Έμ μΌλ‘ mapκ³Ό κ°μμν . errorκ° λ°μνλ©΄ ν΄λΉ error λ₯Ό λ΄κ° μνλ error νμ
μΌλ‘ λ°κΏμ€λ€.
.mapError { error in
.parsing(description: error.localizedDescription)
}
.eraseToAnyPublisher()
- // Operationμμ λ°μ΄ν°λ₯Ό μ²λ¦¬ν λ Operation μνΈ κ° μλ¬ μ²λ¦¬λ νΉμ μ€νΈλ¦Ό μ μ΄λ₯Ό μν΄μ
- // λ°μ΄ν° νμμ μμμΌ νμ§λ§ Subscriber μκ² μ λ¬λ λ νμκ° μλ€.
- // λ°λΌμ μ΅μ’
μ μΈ ννλ‘ λ°μ΄ν°λ₯Ό μ λ¬ν λ eraseToAnyPublisher() λ₯Ό μ¬μ©νκ² λλ€.
+ }
+
+ func encode(anyData: T) -> Result {
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = .prettyPrinted
+
+ guard let data = try? encoder.encode(anyData) else {
+ return Result.failure(NetworkError.encoding(description: "encoding Failure"))
+ }
+ return Result.success(data)
}
}
enum NetworkError: Error {
+ case encoding(description: String)
case parsing(description: String)
case network(description: String)
}
+
+public enum HttpMethod: String {
+ case get = "GET"
+ case post = "POST"
+}
diff --git a/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailRepository.swift b/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailRepository.swift
new file mode 100644
index 000000000..8d483db77
--- /dev/null
+++ b/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailRepository.swift
@@ -0,0 +1,38 @@
+//
+// BanchanDetailRepository.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import Combine
+
+protocol BanchanDetailRepository {
+ func fetchBanchanDetail(hash: Int, completion: @escaping (BanchanDetail?) -> Void)
+}
+
+class DefaultBanchanDetailRepository: BanchanDetailRepository {
+ private let network: NetworkService
+ private var subscriptions = Set()
+
+ init(network: NetworkService) {
+ self.network = network
+ }
+
+ func fetchBanchanDetail(hash: Int, completion: @escaping (BanchanDetail?) -> Void) {
+ let endPoint = BanchanDetailAPIEndpoint(path: "\(hash)", httpMethod: .get)
+
+ network.request(with: endPoint, dataType: BanchanDetailDTO.self)
+ .sink(receiveCompletion: {result in
+ switch result {
+ case .failure(let error):
+ print(error)
+ case .finished:
+ break
+ }
+ }, receiveValue: { banchanDetailDTO in
+ completion(banchanDetailDTO.toDomain())
+ })
+ .store(in: &subscriptions)
+ }
+}
diff --git a/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailViewModel.swift b/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailViewModel.swift
new file mode 100644
index 000000000..690f47e2f
--- /dev/null
+++ b/iOS/SideDish/SideDish/Data/Repositories/BanchanDetailViewModel.swift
@@ -0,0 +1,69 @@
+//
+// BanchanDetailViewModel.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import UIKit
+import Combine
+
+struct BanchanDetailViewModelAction {
+ let popBanchanDetail: () -> Void
+}
+enum AlertMessage: String {
+ case success = "μ£Όλ¬Έμ΄ μλ£λμμ΅λλ€."
+ case failure = "μ¬κ³ κ° λΆμ‘±ν©λλ€."
+}
+
+
+class BanchanDetailViewModel {
+
+ private var fetchBanchanDetailUseCase: FetchBanchanDetailUseCase
+ private var orderBanchanDetailUseCase: OrderBanchanUseCase
+ private var action: BanchanDetailViewModelAction
+ @Published var banchanDetail: BanchanDetail!
+ private var hash: Int
+
+ init(hash: Int, fetchBanchanDetailUseCase: FetchBanchanDetailUseCase, action: BanchanDetailViewModelAction) {
+ self.hash = hash
+ self.fetchBanchanDetailUseCase = fetchBanchanDetailUseCase
+ self.orderBanchanDetailUseCase = DefaultOrderBanchanUseCase(network: DefaultNetworkSerivce())
+ self.action = action
+ fetchBanchanDetail()
+ }
+
+ func fetchBanchanDetail() {
+ fetchBanchanDetailUseCase.execute(hash: hash) { (banchanDetail) in
+ self.banchanDetail = banchanDetail
+ }
+ }
+
+ func didTappedOrderButton(quantity: Int, completion: @escaping (Int?, NetworkError?) -> Void) {
+ orderBanchanDetailUseCase.execute(hash: hash, quantity: quantity) { (result, error) in
+ completion(result,error)
+ }
+ }
+
+ func popBanchanDetail() {
+ action.popBanchanDetail()
+ }
+
+ func makeAlert(message: AlertMessage) -> UIAlertController {
+ let alert = UIAlertController(title: "", message: "", preferredStyle: .alert)
+
+ switch message {
+ case .failure:
+ alert.message = message.rawValue
+ let okAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
+ alert.addAction(okAction)
+ case .success:
+ alert.message = message.rawValue
+ let okAction = UIAlertAction(title: "OK", style: .cancel, handler: { _ in
+ self.popBanchanDetail()
+ })
+ alert.addAction(okAction)
+ }
+ return alert
+ }
+}
diff --git a/iOS/SideDish/SideDish/Data/Repositories/BanchanListRepository.swift b/iOS/SideDish/SideDish/Data/Repositories/BanchanListRepository.swift
new file mode 100644
index 000000000..e1bdd704b
--- /dev/null
+++ b/iOS/SideDish/SideDish/Data/Repositories/BanchanListRepository.swift
@@ -0,0 +1,49 @@
+//
+// BanchanListRepository.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import CoreData
+import Combine
+
+protocol BanchanListRepository {
+ func fetchBanchanList(section: Section, completion: @escaping ([Banchan]?) -> Void)
+}
+
+class DefaultBanchanListRepository: BanchanListRepository {
+ private let network: NetworkService
+ private let banchanListStorage: CoreDataBanchanListStorage
+ private var subscriptions = Set()
+
+ init(network: NetworkService, storage: CoreDataBanchanListStorage) {
+ self.network = network
+ self.banchanListStorage = storage
+ }
+
+ func fetchBanchanList(section: Section, completion: @escaping ([Banchan]?) -> Void) {
+ banchanListStorage.fetch(section: section) { result in
+ switch result {
+ case .success(let banchans):
+ completion(banchans)
+ case .failure(_):
+ print("coredata Failure")
+ let endPoint = BanchanAPIEndpoint(path: section.path(), httpMethod: .get)
+ self.network.request(with: endPoint, dataType: BanchanListDTO.self)
+ .sink(receiveCompletion: { result in
+ switch result {
+ case .failure(let error):
+ print(error)
+ case .finished:
+ break
+ }
+ }, receiveValue: { banchanListDTO in
+ self.banchanListStorage.save(section: section, requestDTO: banchanListDTO)
+ completion(banchanListDTO.toDomain())
+ })
+ .store(in: &self.subscriptions)
+ }
+ }
+ }
+}
diff --git a/iOS/SideDish/SideDish/Domain/Entities/BanchanDetail.swift b/iOS/SideDish/SideDish/Domain/Entities/BanchanDetail.swift
index 183bbc4c0..751620363 100644
--- a/iOS/SideDish/SideDish/Domain/Entities/BanchanDetail.swift
+++ b/iOS/SideDish/SideDish/Domain/Entities/BanchanDetail.swift
@@ -8,15 +8,16 @@
import Foundation
struct BanchanDetail {
- private var topImage: URL
- private var thumbImages: [URL]
- private var detailImage: [URL]
- private var description: String
- private var title: String
- private var deliveryInfo: String
- private var deliveryFee: String
- private var point: Int
- private var netPrice: Int
- private var salePrice: Int?
- private var badges: [PriceType]
+ private (set) var topImage: String
+ private (set) var thumbImages: [String]
+ private (set) var detailSection: [String]
+ private (set) var productDescription: String
+ private (set) var title: String
+ private (set) var deliveryInfo: String
+ private (set) var deliveryFee: String
+ private (set) var point: String
+ private (set) var nPrice: String?
+ private (set) var sPrice: String
+ private (set) var badges: [String]
+ private (set) var inStock: Bool
}
diff --git a/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanDetailUseCase.swift b/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanDetailUseCase.swift
new file mode 100644
index 000000000..577c4ff80
--- /dev/null
+++ b/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanDetailUseCase.swift
@@ -0,0 +1,27 @@
+//
+// File.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/27.
+//
+
+import Combine
+
+protocol FetchBanchanDetailUseCase {
+ func execute(hash: Int, completion: @escaping (BanchanDetail?)->Void)
+}
+
+class DefaultFetchBanchanDetailUseCase: FetchBanchanDetailUseCase {
+
+ private let banchanDetailRepository: BanchanDetailRepository
+
+ init(banchanDetailRepository: BanchanDetailRepository) {
+ self.banchanDetailRepository = banchanDetailRepository
+ }
+
+ func execute(hash: Int, completion: @escaping (BanchanDetail?)->Void) {
+ banchanDetailRepository.fetchBanchanDetail(hash: hash) { (banchanDetail) in
+ completion(banchanDetail)
+ }
+ }
+}
diff --git a/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanListUseCase.swift b/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanListUseCase.swift
index 5ce90a430..cab24715f 100644
--- a/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanListUseCase.swift
+++ b/iOS/SideDish/SideDish/Domain/UseCases/FetchBanchanListUseCase.swift
@@ -5,27 +5,23 @@
// Created by μ§λΆ on 2021/04/21.
//
-import Foundation
import Combine
-struct FetchBanchanListUseCase {
- private var subscriptions = Set()
-
- mutating func fetchBanchanList(network: NetworkRequest, baseURL: String, section: String, completion: @escaping ([Banchan]?) -> Void) {
- let url = baseURL+section
+protocol FetchBanchanListUseCase {
+ func execute(section: Section, completion: @escaping ([Banchan]?)->Void)
+}
- network.request(with: url, dataType: BanchanListDTO.self, httpMethod: .get)
- .receive(on: DispatchQueue.main)
- .sink(receiveCompletion: { result in
- switch result {
- case .failure(let error):
- print(error)
- case .finished:
- break
- }
- }, receiveValue: { banchanListDTO in
- completion(banchanListDTO.toDomain())
- })
- .store(in: &subscriptions)
+class DefaultFetchBanchanListUseCase: FetchBanchanListUseCase {
+
+ private let banchanListRepository: BanchanListRepository
+
+ init(banchanListRepository: BanchanListRepository) {
+ self.banchanListRepository = banchanListRepository
+ }
+
+ func execute(section: Section, completion: @escaping ([Banchan]?)->Void) {
+ banchanListRepository.fetchBanchanList(section: section) { (banchans) in
+ completion(banchans)
+ }
}
}
diff --git a/iOS/SideDish/SideDish/Domain/UseCases/FetchImageUseCase.swift b/iOS/SideDish/SideDish/Domain/UseCases/FetchImageUseCase.swift
deleted file mode 100644
index c4e5e03a4..000000000
--- a/iOS/SideDish/SideDish/Domain/UseCases/FetchImageUseCase.swift
+++ /dev/null
@@ -1,17 +0,0 @@
-//
-// FetchImageUseCase.swift
-// AlamofirePratice
-//
-// Created by μ§λΆ on 2021/04/21.
-//
-
-import Foundation
-
-struct FetchImageUseCase {
-// static func fetch(network: NetworkRequest, imgURL: String, completion: @escaping (Data?) -> Void) {
-// network.request(url: imgURL, httpMethod: .get) { dataDummy in
-// let data = dataDummy.data
-// completion(data)
-// }
-// }
-}
diff --git a/iOS/SideDish/SideDish/Domain/UseCases/OrderBanchanUseCase.swift b/iOS/SideDish/SideDish/Domain/UseCases/OrderBanchanUseCase.swift
new file mode 100644
index 000000000..d467d48b9
--- /dev/null
+++ b/iOS/SideDish/SideDish/Domain/UseCases/OrderBanchanUseCase.swift
@@ -0,0 +1,44 @@
+//
+// OrderBanchanUseCase.swift
+// SideDish
+//
+// Created by μ¬μλ―Ό on 2021/04/28.
+//
+
+import Combine
+import UIKit
+
+protocol OrderBanchanUseCase {
+ func execute(hash: Int, quantity: Int, completion: @escaping (Int?, NetworkError?) -> Void)
+}
+
+class DefaultOrderBanchanUseCase: OrderBanchanUseCase {
+
+ private var network: NetworkService
+ private var subscriptions = Set()
+ init(network: NetworkService) {
+ self.network = network
+ }
+
+ func execute(hash: Int, quantity: Int, completion: @escaping (Int?, NetworkError?) -> Void) {
+ let endPoint = OrderBanchanAPIEndPoint(path: "\(hash)", httpMethod: .post)
+ let data = body(quantity: quantity)
+ network.post(with: endPoint, data: data)
+ .sink(receiveCompletion: { result in
+ switch result {
+ case .failure(let error):
+ completion(nil, error)
+ case .finished:
+ break
+ }
+ }, receiveValue: { Result in
+ completion(Result, nil)
+ })
+ .store(in: &subscriptions)
+ }
+}
+
+
+struct body: Encodable {
+ var quantity: Int
+}
diff --git a/iOS/SideDish/SideDish/PersistentStorages/BanchanListStorage.swift b/iOS/SideDish/SideDish/PersistentStorages/BanchanListStorage.swift
new file mode 100644
index 000000000..ef96bdabd
--- /dev/null
+++ b/iOS/SideDish/SideDish/PersistentStorages/BanchanListStorage.swift
@@ -0,0 +1,95 @@
+//
+// BanchanListStorage.swift
+// SideDish
+//
+// Created by μ§λΆ on 2021/04/29.
+//
+
+import Foundation
+import CoreData
+
+enum CoreDataError: Error {
+ case fetError
+}
+
+class CoreDataBanchanListStorage {
+ private let coreDataManager: CoreDataManager
+
+ init(manager: CoreDataManager = CoreDataManager.shared) {
+ self.coreDataManager = manager
+ setSectionEntity()
+ }
+
+ func setSectionEntity() {
+ let requset: NSFetchRequest = BanchanSectionEntity.fetchRequest()
+
+ coreDataManager.performBackgroundTask { context in
+ do {
+ let sectionEntities = try context.fetch(requset)
+ if sectionEntities.count >= 3 { return }
+ else {
+ try Section.allCases.forEach { section in
+ let banchanSectionEntity = BanchanSectionEntity.init(context: context)
+ try context.save()
+ }
+ }
+ } catch {
+ print("error in sectionEntity")
+ }
+ }
+ }
+ func save(section: Section, requestDTO: BanchanListDTO) {
+ let request: NSFetchRequest = BanchanSectionEntity.fetchRequest()
+
+ coreDataManager.performBackgroundTask { context in
+ do{
+ let entities = requestDTO.toEntity(with: context)
+ let sectionEntity = try context.fetch(request)
+
+ entities.forEach { entity in
+ sectionEntity[section.rawValue].addToEntities(entity)
+ }
+ try context.save()
+ } catch {
+ fatalError()
+ }
+ }
+ }
+
+ func fetch(section: Section, handler: @escaping (Result<[Banchan],CoreDataError>) -> Void) {
+ coreDataManager.performBackgroundTask { context in
+ do {
+ let fetchResult: NSFetchRequest = BanchanSectionEntity.fetchRequest()
+ let requestEntity = try context.fetch(fetchResult)
+
+ var sectionEntity: BanchanSectionEntity
+ let index = section.rawValue
+
+ if requestEntity[section.rawValue].entities?.array.count == 0 {
+ handler(.failure(.fetError))
+ return
+ }
+ sectionEntity = requestEntity[index]
+ let entities = sectionEntity.entities?.array as! [BanchanEntity]
+ let newEntities = entities.map { entity in
+ entity.toDomain()
+ }
+ handler(.success(newEntities))
+ } catch {
+ handler(.failure(.fetError))
+ }
+ }
+ }
+
+ func deleteAll(request: NSFetchRequest) {
+ coreDataManager.performBackgroundTask { context in
+ let request: NSFetchRequest = T.fetchRequest()
+ let delete = NSBatchDeleteRequest(fetchRequest: request)
+ do {
+ try context.execute(delete)
+ } catch {
+ print(error.localizedDescription)
+ }
+ }
+ }
+}
diff --git a/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataManager.swift b/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataManager.swift
new file mode 100644
index 000000000..ac64dbdc7
--- /dev/null
+++ b/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataManager.swift
@@ -0,0 +1,43 @@
+//
+// CoreDataManager.swift
+// SideDish
+//
+// Created by μ§λΆ on 2021/04/29.
+//
+
+import Foundation
+import CoreData
+
+class CoreDataManager {
+ static let shared = CoreDataManager()
+
+ private init() {}
+
+ private lazy var persistentContainer: NSPersistentContainer = {
+ let container = NSPersistentContainer(name: "CoreDataStorage")
+ container.loadPersistentStores { _, error in
+ if let error = error as NSError? {
+ // TODO: - Log to Crashlytics
+ assertionFailure("CoreDataStorage Unresolved error \(error), \(error.userInfo)")
+ }
+ }
+ return container
+ }()
+
+ // MARK: - Core Data Saving support
+ func saveContext() {
+ let context = persistentContainer.viewContext
+ if context.hasChanges {
+ do {
+ try context.save()
+ } catch {
+ // TODO: - Log to Crashlytics
+ assertionFailure("CoreDataStorage Unresolved error \(error), \((error as NSError).userInfo)")
+ }
+ }
+ }
+
+ func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
+ persistentContainer.performBackgroundTask(block)
+ }
+}
diff --git a/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataStorage.xcdatamodeld/CoreDataStorage.xcdatamodel/contents b/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataStorage.xcdatamodeld/CoreDataStorage.xcdatamodel/contents
new file mode 100644
index 000000000..3da5f495a
--- /dev/null
+++ b/iOS/SideDish/SideDish/PersistentStorages/CoreDataStorage/CoreDataStorage.xcdatamodeld/CoreDataStorage.xcdatamodel/contents
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/iOS/SideDish/SideDish/Presentaion/BanchanList/View/BanchanListViewController.swift b/iOS/SideDish/SideDish/Presentaion/BanchanList/View/BanchanListViewController.swift
index 54f4cfda5..d02962673 100644
--- a/iOS/SideDish/SideDish/Presentaion/BanchanList/View/BanchanListViewController.swift
+++ b/iOS/SideDish/SideDish/Presentaion/BanchanList/View/BanchanListViewController.swift
@@ -13,13 +13,24 @@ class BanchanListViewController: UIViewController {
@IBOutlet weak var banchanCollectionView: BanchanCollectionView!
- private var viewModel = BanchanListViewModel()
+ static let storyboardName = "Main"
+ static let storyboardID = "BanchanListViewController"
+
+ private var viewModel: BanchanListViewModel!
private lazy var dataSource = configureDataSource()
private var subscriptions = Set()
typealias DataSource = UICollectionViewDiffableDataSource
typealias Snapshot = NSDiffableDataSourceSnapshot
- typealias Section = BanchanListViewModel.Section
+
+ static func create(with viewModel: BanchanListViewModel) -> BanchanListViewController {
+ let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle.main)
+ guard let vc = storyboard.instantiateViewController(identifier: storyboardID) as? BanchanListViewController else {
+ return BanchanListViewController()
+ }
+ vc.viewModel = viewModel
+ return vc
+ }
override func viewDidLoad() {
super.viewDidLoad()
@@ -99,6 +110,10 @@ extension BanchanListViewController: UICollectionViewDelegate, UICollectionViewD
let cellSize = CGSize(width: 343, height: 130)
return cellSize
}
+
+ func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+ viewModel.didSelectedItem(indexPath: indexPath)
+ }
}
// MARK: - UITapGestureRecongnizer Custom Class
diff --git a/iOS/SideDish/SideDish/Presentaion/BanchanList/ViewModel/BanchanListViewModel.swift b/iOS/SideDish/SideDish/Presentaion/BanchanList/ViewModel/BanchanListViewModel.swift
index ccbcf827e..8bcd1731c 100644
--- a/iOS/SideDish/SideDish/Presentaion/BanchanList/ViewModel/BanchanListViewModel.swift
+++ b/iOS/SideDish/SideDish/Presentaion/BanchanList/ViewModel/BanchanListViewModel.swift
@@ -8,57 +8,49 @@
import Foundation
import Combine
+struct BanchanListViewModelAction {
+ let showBanchanDetails: ((Banchan) -> Void)
+}
+
class BanchanListViewModel {
- enum Section: Int, CaseIterable{
- case main = 0
- case soup, side
-
- func description() -> String {
- switch self {
- case .main:
- return "λͺ¨λκ° μ’μνλ λ λ ν λ©μΈμ리"
- case .soup:
- return "μ μ±μ΄ λ΄κΈ΄ λ¨λλ¨λ κ΅λ¬Όμ리"
- case .side:
- return "μνμ νμ±νκ² νλ μ κ°ν λ°λ°μ°¬"
- }
- }
- }
- @Published var menu: Dictionary
- private let baseURL = "http://ec2-54-180-115-20.ap-northeast-2.compute.amazonaws.com:8080/"
+ @Published var menu: [[Banchan]]
private var fetchBanchanListUseCase: FetchBanchanListUseCase
- private var network = NetworkSerivce.shared
+ private var action: BanchanListViewModelAction
- init() {
- self.menu = [:]
- self.fetchBanchanListUseCase = FetchBanchanListUseCase.init()
+ init(fetchBanchanListUseCase: FetchBanchanListUseCase, action: BanchanListViewModelAction) {
+ self.fetchBanchanListUseCase = fetchBanchanListUseCase
+ self.menu = []
+ self.action = action
+ configureMenu()
fetchMenu()
}
func count(section: Section) -> Int {
- return menu[section]?.count ?? 0
+ return menu[section.rawValue].count
}
func fetchMenu() {
- fetchBanchanListUseCase.fetchBanchanList(network: network, baseURL: baseURL, section: "main", completion: { banchans in
- guard let banchans = banchans else { return }
- self.menu[.main] = banchans
- })
-
- fetchBanchanListUseCase.fetchBanchanList(network: network, baseURL: baseURL, section: "soup", completion: { banchans in
- guard let banchans = banchans else { return }
- self.menu[.soup] = banchans
- })
-
- fetchBanchanListUseCase.fetchBanchanList(network: network, baseURL: baseURL, section: "side", completion: { banchans in
- guard let banchans = banchans else { return }
- self.menu[.side] = banchans
+ Section.allCases.forEach { (section) in
+ fetchBanchanListUseCase.execute(section: section) { (banchans) in
+ self.menu[section.rawValue] = banchans ?? []
+ }
+ }
+ }
+
+ func configureMenu() {
+ Section.allCases.forEach({ _ in
+ menu.append([])
})
}
+ func didSelectedItem(indexPath: IndexPath) {
+ let banchan = menu[indexPath.section][indexPath.row]
+ action.showBanchanDetails(banchan)
+ }
+
func getBanchans(section: Section) -> [Banchan]? {
- return menu[section]
+ return menu[section.rawValue]
}
}
diff --git a/iOS/SideDish/SideDish/Presentaion/DetailBanchan/VIew/BanchanDetailViewController.swift b/iOS/SideDish/SideDish/Presentaion/DetailBanchan/VIew/BanchanDetailViewController.swift
index 913c64588..e95105417 100644
--- a/iOS/SideDish/SideDish/Presentaion/DetailBanchan/VIew/BanchanDetailViewController.swift
+++ b/iOS/SideDish/SideDish/Presentaion/DetailBanchan/VIew/BanchanDetailViewController.swift
@@ -6,10 +6,202 @@
//
import UIKit
+import Combine
class BanchanDetailViewController: UIViewController {
-
+
+ @IBOutlet weak var mainScrollView: UIScrollView!
+ @IBOutlet weak var imageScrollView: UIScrollView!
+ @IBOutlet var imageViews: [UIImageView]!
+
+ @IBOutlet weak var detailDescriptionStackView: UIStackView!
+ @IBOutlet weak var titleLabel: UILabel!
+ @IBOutlet weak var descriptionLabel: UILabel!
+ @IBOutlet weak var sPriceLabel: UILabel!
+ @IBOutlet weak var nPriceLabel: UILabel!
+ @IBOutlet weak var badgeStackView: UIStackView!
+ @IBOutlet weak var pointLabel: UILabel!
+ @IBOutlet weak var deliveryInfoLabel: UILabel!
+ @IBOutlet weak var deliveryFeeLabel: UILabel!
+ @IBOutlet weak var orderCountLabel: UILabel!
+ @IBOutlet weak var plusButton: UIButton!
+ @IBOutlet weak var minusButton: UIButton!
+ @IBOutlet weak var totalPriceLabel: UILabel!
+
+ @IBOutlet weak var orderButton: UIButton!
+ private var quantity: Int = 1
+ private var subscriptions = Set()
+ private var viewModel: BanchanDetailViewModel!
+
+ static let storyboardName = "Main"
+ static let storyboardID = "BanchanDetailViewController"
+
+ static func create(with viewModel: BanchanDetailViewModel) -> BanchanDetailViewController {
+ let storyboard = UIStoryboard(name: storyboardName, bundle: Bundle.main)
+ guard let vc = storyboard.instantiateViewController(identifier: storyboardID) as? BanchanDetailViewController else {
+ return BanchanDetailViewController()
+ }
+ vc.viewModel = viewModel
+ return vc
+ }
+
+
override func viewDidLoad() {
super.viewDidLoad()
+ bind()
+ }
+
+ private func bind() {
+ viewModel.$banchanDetail
+ .receive(on: DispatchQueue.main)
+ .sink(receiveCompletion: { (result) in
+ switch result {
+ case .failure(let error):
+ print(error)
+ case .finished:
+ break
+ }
+ }, receiveValue: { (banchanDetail) in
+ self.configure(banchanDetail: banchanDetail)
+ })
+ .store(in: &subscriptions)
+ }
+
+ @IBAction func buttonTouched(_ sender: UIButton) {
+
+ if sender == plusButton {
+ quantity += 1
+ orderCountLabel.text = "\(quantity)"
+
+ } else {
+ if quantity != 1 {
+ quantity -= 1
+ orderCountLabel.text = "\(quantity)"
+ }
+ }
+
+ totalPriceLabel.text = decimalWon(value: viewModel.banchanDetail.sPrice)
+
+ }
+
+ func decimalWon(value: String) -> String {
+ let price = Int(value.replacingOccurrences(of: ",", with: "").replacingOccurrences(of: "μ", with: "")) ?? 0
+ let totalPrice = price * quantity
+
+ let numberFormatter = NumberFormatter()
+ numberFormatter.numberStyle = .decimal
+ let result = numberFormatter.string(from: NSNumber(value: totalPrice))! + "μ"
+
+ return result
+ }
+
+ @IBAction func orderButtonTouched(_ sender: UIButton) {
+ viewModel.didTappedOrderButton(quantity: quantity) { result, error in
+ var alert: UIAlertController!
+
+ switch result {
+ case 200:
+ DispatchQueue.main.async {
+ alert = self.viewModel.makeAlert(message: .success)
+ self.present(alert, animated: true, completion: nil)
+ }
+ default:
+ DispatchQueue.main.async {
+ alert = self.viewModel.makeAlert(message: .failure)
+ self.present(alert, animated: true, completion: nil)
+ }
+ }
+ }
+ }
+
+ private func configure(banchanDetail: BanchanDetail?) {
+
+ guard let banchanDetail = banchanDetail else {
+ return
+ }
+ self.title = viewModel.banchanDetail.title
+ self.titleLabel.text = banchanDetail.title
+ self.descriptionLabel.text = banchanDetail.productDescription
+ self.sPriceLabel.text = banchanDetail.sPrice
+ setNPrice(text: banchanDetail.nPrice)
+ setBadges(badges: banchanDetail.badges)
+ self.pointLabel.text = banchanDetail.point
+ self.deliveryInfoLabel.text = banchanDetail.deliveryInfo
+ self.totalPriceLabel.text = banchanDetail.sPrice
+ setDeliveryFee(text: "\(banchanDetail.deliveryFee) (40,000μ μ΄μ ꡬ맀 μ 무λ£)")
+ configureThumbImage(images: banchanDetail.thumbImages)
+ configureDescriptionImage(images: banchanDetail.detailSection)
+ configureOrderButton(stock: banchanDetail.inStock)
+ }
+
+ private func setNPrice(text: String?) {
+ guard let text = text else {
+ nPriceLabel.isHidden = true
+ return
+ }
+
+ let strikeThroughAttributeStyle = [NSAttributedString.Key.strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]
+ nPriceLabel.attributedText = NSAttributedString(string: text, attributes: strikeThroughAttributeStyle)
+ }
+
+ private func setDeliveryFee(text: String) {
+ let font = UIFont.boldSystemFont(ofSize: 14)
+
+ let attributedStr = NSMutableAttributedString(string: text)
+
+ attributedStr.addAttribute(.font, value: font, range: (text as NSString).range(of: "(40,000μ μ΄μ ꡬ맀 μ 무λ£)"))
+ attributedStr.addAttribute(.foregroundColor, value: UIColor.black, range: (text as NSString).range(of: "40,000μ μ΄μ ꡬ맀 μ 무λ£"))
+ deliveryFeeLabel.attributedText = attributedStr
+ }
+
+ private func setBadges(badges: [String]?) {
+ badgeStackView.arrangedSubviews.forEach {
+ $0.removeFromSuperview()
+ }
+
+ guard let badges = badges else {
+ badgeStackView.isHidden = true
+ return
+ }
+ badges.forEach({ (badge) in
+ let badgeLabel = BadgeLabel()
+ badgeLabel.configure(text: badge)
+ badgeStackView.addArrangedSubview(badgeLabel)
+ })
+ }
+
+ private func configureThumbImage(images: [String]) {
+ imageViews.removeAll()
+ for i in 0.. BanchanListViewController
+ func makeBanchanDetailViewController(hash: Int, action: BanchanDetailViewModelAction) -> BanchanDetailViewController
+}
+
+class BanchanSceneFlowCoordinator {
+
+ private weak var navigationController: UINavigationController?
+ private var banchanListVC: BanchanListViewController?
+ private let dependencies: BanchanSceneFlowCoordinatorDependencies
+
+ init(navigationController: UINavigationController, dependencies: BanchanSceneFlowCoordinatorDependencies) {
+ self.navigationController = navigationController
+ self.dependencies = dependencies
+ }
+
+ func start() {
+ let action = BanchanListViewModelAction(showBanchanDetails: showBanchanDetails(banchan:))
+ let vc = dependencies.makeBanchanListViewController(action: action)
+ self.navigationController?.pushViewController(vc, animated: true)
+ self.banchanListVC = vc
+ }
+
+ func showBanchanDetails(banchan: Banchan) {
+ let action = BanchanDetailViewModelAction(popBanchanDetail: popBanchanDetail)
+ let vc = dependencies.makeBanchanDetailViewController(hash: banchan.detailHash, action: action)
+ self.navigationController?.pushViewController(vc, animated: true)
+ }
+
+ func popBanchanDetail() {
+ self.navigationController?.popViewController(animated: true)
+ }
+
+}
diff --git a/iOS/SideDish/SideDish/SupportingFiles/SceneDelegate.swift b/iOS/SideDish/SideDish/SupportingFiles/SceneDelegate.swift
deleted file mode 100644
index 420f341dc..000000000
--- a/iOS/SideDish/SideDish/SupportingFiles/SceneDelegate.swift
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// SceneDelegate.swift
-// SideDish
-//
-// Created by μ¬μλ―Ό on 2021/04/20.
-//
-
-import UIKit
-
-class SceneDelegate: UIResponder, UIWindowSceneDelegate {
-
- var window: UIWindow?
-
-
- func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
- guard let _ = (scene as? UIWindowScene) else { return }
- }
-}
-