diff --git a/Side-dish/Side-dish.xcodeproj/project.pbxproj b/Side-dish/Side-dish.xcodeproj/project.pbxproj index 6a55c8be3..6615a523d 100644 --- a/Side-dish/Side-dish.xcodeproj/project.pbxproj +++ b/Side-dish/Side-dish.xcodeproj/project.pbxproj @@ -13,7 +13,7 @@ 8844B7D4262DBA3F00FA49E9 /* SideDishes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8844B7D3262DBA3F00FA49E9 /* SideDishes.swift */; }; BFCE4953262D4891006C0882 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE4952262D4891006C0882 /* AppDelegate.swift */; }; BFCE4955262D4891006C0882 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE4954262D4891006C0882 /* SceneDelegate.swift */; }; - BFCE4957262D4891006C0882 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE4956262D4891006C0882 /* ViewController.swift */; }; + BFCE4957262D4891006C0882 /* SideDishViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE4956262D4891006C0882 /* SideDishViewController.swift */; }; BFCE495C262D4895006C0882 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFCE495B262D4895006C0882 /* Assets.xcassets */; }; BFCE495F262D4895006C0882 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFCE495D262D4895006C0882 /* LaunchScreen.storyboard */; }; BFCE496F262D883F006C0882 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFCE496E262D883F006C0882 /* Main.storyboard */; }; @@ -21,6 +21,9 @@ BFCE497A262DC205006C0882 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE4979262DC205006C0882 /* HTTPMethod.swift */; }; BFCE497D262DC22A006C0882 /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE497C262DC22A006C0882 /* NetworkError.swift */; }; BFCE4980262DC3F2006C0882 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE497F262DC3F2006C0882 /* NetworkManager.swift */; }; + BFCE49B7262E751F006C0882 /* SideDishUseCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE49B6262E751E006C0882 /* SideDishUseCase.swift */; }; + BFCE49BD262E7759006C0882 /* SideDishViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE49BC262E7759006C0882 /* SideDishViewModel.swift */; }; + BFCE49C2262E8918006C0882 /* DIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFCE49C1262E8918006C0882 /* DIContainer.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -31,7 +34,7 @@ BFCE494F262D4891006C0882 /* Side-dish.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Side-dish.app"; sourceTree = BUILT_PRODUCTS_DIR; }; BFCE4952262D4891006C0882 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; BFCE4954262D4891006C0882 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - BFCE4956262D4891006C0882 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; + BFCE4956262D4891006C0882 /* SideDishViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDishViewController.swift; sourceTree = ""; }; BFCE495B262D4895006C0882 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; BFCE495E262D4895006C0882 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; BFCE4960262D4895006C0882 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -40,6 +43,9 @@ BFCE4979262DC205006C0882 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; BFCE497C262DC22A006C0882 /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; BFCE497F262DC3F2006C0882 /* NetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; }; + BFCE49B6262E751E006C0882 /* SideDishUseCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDishUseCase.swift; sourceTree = ""; }; + BFCE49BC262E7759006C0882 /* SideDishViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SideDishViewModel.swift; sourceTree = ""; }; + BFCE49C1262E8918006C0882 /* DIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIContainer.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -99,11 +105,13 @@ BFCE4951262D4891006C0882 /* Side-dish */ = { isa = PBXGroup; children = ( - 8844B7CD262DB62C00FA49E9 /* DataLayer */, BFCE4952262D4891006C0882 /* AppDelegate.swift */, BFCE4954262D4891006C0882 /* SceneDelegate.swift */, + BFCE49C1262E8918006C0882 /* DIContainer.swift */, + 8844B7CD262DB62C00FA49E9 /* DataLayer */, + BFCE49B4262E74FF006C0882 /* DomainLayer */, + BFCE49BB262E7742006C0882 /* PresentationLayer */, 8844B7C1262D6CEC00FA49E9 /* View */, - BFCE4956262D4891006C0882 /* ViewController.swift */, BFCE496E262D883F006C0882 /* Main.storyboard */, BFCE495B262D4895006C0882 /* Assets.xcassets */, BFCE495D262D4895006C0882 /* LaunchScreen.storyboard */, @@ -123,6 +131,31 @@ path = Network; sourceTree = ""; }; + BFCE49B4262E74FF006C0882 /* DomainLayer */ = { + isa = PBXGroup; + children = ( + BFCE49B5262E7513006C0882 /* UseCase */, + ); + path = DomainLayer; + sourceTree = ""; + }; + BFCE49B5262E7513006C0882 /* UseCase */ = { + isa = PBXGroup; + children = ( + BFCE49B6262E751E006C0882 /* SideDishUseCase.swift */, + ); + path = UseCase; + sourceTree = ""; + }; + BFCE49BB262E7742006C0882 /* PresentationLayer */ = { + isa = PBXGroup; + children = ( + BFCE49BC262E7759006C0882 /* SideDishViewModel.swift */, + BFCE4956262D4891006C0882 /* SideDishViewController.swift */, + ); + path = PresentationLayer; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -195,14 +228,17 @@ buildActionMask = 2147483647; files = ( 8844B7D0262DB6A500FA49E9 /* Item.swift in Sources */, - BFCE4957262D4891006C0882 /* ViewController.swift in Sources */, + BFCE4957262D4891006C0882 /* SideDishViewController.swift in Sources */, BFCE4980262DC3F2006C0882 /* NetworkManager.swift in Sources */, BFCE4953262D4891006C0882 /* AppDelegate.swift in Sources */, BFCE4955262D4891006C0882 /* SceneDelegate.swift in Sources */, + BFCE49B7262E751F006C0882 /* SideDishUseCase.swift in Sources */, + BFCE49C2262E8918006C0882 /* DIContainer.swift in Sources */, BFCE497A262DC205006C0882 /* HTTPMethod.swift in Sources */, 8844B7BF262D6C3000FA49E9 /* FoodCardCell.swift in Sources */, BFCE497D262DC22A006C0882 /* NetworkError.swift in Sources */, BFCE4976262DBFE2006C0882 /* EndPoint.swift in Sources */, + BFCE49BD262E7759006C0882 /* SideDishViewModel.swift in Sources */, 8844B7D4262DBA3F00FA49E9 /* SideDishes.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Side-dish/Side-dish.xcodeproj/project.xcworkspace/xcuserdata/hoonhachoi.xcuserdatad/UserInterfaceState.xcuserstate b/Side-dish/Side-dish.xcodeproj/project.xcworkspace/xcuserdata/hoonhachoi.xcuserdatad/UserInterfaceState.xcuserstate index afc83d2ae..f82fc91da 100644 Binary files a/Side-dish/Side-dish.xcodeproj/project.xcworkspace/xcuserdata/hoonhachoi.xcuserdatad/UserInterfaceState.xcuserstate and b/Side-dish/Side-dish.xcodeproj/project.xcworkspace/xcuserdata/hoonhachoi.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Side-dish/Side-dish/DIContainer.swift b/Side-dish/Side-dish/DIContainer.swift new file mode 100644 index 000000000..b28da2daa --- /dev/null +++ b/Side-dish/Side-dish/DIContainer.swift @@ -0,0 +1,17 @@ +// +// DIContainer.swift +// Side-dish +// +// Created by HOONHA CHOI on 2021/04/20. +// + +import Foundation + +class DIContainer { + static func createDI() -> SideDishViewModel { + let networkManage = NetworkManager() + let useCase = SideDishUseCase(networkManager: networkManage) + let viewModel = SideDishViewModel(sideDishUseCase: useCase) + return viewModel + } +} diff --git a/Side-dish/Side-dish/DataLayer/Network/EndPoint.swift b/Side-dish/Side-dish/DataLayer/Network/EndPoint.swift index 12bb732bd..7392e06bd 100644 --- a/Side-dish/Side-dish/DataLayer/Network/EndPoint.swift +++ b/Side-dish/Side-dish/DataLayer/Network/EndPoint.swift @@ -21,8 +21,8 @@ enum Endpoint { } } -enum Path: String { - case main = "1main1" - case soup = "soup" - case side = "side" +enum Path: String, CaseIterable { + case main + case soup + case side } diff --git a/Side-dish/Side-dish/DataLayer/Network/NetworkManager.swift b/Side-dish/Side-dish/DataLayer/Network/NetworkManager.swift index 3b8d00bcb..8b09e3e47 100644 --- a/Side-dish/Side-dish/DataLayer/Network/NetworkManager.swift +++ b/Side-dish/Side-dish/DataLayer/Network/NetworkManager.swift @@ -8,9 +8,13 @@ import Foundation import Combine -class NetworkManager { +protocol NetworkManageable { + func requestResource(path: Path, method: HTTPMethod) -> AnyPublisher +} + +class NetworkManager: NetworkManageable { - func getResource(path: Path, method: HTTPMethod) -> AnyPublisher { + func requestResource(path: Path, method: HTTPMethod) -> AnyPublisher { guard let urlRequest = makeURLRequest(path: path, method: method) else { return Fail(error: NetworkError.invalidURL).eraseToAnyPublisher() } @@ -18,23 +22,19 @@ class NetworkManager { .mapError { _ in NetworkError.invalidRequest } - .tryMap{ data , response -> Data in + .flatMap { data, response -> AnyPublisher in guard let httpResponse = response as? HTTPURLResponse else { - throw NetworkError.invalidResponse + return Fail(error: NetworkError.invalidResponse).eraseToAnyPublisher() } guard 200..<300 ~= httpResponse.statusCode else { - throw NetworkError.invalidStatusCode(httpResponse.statusCode) - } - guard !data.isEmpty else { - throw NetworkError.emptyData + return Fail(error:NetworkError.invalidStatusCode(httpResponse.statusCode)).eraseToAnyPublisher() } - return data - } - .decode(type: SideDishes.self, decoder: JSONDecoder()) - .mapError { _ in - NetworkError.failParsing - } - .eraseToAnyPublisher() + return Just(data) + .decode(type: SideDishes.self, decoder: JSONDecoder()) + .mapError { _ in + NetworkError.failParsing + }.eraseToAnyPublisher() + }.eraseToAnyPublisher() } private func makeURLRequest(path: Path, method: HTTPMethod) -> URLRequest? { diff --git a/Side-dish/Side-dish/DomainLayer/UseCase/SideDishUseCase.swift b/Side-dish/Side-dish/DomainLayer/UseCase/SideDishUseCase.swift new file mode 100644 index 000000000..4c6d59d95 --- /dev/null +++ b/Side-dish/Side-dish/DomainLayer/UseCase/SideDishUseCase.swift @@ -0,0 +1,26 @@ +// +// UseCase.swift +// Side-dish +// +// Created by HOONHA CHOI on 2021/04/20. +// + +import Foundation +import Combine + +protocol SideDishProtocol { + func execute(path: Path) -> AnyPublisher +} + +class SideDishUseCase: SideDishProtocol { + + private let networkManager: NetworkManageable + + init(networkManager : NetworkManageable) { + self.networkManager = networkManager + } + + func execute(path: Path) -> AnyPublisher { + return networkManager.requestResource(path: path, method: .get) + } +} diff --git a/Side-dish/Side-dish/Main.storyboard b/Side-dish/Side-dish/Main.storyboard index a4d2ef32d..600a2f714 100644 --- a/Side-dish/Side-dish/Main.storyboard +++ b/Side-dish/Side-dish/Main.storyboard @@ -1,5 +1,5 @@ - + @@ -9,10 +9,10 @@ - + - + @@ -54,24 +54,7 @@ - - - - - - - - - - - - - - - - - - + diff --git a/Side-dish/Side-dish/ViewController.swift b/Side-dish/Side-dish/PresentationLayer/SideDishViewController.swift similarity index 65% rename from Side-dish/Side-dish/ViewController.swift rename to Side-dish/Side-dish/PresentationLayer/SideDishViewController.swift index 6b6437e88..da5f8a22d 100644 --- a/Side-dish/Side-dish/ViewController.swift +++ b/Side-dish/Side-dish/PresentationLayer/SideDishViewController.swift @@ -8,30 +8,35 @@ import UIKit import Combine -class ViewController: UIViewController { +class SideDishViewController: UIViewController { @IBOutlet weak var SideDishCollectionView: UICollectionView! private var cancellable = Set() + private var sideDishViewModel: SideDishViewModel! override func viewDidLoad() { super.viewDidLoad() + navigationController?.navigationBar.isHidden = true SideDishCollectionView.register(FoodCardCell.nib, forCellWithReuseIdentifier: FoodCardCell.identifier) SideDishCollectionView.dataSource = self SideDishCollectionView.delegate = self - - NetworkManager().getResource(path: .main, method: .get).sink { (complete) in - if case .failure(let error) = complete { - print(error) + + sideDishViewModel.test { (t) in + t.forEach { (item) in + print(item.title) } - } receiveValue: { (category) in - print(category) - }.store(in: &cancellable) - + } + sideDishViewModel.occur { (t) in + print("test : \(t)") + } + } + + func depend(sideDishViewModel: SideDishViewModel) { + self.sideDishViewModel = sideDishViewModel } - } -extension ViewController: UICollectionViewDataSource { +extension SideDishViewController: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return 3 } @@ -42,7 +47,7 @@ extension ViewController: UICollectionViewDataSource { } } -extension ViewController: UICollectionViewDelegateFlowLayout { +extension SideDishViewController: UICollectionViewDelegateFlowLayout { func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { return CGSize(width: collectionView.frame.width, height: 130) } diff --git a/Side-dish/Side-dish/PresentationLayer/SideDishViewModel.swift b/Side-dish/Side-dish/PresentationLayer/SideDishViewModel.swift new file mode 100644 index 000000000..855310c4f --- /dev/null +++ b/Side-dish/Side-dish/PresentationLayer/SideDishViewModel.swift @@ -0,0 +1,52 @@ +// +// ViewModel.swift +// Side-dish +// +// Created by HOONHA CHOI on 2021/04/20. +// + +import Foundation +import Combine + +class SideDishViewModel { + + private let sideDishUseCase: SideDishProtocol + private var cancellable = Set() + + @Published var errorMessage = "" + @Published var item: [Item] = [] + + init(sideDishUseCase: SideDishProtocol) { + self.sideDishUseCase = sideDishUseCase + request() + } + + private func request() { + Path.allCases.forEach { (path) in + sideDishUseCase.execute(path: path).sink { (complete) in + if case .failure(let error) = complete { + self.errorMessage = error.message + } + } receiveValue: { (SideDishes) in + self.item = SideDishes.body + }.store(in: &cancellable) + } + + } + + func test(completion: @escaping ([Item]) -> ()){ + $item + .dropFirst() + .sink { (item) in + completion(item) + }.store(in: &cancellable) + } + + func occur(completion: @escaping ((String) ->())) { + $errorMessage + .dropFirst() + .sink { (message) in + completion(message) + }.store(in: &cancellable) + } +} diff --git a/Side-dish/Side-dish/SceneDelegate.swift b/Side-dish/Side-dish/SceneDelegate.swift index c21a88f6a..1a78815de 100644 --- a/Side-dish/Side-dish/SceneDelegate.swift +++ b/Side-dish/Side-dish/SceneDelegate.swift @@ -8,11 +8,21 @@ 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 } + guard let screen = (scene as? UIWindowScene) else { return } + + guard let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController") as? SideDishViewController else { + return + } + + viewController.depend(sideDishViewModel: DIContainer.createDI()) + window = UIWindow(frame: screen.coordinateSpace.bounds) + window?.windowScene = screen + window?.rootViewController = UINavigationController(rootViewController: viewController) + window?.makeKeyAndVisible() } }