From 941ea7885ef45e633ea1d816b4338a1f176ac088 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Mon, 11 Apr 2022 22:37:15 +0200 Subject: [PATCH 001/101] api call implemented in Waiting module. --- Cars Map/Scenes/ApiClient.swift | 4 ++-- Cars Map/Scenes/Waiting/WaitingVC.swift | 2 ++ Cars Map/Scenes/Waiting/WaitingViewModel.swift | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Cars Map/Scenes/ApiClient.swift b/Cars Map/Scenes/ApiClient.swift index 6905823..888485e 100644 --- a/Cars Map/Scenes/ApiClient.swift +++ b/Cars Map/Scenes/ApiClient.swift @@ -22,8 +22,7 @@ class ApiClient: Network { func fetch(completionHandler: @escaping (Result) -> ()) { // if !connected() { completionHandler(.failure(CatAPIError.disconnected)) } - - let url = URL(string: "https://cat-fact.herokuapp.com/" + "facts/" + "random")! + let url = URL(string: "https://cdn.sixt.io/" + "codingtask/" + "cars")! let session = URLSession(configuration: configuration) let task = session.dataTask(with: url, completionHandler: { (data, response, error) in @@ -40,6 +39,7 @@ class ApiClient: Network { } else if let data = data { // let cat = try? JSONDecoder().decode(Cat.self, from: data) // completionHandler(.success(cat)) + completionHandler(.success(data)) } }) task.resume() diff --git a/Cars Map/Scenes/Waiting/WaitingVC.swift b/Cars Map/Scenes/Waiting/WaitingVC.swift index 88bb3f7..9bef9bb 100644 --- a/Cars Map/Scenes/Waiting/WaitingVC.swift +++ b/Cars Map/Scenes/Waiting/WaitingVC.swift @@ -20,7 +20,9 @@ class WaitingVC: UIViewController { // MARK: UIViewController override func viewDidLoad() { + super.viewDidLoad() print("WaitingVC loaded!") + viewModel.start() } // MARK: Setup diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index feb5109..acb8c45 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -16,15 +16,31 @@ class WaitingViewModel: WaitingViewModelType { var appCoordinatorDelegate: AppCoordinatorDelegate? var viewDelegate: WaitingViewModelViewDelegate? + //MARK: Waiting VM init(apiClient: Network) { self.apiClient = apiClient print("WaitingVM is running!") } + + func start() { + fetch() + } } extension WaitingViewModel { func fetch() { // 1. request + apiClient.fetch { (result) in + switch result { + case .success(let cars): + print(cars) + // add to array + // refresh view using delegate + case .failure(let error): + let errorMessage = error.localizedDescription + // refresh view using delegate + } + } // 2. handle errors and show on label -> viewDelegate. // 3. give back data -> appCoordinatorDelegate } From 04d751b108886fbef45908cd955af9961b20f09e Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Mon, 11 Apr 2022 23:23:36 +0200 Subject: [PATCH 002/101] IBoutlets added to the WaitingVC as well as retry button. --- Cars Map/Scenes/Waiting/Waiting.storyboard | 9 +++++++++ Cars Map/Scenes/Waiting/WaitingVC.swift | 11 ++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Cars Map/Scenes/Waiting/Waiting.storyboard b/Cars Map/Scenes/Waiting/Waiting.storyboard index 8b2ceb1..9f1240f 100644 --- a/Cars Map/Scenes/Waiting/Waiting.storyboard +++ b/Cars Map/Scenes/Waiting/Waiting.storyboard @@ -32,6 +32,10 @@ + @@ -45,6 +49,11 @@ + + + + + diff --git a/Cars Map/Scenes/Waiting/WaitingVC.swift b/Cars Map/Scenes/Waiting/WaitingVC.swift index 9bef9bb..857cf6f 100644 --- a/Cars Map/Scenes/Waiting/WaitingVC.swift +++ b/Cars Map/Scenes/Waiting/WaitingVC.swift @@ -10,6 +10,9 @@ import UIKit class WaitingVC: UIViewController { // MARK: Outlets + @IBOutlet weak var acitivityIndicator: UIActivityIndicatorView! + @IBOutlet weak var infoLabel: UILabel! + @IBOutlet weak var retryBtn: UIButton! // MARK: Properties var viewModel: WaitingViewModel! { @@ -21,19 +24,21 @@ class WaitingVC: UIViewController { // MARK: UIViewController override func viewDidLoad() { super.viewDidLoad() - print("WaitingVC loaded!") viewModel.start() } // MARK: Setup // MARK: Actions + private func retry() { + viewModel.retry() + } } // MARK: - ViewModel Delegate extension WaitingVC: WaitingViewModelViewDelegate { - func updateLabelWith(text: String) { - print("Update error label to" + text) + func showError(text: String) { + } func animate(_ flag: Bool) { From 63d5b3cf7d782cd985913250e5940ad97f75b605 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Mon, 11 Apr 2022 23:36:48 +0200 Subject: [PATCH 003/101] The app now can receives cars Json files from API and convert it to native Car struct. --- Cars Map/Scenes/ApiClient.swift | 5 ++--- Cars Map/Scenes/Car.swift | 10 +++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Cars Map/Scenes/ApiClient.swift b/Cars Map/Scenes/ApiClient.swift index 888485e..904a29a 100644 --- a/Cars Map/Scenes/ApiClient.swift +++ b/Cars Map/Scenes/ApiClient.swift @@ -37,9 +37,8 @@ class ApiClient: Network { } else if serverError { completionHandler(.failure(CarsAPIError.serverError)) } else if let data = data { -// let cat = try? JSONDecoder().decode(Cat.self, from: data) -// completionHandler(.success(cat)) - completionHandler(.success(data)) + let cat = try? JSONDecoder().decode([Car].self, from: data) + completionHandler(.success(cat)) } }) task.resume() diff --git a/Cars Map/Scenes/Car.swift b/Cars Map/Scenes/Car.swift index 85bfa09..9c79d6a 100644 --- a/Cars Map/Scenes/Car.swift +++ b/Cars Map/Scenes/Car.swift @@ -6,6 +6,14 @@ // -struct Car { +struct Car: Decodable { + let id: String + let modelName: String let name: String + let make: String + let color: String + let fuelLevel: Float + let latitude: Float + let longitude: Float + let carImageUrl: String } From 97768ac147b40d3ab36d584be33bb0aed53887d3 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Mon, 11 Apr 2022 23:41:20 +0200 Subject: [PATCH 004/101] API errors handled. --- Cars Map/Scenes/Waiting/Waiting.storyboard | 10 +++---- Cars Map/Scenes/Waiting/WaitingVC.swift | 13 +++++---- .../Scenes/Waiting/WaitingViewModel.swift | 28 +++++++++++++------ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Cars Map/Scenes/Waiting/Waiting.storyboard b/Cars Map/Scenes/Waiting/Waiting.storyboard index 9f1240f..0b83b60 100644 --- a/Cars Map/Scenes/Waiting/Waiting.storyboard +++ b/Cars Map/Scenes/Waiting/Waiting.storyboard @@ -18,13 +18,13 @@ - + - + - + diff --git a/Cars Map/Scenes/Waiting/WaitingVC.swift b/Cars Map/Scenes/Waiting/WaitingVC.swift index 857cf6f..3ad953b 100644 --- a/Cars Map/Scenes/Waiting/WaitingVC.swift +++ b/Cars Map/Scenes/Waiting/WaitingVC.swift @@ -32,18 +32,21 @@ class WaitingVC: UIViewController { // MARK: Actions private func retry() { viewModel.retry() + self.hideError() } } // MARK: - ViewModel Delegate extension WaitingVC: WaitingViewModelViewDelegate { func showError(text: String) { - + self.infoLabel.text = text + " ☹️" + self.acitivityIndicator.isHidden = true + self.retryBtn.isHidden = false } - func animate(_ flag: Bool) { - print("you should \(flag)") + func hideError() { + self.infoLabel.text = "I'm calling the server, please give me a sec!\n\nπŸ“žπŸŒβ˜ΊοΈ" + self.acitivityIndicator.isHidden = false + self.retryBtn.isHidden = true } - - } diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index acb8c45..6ef37cf 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -16,6 +16,13 @@ class WaitingViewModel: WaitingViewModelType { var appCoordinatorDelegate: AppCoordinatorDelegate? var viewDelegate: WaitingViewModelViewDelegate? + var cars : [Car]? { + didSet { + // I'm filled + // call the tab bars + } + } + //MARK: Waiting VM init(apiClient: Network) { self.apiClient = apiClient @@ -33,16 +40,21 @@ extension WaitingViewModel { apiClient.fetch { (result) in switch result { case .success(let cars): - print(cars) - // add to array - // refresh view using delegate + if let cars = cars as? [Car] { + self.cars = cars + }else { + DispatchQueue.main.async { + self.viewDelegate?.showError(text: "Bad error") + } + } + + // add to array + // refresh view using delegate case .failure(let error): let errorMessage = error.localizedDescription - // refresh view using delegate + self.viewDelegate?.showError(text: errorMessage) } } - // 2. handle errors and show on label -> viewDelegate. - // 3. give back data -> appCoordinatorDelegate } func retry() { @@ -65,6 +77,6 @@ protocol AppCoordinatorDelegate { // MARK: - ViewModelViewDelegate protocol WaitingViewModelViewDelegate { - func updateLabelWith(text: String) - func animate(_:Bool) + func showError(text: String) + func hideError() } From 51ad9bb9fb57a4806239df01e7f52bc25b84c85f Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 00:01:26 +0200 Subject: [PATCH 005/101] api call code cleaned. --- Cars Map/Scenes/Waiting/WaitingViewModel.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index 6ef37cf..f242c96 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -37,14 +37,17 @@ class WaitingViewModel: WaitingViewModelType { extension WaitingViewModel { func fetch() { // 1. request - apiClient.fetch { (result) in + apiClient.fetch { + [weak self] + (result) in + guard let sSelf = self else { return } switch result { case .success(let cars): if let cars = cars as? [Car] { - self.cars = cars + sSelf.cars = cars }else { DispatchQueue.main.async { - self.viewDelegate?.showError(text: "Bad error") + sSelf.viewDelegate?.showError(text: "Bad error") } } @@ -52,7 +55,7 @@ extension WaitingViewModel { // refresh view using delegate case .failure(let error): let errorMessage = error.localizedDescription - self.viewDelegate?.showError(text: errorMessage) + sSelf.viewDelegate?.showError(text: errorMessage) } } } From 2816927aeb55e0612d1dd9103541fcc1514fc046 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 00:05:15 +0200 Subject: [PATCH 006/101] error handling improved. --- Cars Map/Scenes/Waiting/WaitingViewModel.swift | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index f242c96..9ac6ca6 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -22,11 +22,10 @@ class WaitingViewModel: WaitingViewModelType { // call the tab bars } } - + //MARK: Waiting VM init(apiClient: Network) { self.apiClient = apiClient - print("WaitingVM is running!") } func start() { @@ -47,15 +46,15 @@ extension WaitingViewModel { sSelf.cars = cars }else { DispatchQueue.main.async { - sSelf.viewDelegate?.showError(text: "Bad error") + sSelf.viewDelegate?.showError(text: CarsAPIError.noData.localizedDescription) } } - - // add to array // refresh view using delegate case .failure(let error): let errorMessage = error.localizedDescription - sSelf.viewDelegate?.showError(text: errorMessage) + DispatchQueue.main.async { + sSelf.viewDelegate?.showError(text: errorMessage) + } } } } From 1ee7338d6f7c8abc737c37924bc9faf780322fe1 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 00:06:36 +0200 Subject: [PATCH 007/101] retry API call functionality implemented on WaitingVC. --- Cars Map/Scenes/Waiting/Waiting.storyboard | 3 +++ Cars Map/Scenes/Waiting/WaitingVC.swift | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Cars Map/Scenes/Waiting/Waiting.storyboard b/Cars Map/Scenes/Waiting/Waiting.storyboard index 0b83b60..f1d5645 100644 --- a/Cars Map/Scenes/Waiting/Waiting.storyboard +++ b/Cars Map/Scenes/Waiting/Waiting.storyboard @@ -35,6 +35,9 @@ diff --git a/Cars Map/Scenes/Waiting/WaitingVC.swift b/Cars Map/Scenes/Waiting/WaitingVC.swift index 3ad953b..97f1549 100644 --- a/Cars Map/Scenes/Waiting/WaitingVC.swift +++ b/Cars Map/Scenes/Waiting/WaitingVC.swift @@ -30,7 +30,7 @@ class WaitingVC: UIViewController { // MARK: Setup // MARK: Actions - private func retry() { + @IBAction func retry(_ sender: Any) { viewModel.retry() self.hideError() } From 38ac51ac89457356820181c016989b52f9c91edb Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 00:10:20 +0200 Subject: [PATCH 008/101] Wrote better comments for WaitingVM. --- Cars Map/Scenes/Waiting/WaitingViewModel.swift | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index 9ac6ca6..558e430 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -33,9 +33,9 @@ class WaitingViewModel: WaitingViewModelType { } } +// MARK: Network extension WaitingViewModel { func fetch() { - // 1. request apiClient.fetch { [weak self] (result) in @@ -49,7 +49,6 @@ extension WaitingViewModel { sSelf.viewDelegate?.showError(text: CarsAPIError.noData.localizedDescription) } } - // refresh view using delegate case .failure(let error): let errorMessage = error.localizedDescription DispatchQueue.main.async { @@ -60,9 +59,7 @@ extension WaitingViewModel { } func retry() { - // it should resend the request fetch() - // update views } } From 4628a4522ccb02772cb512ecbeab3fac7169b778 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 00:16:35 +0200 Subject: [PATCH 009/101] Design pattern implemented to send received cars in WaitingVM to AppCoordinator. --- Cars Map/Scenes/AppCoordinator.swift | 9 +++++++++ Cars Map/Scenes/Waiting/WaitingViewModel.swift | 5 +++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/Cars Map/Scenes/AppCoordinator.swift b/Cars Map/Scenes/AppCoordinator.swift index 60c9b18..db20d97 100644 --- a/Cars Map/Scenes/AppCoordinator.swift +++ b/Cars Map/Scenes/AppCoordinator.swift @@ -59,8 +59,17 @@ extension AppCoordinator { let waitingStoryBoard = UIStoryboard.init(name: "Waiting", bundle: nil) let waitingVC = waitingStoryBoard.instantiateViewController(withIdentifier: "WaitingVC") as! WaitingVC let waitingVM = WaitingViewModel(apiClient: apiClient) + waitingVM.appCoordinatorDelegate = self waitingVC.viewModel = waitingVM window?.rootViewController = waitingVC } } +// MARK: AppCoordinator Delegate +extension AppCoordinator: AppCoordinatorDelegate{ + func dataReceived(cars: [Car]) { + print("I'm in AppCoordinator and received:\n:", cars) + // close WaitingVC + // start tabbars + } +} diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index 558e430..19a5916 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -18,8 +18,9 @@ class WaitingViewModel: WaitingViewModelType { var cars : [Car]? { didSet { - // I'm filled - // call the tab bars + if let cars = cars { + appCoordinatorDelegate?.dataReceived(cars: cars) + } } } From 5489ebb09c208bba9fa639033b74b8f8c564e05b Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 09:06:59 +0200 Subject: [PATCH 010/101] coordinate from WaitingVC to TabBarController. --- Cars Map.xcodeproj/project.pbxproj | 2 +- Cars Map/Scenes/AppCoordinator.swift | 14 ++++++++------ Cars Map/Scenes/Waiting/WaitingViewModel.swift | 6 +++++- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 964a7ab..c25980d 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -100,9 +100,9 @@ 87B6E71D28049CD20059A1E3 /* Scenes */ = { isa = PBXGroup; children = ( - 87B6E7562804A5480059A1E3 /* ApiClient.swift */, 87B6E73A28049EF10059A1E3 /* Coordinator.swift */, 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */, + 87B6E7562804A5480059A1E3 /* ApiClient.swift */, 879EFF352804B95900ED0CD0 /* Car.swift */, 879EFF312804B7FE00ED0CD0 /* Waiting */, 87B6E72428049D760059A1E3 /* MapCars */, diff --git a/Cars Map/Scenes/AppCoordinator.swift b/Cars Map/Scenes/AppCoordinator.swift index db20d97..1c041b9 100644 --- a/Cars Map/Scenes/AppCoordinator.swift +++ b/Cars Map/Scenes/AppCoordinator.swift @@ -11,11 +11,11 @@ import UIKit final class AppCoordinator: Coordinator { // TabCoordinator // MARK: Properties - let window: UIWindow? + private let window: UIWindow? - var rootTabBarController = UITabBarController() + private var rootTabBarController = UITabBarController() - var apiClient: Network = { + private var apiClient: Network = { let configuration = URLSessionConfiguration.default configuration.httpAdditionalHeaders = ["Content-Type": "application/json; charset=utf-8"] let apiClient = ApiClient(configuration: configuration) @@ -23,8 +23,8 @@ final class AppCoordinator: Coordinator { // TabCoordinator }() // MARK: Child Coordinators - weak var mapCarsCoordinator: Coordinator? - weak var listCarsCoordinator: Coordinator? + private weak var mapCarsCoordinator: Coordinator? + private weak var listCarsCoordinator: Coordinator? init(window: UIWindow?) { self.window = window @@ -68,8 +68,10 @@ extension AppCoordinator { // MARK: AppCoordinator Delegate extension AppCoordinator: AppCoordinatorDelegate{ func dataReceived(cars: [Car]) { - print("I'm in AppCoordinator and received:\n:", cars) + print("\nI'm in AppCoordinator and received \(cars.count) cars.") // close WaitingVC + self.window?.rootViewController = rootTabBarController // start tabbars + startTabBarControllers(with: cars) } } diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index 19a5916..cf0b01f 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -19,7 +19,11 @@ class WaitingViewModel: WaitingViewModelType { var cars : [Car]? { didSet { if let cars = cars { - appCoordinatorDelegate?.dataReceived(cars: cars) + DispatchQueue.main.async { + // because it's called from background thread + // and coordinatorDelegate will have view related functinos + self.appCoordinatorDelegate?.dataReceived(cars: cars) + } } } } From e7395bedd9d23e2ad1edec45831dbe613613fe77 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 09:49:21 +0200 Subject: [PATCH 011/101] added MapKit to the View. --- Cars Map.xcodeproj/project.pbxproj | 8 ++++---- Cars Map/Scenes/MapCars/View/MapCars.storyboard | 13 ++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index c25980d..67eb4d5 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -18,7 +18,7 @@ 87B6E73228049E110059A1E3 /* MapCarsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73128049E110059A1E3 /* MapCarsVC.swift */; }; 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73A28049EF10059A1E3 /* Coordinator.swift */; }; 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */; }; - 87B6E7412804A1100059A1E3 /* CatsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73F2804A1100059A1E3 /* CatsViewModel.swift */; }; + 87B6E7412804A1100059A1E3 /* MapCarsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */; }; 87B6E7422804A1100059A1E3 /* ViewModelAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */; }; 87B6E7492804A1310059A1E3 /* ListCars.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E7482804A1310059A1E3 /* ListCars.storyboard */; }; 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */; }; @@ -41,7 +41,7 @@ 87B6E73128049E110059A1E3 /* MapCarsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsVC.swift; sourceTree = ""; }; 87B6E73A28049EF10059A1E3 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; - 87B6E73F2804A1100059A1E3 /* CatsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CatsViewModel.swift; sourceTree = ""; }; + 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsViewModel.swift; sourceTree = ""; }; 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelAbstractions.swift; sourceTree = ""; }; 87B6E7482804A1310059A1E3 /* ListCars.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ListCars.storyboard; sourceTree = ""; }; 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCarsVC.swift; sourceTree = ""; }; @@ -169,7 +169,7 @@ 87B6E72A28049DD00059A1E3 /* ViewModel */ = { isa = PBXGroup; children = ( - 87B6E73F2804A1100059A1E3 /* CatsViewModel.swift */, + 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */, 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */, ); path = ViewModel; @@ -271,7 +271,7 @@ 87B6E73228049E110059A1E3 /* MapCarsVC.swift in Sources */, 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */, 87B6E70728049BE60059A1E3 /* AppDelegate.swift in Sources */, - 87B6E7412804A1100059A1E3 /* CatsViewModel.swift in Sources */, + 87B6E7412804A1100059A1E3 /* MapCarsViewModel.swift in Sources */, 87B6E7502804A4700059A1E3 /* MapCarsCoordinator.swift in Sources */, 87B6E7532804A4C50059A1E3 /* ListCarsCoordinator.swift in Sources */, 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */, diff --git a/Cars Map/Scenes/MapCars/View/MapCars.storyboard b/Cars Map/Scenes/MapCars/View/MapCars.storyboard index 380a72d..38558a8 100644 --- a/Cars Map/Scenes/MapCars/View/MapCars.storyboard +++ b/Cars Map/Scenes/MapCars/View/MapCars.storyboard @@ -16,13 +16,24 @@ + + + + + + + + + + + - + From 5b6cb680af798bc6b31f69f131de068fd31a030d Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 09:50:19 +0200 Subject: [PATCH 012/101] MapCarVC implemented. --- .../MapCars/ViewController/MapCarsVC.swift | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift index d3b8928..8be1113 100644 --- a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift +++ b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift @@ -7,4 +7,41 @@ import UIKit -class MapCarsVC: UIViewController {} +class MapCarsVC: UIViewController { + + // MARK: Outlets + + + // MARK: Properties + var viewModel: MapCarsViewModel! { + didSet { + viewModel.viewDelegate = self + } + } + + // MARK: UIViewController + override func viewDidLoad() { + super.viewDidLoad() + viewModel.start() + } + + // MARK: Setup + + // MARK: Actions + @IBAction func retry(_ sender: Any) { + + } +} + +// MARK: - ViewModel Delegate +extension MapCarsVC: MapCarsViewModelViewDelegate { + func refreshScreen(with cars: [Car]) { + print("refreshScreen functino in MapCarsVC received \(cars.count) cars") + } + + func selected(car: Car) { + print("selected function in MapCarsVC received \(car) cars") + } + + +} From cee8868011cd954dd5c8521f2c7ae27d0c180cf6 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 09:51:10 +0200 Subject: [PATCH 013/101] MapCars Coordinator delegate handled and the class cleaned a bit. --- .../Scenes/MapCars/MapCarsCoordinator.swift | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift index 3ace089..bcae47d 100644 --- a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift +++ b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift @@ -15,24 +15,34 @@ class MapCarsCoordinator: Coordinator { private let mapCarsStoryboard = UIStoryboard(name: "MapCars", bundle: nil) -// private let apiClient: Network - // MARK: VM + private var mapCarsVM: MapCarsViewModel { + let mapCarsVM = MapCarsViewModel() + mapCarsVM.mapCarsCoordinatorDelegate = self + return mapCarsVM + } // MARK: Coordinator - // inject VM and API init(rootTabBarController: UITabBarController) { // set MapCarsVC to root VC - + super.init() + self.rootTabBarController = rootTabBarController + self.start() + } + + override func start() { let mapCarsVC = mapCarsStoryboard.instantiateViewController(withIdentifier: "MapCarsVC") as! MapCarsVC + mapCarsVC.viewModel = mapCarsVM mapCarsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .downloads, tag: 0) mapCarsNavigationContrller.setViewControllers([mapCarsVC], animated: true) rootTabBarController.setViewControllers([mapCarsNavigationContrller], animated: true) } - - override func start() { - print("I'm in MapCarsCoordinator") +} + +extension MapCarsCoordinator: MapCarsViewModelCoordinatorDelegate { + func didSelect(car: Car, from controller: UIViewController) { + print("I'm in MapCarsCoordinator and received a car using MapCarsVMDelegate") } } From 555d56f23e5059df800fe211c8acfa931c6f1fe2 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 09:52:08 +0200 Subject: [PATCH 014/101] MapCarsVM implemented. --- .../MapCars/ViewModel/CatsViewModel.swift | 161 ------------------ .../MapCars/ViewModel/MapCarsViewModel.swift | 69 ++++++++ .../ViewModel/ViewModelAbstractions.swift | 64 ++++--- 3 files changed, 100 insertions(+), 194 deletions(-) delete mode 100644 Cars Map/Scenes/MapCars/ViewModel/CatsViewModel.swift create mode 100644 Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift diff --git a/Cars Map/Scenes/MapCars/ViewModel/CatsViewModel.swift b/Cars Map/Scenes/MapCars/ViewModel/CatsViewModel.swift deleted file mode 100644 index f3f9ef8..0000000 --- a/Cars Map/Scenes/MapCars/ViewModel/CatsViewModel.swift +++ /dev/null @@ -1,161 +0,0 @@ -//// -//// CatsViewModel.swift -//// Cat Facts -//// -//// Created by iMamad on 4/8/22. -//// -// -//import UIKit -//import CoreData -// -//class CatsViewModel { -// -// // MARK: Delegates -// var coordinatorDelegate: CatsCoordinator? -// var viewDelegate: CatsViewModelViewDelegate? -// -// // MARK: Properties -// private let service: CatsServices // API Call & CoreData -// -// var cats: [Cat] = [] -// var catsNSManagedObjects: [NSManagedObject]? -// -// // MARK: Init -// init(service: CatsServices) { self.service = service } -// -// func start() { -// service.fetchSavedCats { -// [weak self] -// (catsNSObjectArray, error) in -// guard let sSelf = self, -// let catsNSObjectArray = catsNSObjectArray as? [NSManagedObject] else { -// return -// } -// sSelf.catsNSManagedObjects = catsNSObjectArray -// let cats = CatCoreDataConvertor().giveMeCats(from: catsNSObjectArray) -// sSelf.cats = cats -// } -// } -// -//} -// -//// MARK: - Network -//extension CatsViewModel { -// private func getNewCat() { -// DispatchQueue.main.async { -// self.viewDelegate?.hud(show: true) -// } -// service.fetchCat { -// [weak self] -// (cat, error) in -// guard let sSelf = self else { return } -// -// if let error = error { -// DispatchQueue.main.async { -// sSelf.viewDelegate?.showError(errorMessage: error.localizedDescription) -// } -// return -// } -// -// if let cat = cat as? Cat { -// DispatchQueue.main.async { -// sSelf.saveNewCat(cat: cat) -// sSelf.viewDelegate?.updateScreen() -// sSelf.viewDelegate?.hud(show: false) -// } -// return -// } -// sSelf.viewDelegate?.showError(errorMessage: "Some Errors when communicating with the server occured!") -// -// } -// } -//} -// -//// MARK: - Core Data -//extension CatsViewModel { -// -// func saveNewCat(cat: Cat) { -// service.save(cat: cat) { -// [weak self] -// (error) in -// guard let sSelf = self else { return } -// -// if let error = error { -// DispatchQueue.main.async { -// sSelf.viewDelegate?.showError(errorMessage: error.localizedDescription) -// } -// return -// } -// -// sSelf.refreshView() -// } -// } -// -// func remove(at index: Int?) { -// -// guard let row = index, -// let catNSManagedObj = catsNSManagedObjects?[row] else { return } -// -// service.delete(cat: catNSManagedObj) { -// [weak self] -// (deleted, error) in -// guard let sSelf = self else { return } -// if let error = error { -// DispatchQueue.main.async { -// sSelf.viewDelegate?.showError(errorMessage: error.localizedDescription) -// } -// return -// } -// -// if deleted { -// sSelf.refreshView() -// } -// } -// -// } -//} -// -//// MARK: - ViewModelType -//extension CatsViewModel: CatsViewModelType { -// -// func numberOfItems() -> Int { -// return cats.count -// } -// -// func itemFor(row: Int) -> UITableViewCell { -// let cell = UITableViewCell(style: .value1, reuseIdentifier: "catID") -// let catViewData = CatViewData(cat: cats[row]) -// cell.textLabel?.text = catViewData._id -// cell.detailTextLabel?.text = catViewData.createdAt -// return cell -// } -// -// func add() { -// getNewCat() -// viewDelegate?.updateScreen() -// } -// -// func delete() { -// let row = self.viewDelegate?.selectedCatRow() -// self.remove(at: row) -// } -// -// func didSelectRow(_ row: Int, from controller: UIViewController) { -// print("Cat in \(row) selected") -// didSelect(cat: cats[row], from: controller) -// } -// -// func refreshView() { -// start() // to refresh arrays -// viewDelegate?.updateScreen() -// } -//} -// -//// MARK: - ViewModelCoordinator -//extension CatsViewModel: CatsViewModelCoordinatorDelegate { -// func didSelect(cat: Cat, from controller: UIViewController) { -// coordinatorDelegate?.didSelect(cat: cat, -// from: controller) -// // It'll open CatDetail VC -// } -//} diff --git a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift new file mode 100644 index 0000000..39c3524 --- /dev/null +++ b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift @@ -0,0 +1,69 @@ +// +// CatsViewModel.swift +// Cat Facts +// +// Created by iMamad on 4/8/22. +// + +import UIKit + +class MapCarsViewModel { + + // MARK: Delegates + var mapCarsCoordinatorDelegate: MapCarsViewModelCoordinatorDelegate? + var viewDelegate: MapCarsViewModelViewDelegate? + + // MARK: Properties + var cars: [Car] = [] + + // MARK: Init + init() {} + + func start() {} + +} + +//// MARK: - ViewModelType +//extension CatsViewModel: CatsViewModelType { +// +// func numberOfItems() -> Int { +// return cats.count +// } +// +// func itemFor(row: Int) -> UITableViewCell { +// let cell = UITableViewCell(style: .value1, reuseIdentifier: "catID") +// let catViewData = CatViewData(cat: cats[row]) +// cell.textLabel?.text = catViewData._id +// cell.detailTextLabel?.text = catViewData.createdAt +// return cell +// } +// +// func add() { +// getNewCat() +// viewDelegate?.updateScreen() +// } +// +// func delete() { +// let row = self.viewDelegate?.selectedCatRow() +// self.remove(at: row) +// } +// +// func didSelectRow(_ row: Int, from controller: UIViewController) { +// print("Cat in \(row) selected") +// didSelect(cat: cats[row], from: controller) +// } +// +// func refreshView() { +// start() // to refresh arrays +// viewDelegate?.updateScreen() +// } +//} + +// MARK: - ViewModelCoordinator +extension MapCarsViewModel: MapCarsViewModelCoordinatorDelegate { + func didSelect(car: Car, from controller: UIViewController) { + mapCarsCoordinatorDelegate?.didSelect(car: car, + from: controller) + // It'll open CarsDetail VC + } +} diff --git a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift index 8df54bd..170d470 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift @@ -1,41 +1,39 @@ -//// -//// ViewModelAbstractions.swift -//// Cat Facts -//// -//// Created by iMamad on 4/9/22. -//// -// -//import UIKit -// -//// MARK: - ViewModelType -//protocol CatsViewModelType { -// -// var viewDelegate: CatsViewModelViewDelegate? { get set } -// +// +// ViewModelAbstractions.swift +// Cat Facts +// +// Created by iMamad on 4/9/22. +// + +import UIKit + +// MARK: - ViewModelType +protocol CatsViewModelType { + + var viewDelegate: MapCarsViewModelViewDelegate? { get set } + // // Data Source // func numberOfItems() -> Int -// +// // func itemFor(row: Int) -> UITableViewCell -// +// // // Events // func add() -// +// // func delete() -// +// // func didSelectRow(_ row: Int, from controller: UIViewController) -// +// // func refreshView() -//} -// -//// MARK: - ViewModelCoordinator(delegate) -//protocol CatsViewModelCoordinatorDelegate: class { -// func didSelect(cat: Cat, from controller: UIViewController) -//} -// -//// MARK: - ViewModelViewDelegate -//protocol CatsViewModelViewDelegate: class { -// func updateScreen() -// func hud(show: Bool) -// func showError(errorMessage: String) -// func selectedCatRow() -> Int -//} +} + +// MARK: - ViewModelCoordinator(delegate) +protocol MapCarsViewModelCoordinatorDelegate: class { + func didSelect(car: Car, from controller: UIViewController) +} + +// MARK: - ViewModelViewDelegate +protocol MapCarsViewModelViewDelegate: class { + func refreshScreen(with cars: [Car]) + func selected(car: Car) +} From b65f90335a57682d15b7c979512a3e71f5420013 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 11:05:30 +0200 Subject: [PATCH 015/101] added viewType for car model to encode MKPointAnnotation within them. --- Cars Map/Scenes/Car.swift | 70 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/Cars Map/Scenes/Car.swift b/Cars Map/Scenes/Car.swift index 9c79d6a..3f3bd4a 100644 --- a/Cars Map/Scenes/Car.swift +++ b/Cars Map/Scenes/Car.swift @@ -5,6 +5,20 @@ // Created by iMamad on 4/11/22. // +import MapKit +import CoreLocation + +protocol CarViewDataType { + var id: String { get } + var modelName: String { get } + var name: String { get } + var make: String { get } + var color: String { get } + var fuelLevel: Float { get } + var latitude: Float { get } + var longitude: Float { get } + var carImageUrl: String { get } +} struct Car: Decodable { let id: String @@ -17,3 +31,59 @@ struct Car: Decodable { let longitude: Float let carImageUrl: String } + + + +struct CarViewData: CarViewDataType { + // MARK: custom properties + var coordinate: MKPointAnnotation { + let annotation = MKPointAnnotation() + annotation.title = car.name + let coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(latitude), + longitude: CLLocationDegrees(longitude)) + annotation.coordinate = coordinate + return annotation + } + + // MARK: properties + var id: String { + return car.id + } + + var modelName: String{ + return car.modelName + } + + var name: String{ + return car.name + } + + var make: String{ + return car.make + } + + var color: String{ + return car.color + } + + var fuelLevel: Float{ + return car.fuelLevel + } + + var latitude: Float{ + return car.latitude + } + + var longitude: Float{ + return car.longitude + } + + var carImageUrl: String { + return car.carImageUrl + } + + // MARK: Init + private let car: Car + + init(car: Car) { self.car = car } +} From 3d919ea7441fbde092c9cc112eb2a41130b48d3f Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 11:06:16 +0200 Subject: [PATCH 016/101] inject received cars from AppCoordinator to CarsMapViewVM --- Cars Map/Scenes/AppCoordinator.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Cars Map/Scenes/AppCoordinator.swift b/Cars Map/Scenes/AppCoordinator.swift index 1c041b9..4e89099 100644 --- a/Cars Map/Scenes/AppCoordinator.swift +++ b/Cars Map/Scenes/AppCoordinator.swift @@ -44,7 +44,8 @@ final class AppCoordinator: Coordinator { // TabCoordinator extension AppCoordinator { private func startTabBarControllers(with cars: [Car]) { // first tab - let mapCarsCoordinator = MapCarsCoordinator(rootTabBarController: rootTabBarController) + let mapCarsCoordinator = MapCarsCoordinator(rootTabBarController: rootTabBarController, + cars: cars) self.addChildCoordinator(mapCarsCoordinator) mapCarsCoordinator.start() From 5a31fea2452e4e679bd79eab23e1e6c3bdeba229 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 11:07:41 +0200 Subject: [PATCH 017/101] receive retrieved cars from AppCoordinator in MapCarsCoordinator. --- Cars Map/Scenes/MapCars/MapCarsCoordinator.swift | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift index bcae47d..ed97681 100644 --- a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift +++ b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift @@ -16,16 +16,19 @@ class MapCarsCoordinator: Coordinator { private let mapCarsStoryboard = UIStoryboard(name: "MapCars", bundle: nil) // MARK: VM + private var cars: [Car] private var mapCarsVM: MapCarsViewModel { let mapCarsVM = MapCarsViewModel() mapCarsVM.mapCarsCoordinatorDelegate = self + mapCarsVM.cars = self.cars return mapCarsVM } // MARK: Coordinator - init(rootTabBarController: UITabBarController) { + init(rootTabBarController: UITabBarController, cars: [Car]) { // set MapCarsVC to root VC + self.cars = cars super.init() self.rootTabBarController = rootTabBarController self.start() From 922f32d2e1d3682f05c6cabf09ec9b80384c80c7 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 11:28:47 +0200 Subject: [PATCH 018/101] show car annotations on map like a charm! :D --- Cars Map.xcodeproj/project.pbxproj | 2 +- Cars Map/Scenes/MapCars/View/MapCars.storyboard | 17 +++++++---------- .../MapCars/ViewController/MapCarsVC.swift | 16 +++++----------- .../MapCars/ViewModel/MapCarsViewModel.swift | 15 ++++++++++++++- .../ViewModel/ViewModelAbstractions.swift | 3 ++- 5 files changed, 29 insertions(+), 24 deletions(-) diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 67eb4d5..3f6acaa 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -169,8 +169,8 @@ 87B6E72A28049DD00059A1E3 /* ViewModel */ = { isa = PBXGroup; children = ( - 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */, 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */, + 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */, ); path = ViewModel; sourceTree = ""; diff --git a/Cars Map/Scenes/MapCars/View/MapCars.storyboard b/Cars Map/Scenes/MapCars/View/MapCars.storyboard index 38558a8..479d05a 100644 --- a/Cars Map/Scenes/MapCars/View/MapCars.storyboard +++ b/Cars Map/Scenes/MapCars/View/MapCars.storyboard @@ -5,7 +5,6 @@ - @@ -18,27 +17,25 @@ - + - + - - + + + + + - - - - - diff --git a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift index 8be1113..b34c859 100644 --- a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift +++ b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift @@ -6,10 +6,12 @@ // import UIKit +import MapKit class MapCarsVC: UIViewController { // MARK: Outlets + @IBOutlet weak var mapView: MKMapView! // MARK: Properties @@ -24,24 +26,16 @@ class MapCarsVC: UIViewController { super.viewDidLoad() viewModel.start() } - - // MARK: Setup - - // MARK: Actions - @IBAction func retry(_ sender: Any) { - - } } // MARK: - ViewModel Delegate extension MapCarsVC: MapCarsViewModelViewDelegate { - func refreshScreen(with cars: [Car]) { - print("refreshScreen functino in MapCarsVC received \(cars.count) cars") + func refreshScreen(with annotations: [MKPointAnnotation]) { + print("refreshScreen functino in MapCarsVC received \(annotations.count) cars") + self.mapView.showAnnotations(annotations, animated: true) } func selected(car: Car) { print("selected function in MapCarsVC received \(car) cars") } - - } diff --git a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift index 39c3524..658348a 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift @@ -6,6 +6,7 @@ // import UIKit +import MapKit class MapCarsViewModel { @@ -19,10 +20,22 @@ class MapCarsViewModel { // MARK: Init init() {} - func start() {} + func start() { + // convert to viewType + var carAnnotations: [MKPointAnnotation] = [] + for car in cars { + let carAnnotation = CarViewData(car: car).coordinate + carAnnotations.append(carAnnotation) + } + // call VC + viewDelegate?.refreshScreen(with: carAnnotations) + } } +// Implement interface below for MapVM + + //// MARK: - ViewModelType //extension CatsViewModel: CatsViewModelType { // diff --git a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift index 170d470..2f7b340 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift @@ -6,6 +6,7 @@ // import UIKit +import MapKit // MARK: - ViewModelType protocol CatsViewModelType { @@ -34,6 +35,6 @@ protocol MapCarsViewModelCoordinatorDelegate: class { // MARK: - ViewModelViewDelegate protocol MapCarsViewModelViewDelegate: class { - func refreshScreen(with cars: [Car]) + func refreshScreen(with annotaions: [MKPointAnnotation]) func selected(car: Car) } From e7a3529100b8c713aa67397fe8972eed31011f9e Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 12:18:34 +0200 Subject: [PATCH 019/101] fetching each car's image in background implemented. --- Cars Map/Scenes/ApiClient.swift | 27 +++++++++++++++---- Cars Map/Scenes/Car.swift | 4 +++ .../Scenes/Waiting/WaitingViewModel.swift | 22 +++++++++++++-- 3 files changed, 46 insertions(+), 7 deletions(-) diff --git a/Cars Map/Scenes/ApiClient.swift b/Cars Map/Scenes/ApiClient.swift index 904a29a..85b7e84 100644 --- a/Cars Map/Scenes/ApiClient.swift +++ b/Cars Map/Scenes/ApiClient.swift @@ -6,10 +6,11 @@ // import Foundation - +import UIKit protocol Network { func fetch(completionHandler: @escaping (Result) -> ()) + func fetchImage(from url: URL?, completionHandler: @escaping (UIImage?) -> ()) } class ApiClient: Network { @@ -21,7 +22,7 @@ class ApiClient: Network { func fetch(completionHandler: @escaping (Result) -> ()) { -// if !connected() { completionHandler(.failure(CatAPIError.disconnected)) } + // if !connected() { completionHandler(.failure(CatAPIError.disconnected)) } let url = URL(string: "https://cdn.sixt.io/" + "codingtask/" + "cars")! let session = URLSession(configuration: configuration) let task = session.dataTask(with: url, completionHandler: { (data, response, error) in @@ -44,9 +45,25 @@ class ApiClient: Network { task.resume() } -// private func connected() -> Bool { // to the internet -// InternetConnectionManager.shared.isConnectedToNetwork() -// } + func fetchImage(from url: URL?, completionHandler: @escaping (UIImage?) -> ()) { + DispatchQueue.global().async { + if let url = url, + let data = try? Data(contentsOf: url), + let image = UIImage(data: data) { + DispatchQueue.main.async { + completionHandler(image) + } + } else { + DispatchQueue.main.async { + completionHandler(nil) + } + } + } + } + + // private func connected() -> Bool { // to the internet + // InternetConnectionManager.shared.isConnectedToNetwork() + // } } struct CarsAPIError: Error { diff --git a/Cars Map/Scenes/Car.swift b/Cars Map/Scenes/Car.swift index 3f3bd4a..7e89389 100644 --- a/Cars Map/Scenes/Car.swift +++ b/Cars Map/Scenes/Car.swift @@ -44,6 +44,10 @@ struct CarViewData: CarViewDataType { annotation.coordinate = coordinate return annotation } + var uiImage : UIImage? + var carImageNativeUrl: URL? { + return URL(string: car.carImageUrl) + } // MARK: properties var id: String { diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index cf0b01f..3f0e6eb 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -19,14 +19,14 @@ class WaitingViewModel: WaitingViewModelType { var cars : [Car]? { didSet { if let cars = cars { + self.fetchImages() DispatchQueue.main.async { - // because it's called from background thread - // and coordinatorDelegate will have view related functinos self.appCoordinatorDelegate?.dataReceived(cars: cars) } } } } + var carViewDatas: [CarViewData] = [] // write a tests to check count of this array and above one //MARK: Waiting VM init(apiClient: Network) { @@ -63,6 +63,24 @@ extension WaitingViewModel { } } + func fetchImages() { + for car in cars! { + var carViewData = CarViewData(car: car) + apiClient.fetchImage(from: carViewData.carImageNativeUrl) { + [weak self] + (image) in + guard let sSelf = self else { return } + if let image = image { + carViewData.uiImage = image + sSelf.carViewDatas.append(carViewData) + } else { + // TODO + // carViewData.uiImage = load default image + } + } + } + } + func retry() { fetch() } From 138c2b8f88ee27cf844352cfdabd3cb9782c645e Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 13:34:04 +0200 Subject: [PATCH 020/101] updated image downloader to a UIImageView extension. --- Cars Map/Scenes/ApiClient.swift | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/Cars Map/Scenes/ApiClient.swift b/Cars Map/Scenes/ApiClient.swift index 85b7e84..e930923 100644 --- a/Cars Map/Scenes/ApiClient.swift +++ b/Cars Map/Scenes/ApiClient.swift @@ -10,7 +10,6 @@ import UIKit protocol Network { func fetch(completionHandler: @escaping (Result) -> ()) - func fetchImage(from url: URL?, completionHandler: @escaping (UIImage?) -> ()) } class ApiClient: Network { @@ -45,22 +44,6 @@ class ApiClient: Network { task.resume() } - func fetchImage(from url: URL?, completionHandler: @escaping (UIImage?) -> ()) { - DispatchQueue.global().async { - if let url = url, - let data = try? Data(contentsOf: url), - let image = UIImage(data: data) { - DispatchQueue.main.async { - completionHandler(image) - } - } else { - DispatchQueue.main.async { - completionHandler(nil) - } - } - } - } - // private func connected() -> Bool { // to the internet // InternetConnectionManager.shared.isConnectedToNetwork() // } @@ -72,3 +55,26 @@ struct CarsAPIError: Error { static let serverError = NSError(domain: "A HTTPS server error occured.", code: 03, userInfo: nil) static let disconnected = NSError(domain: " You're not connected to the internet. So, you can't add a new cat.\n Whatever you see are offline.", code: 04, userInfo: nil) } + + +// MARK: UIImageView Image Download Extension +extension UIImageView { + func downloaded(from url: URL, contentMode mode: ContentMode = .scaleAspectFit) { + contentMode = mode + URLSession.shared.dataTask(with: url) { data, response, error in + guard + let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200, + let mimeType = response?.mimeType, mimeType.hasPrefix("image"), + let data = data, error == nil, + let image = UIImage(data: data) + else { return } + DispatchQueue.main.async() { [weak self] in + self?.image = image + } + }.resume() + } + func downloaded(from link: String, contentMode mode: ContentMode = .scaleAspectFit) { + guard let url = URL(string: link) else { return } + downloaded(from: url, contentMode: mode) + } +} From 2e3d0befd89f61db67d9f8d0b80d0a5eb5c81a2a Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 13:36:02 +0200 Subject: [PATCH 021/101] removed fetchImages function from waitingView. --- .../Scenes/Waiting/WaitingViewModel.swift | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingViewModel.swift index 3f0e6eb..2b5bc49 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingViewModel.swift @@ -19,7 +19,6 @@ class WaitingViewModel: WaitingViewModelType { var cars : [Car]? { didSet { if let cars = cars { - self.fetchImages() DispatchQueue.main.async { self.appCoordinatorDelegate?.dataReceived(cars: cars) } @@ -63,24 +62,6 @@ extension WaitingViewModel { } } - func fetchImages() { - for car in cars! { - var carViewData = CarViewData(car: car) - apiClient.fetchImage(from: carViewData.carImageNativeUrl) { - [weak self] - (image) in - guard let sSelf = self else { return } - if let image = image { - carViewData.uiImage = image - sSelf.carViewDatas.append(carViewData) - } else { - // TODO - // carViewData.uiImage = load default image - } - } - } - } - func retry() { fetch() } From 219616853441d389abe0d39d8c7ab936c0e63c98 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 15:23:50 +0200 Subject: [PATCH 022/101] Custom annotations to show on MapKitView implemented. --- Cars Map.xcodeproj/project.pbxproj | 16 +++++ Cars Map/Scenes/Car.swift | 8 +-- .../Scenes/MapCars/Model/CarAnnotation.swift | 20 ++++++ .../MapCars/View/CarAnnotationView.swift | 33 +++++++++ .../MapCars/ViewController/MapCarsVC.swift | 11 ++- .../MapCars/ViewModel/MapCarsViewModel.swift | 67 ++++++++----------- .../ViewModel/ViewModelAbstractions.swift | 8 +-- 7 files changed, 114 insertions(+), 49 deletions(-) create mode 100644 Cars Map/Scenes/MapCars/Model/CarAnnotation.swift create mode 100644 Cars Map/Scenes/MapCars/View/CarAnnotationView.swift diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 3f6acaa..27d4fa7 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 879EFF2E2804B6F900ED0CD0 /* WaitingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF2D2804B6F900ED0CD0 /* WaitingVC.swift */; }; 879EFF332804B85500ED0CD0 /* WaitingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF322804B85500ED0CD0 /* WaitingViewModel.swift */; }; 879EFF362804B95900ED0CD0 /* Car.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF352804B95900ED0CD0 /* Car.swift */; }; + 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */; }; + 87A8C5E02805B2FD00BF5775 /* CarAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */; }; 87B6E70728049BE60059A1E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E70628049BE60059A1E3 /* AppDelegate.swift */; }; 87B6E71028049BE70059A1E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E70F28049BE70059A1E3 /* Assets.xcassets */; }; 87B6E71328049BE70059A1E3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E71128049BE70059A1E3 /* LaunchScreen.storyboard */; }; @@ -33,6 +35,8 @@ 879EFF2D2804B6F900ED0CD0 /* WaitingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingVC.swift; sourceTree = ""; }; 879EFF322804B85500ED0CD0 /* WaitingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingViewModel.swift; sourceTree = ""; }; 879EFF352804B95900ED0CD0 /* Car.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Car.swift; sourceTree = ""; }; + 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarAnnotationView.swift; sourceTree = ""; }; + 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarAnnotation.swift; sourceTree = ""; }; 87B6E70328049BE60059A1E3 /* Cars Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cars Map.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 87B6E70628049BE60059A1E3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 87B6E70F28049BE70059A1E3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -71,6 +75,14 @@ path = Waiting; sourceTree = ""; }; + 87A8C5D82805B23300BF5775 /* Model */ = { + isa = PBXGroup; + children = ( + 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */, + ); + path = Model; + sourceTree = ""; + }; 87B6E6FA28049BE60059A1E3 = { isa = PBXGroup; children = ( @@ -125,6 +137,7 @@ isa = PBXGroup; children = ( 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */, + 87A8C5D82805B23300BF5775 /* Model */, 87B6E72A28049DD00059A1E3 /* ViewModel */, 87B6E72B28049DD80059A1E3 /* View */, 87B6E72D28049DE40059A1E3 /* ViewController */, @@ -179,6 +192,7 @@ isa = PBXGroup; children = ( 879EFF262804AAA000ED0CD0 /* MapCars.storyboard */, + 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */, ); path = View; sourceTree = ""; @@ -263,7 +277,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 87A8C5E02805B2FD00BF5775 /* CarAnnotation.swift in Sources */, 879EFF332804B85500ED0CD0 /* WaitingViewModel.swift in Sources */, + 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */, 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */, 87B6E7422804A1100059A1E3 /* ViewModelAbstractions.swift in Sources */, 879EFF2E2804B6F900ED0CD0 /* WaitingVC.swift in Sources */, diff --git a/Cars Map/Scenes/Car.swift b/Cars Map/Scenes/Car.swift index 7e89389..910a62b 100644 --- a/Cars Map/Scenes/Car.swift +++ b/Cars Map/Scenes/Car.swift @@ -36,14 +36,12 @@ struct Car: Decodable { struct CarViewData: CarViewDataType { // MARK: custom properties - var coordinate: MKPointAnnotation { - let annotation = MKPointAnnotation() - annotation.title = car.name + var coordinate: CLLocationCoordinate2D { let coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(latitude), longitude: CLLocationDegrees(longitude)) - annotation.coordinate = coordinate - return annotation + return coordinate } + var uiImage : UIImage? var carImageNativeUrl: URL? { return URL(string: car.carImageUrl) diff --git a/Cars Map/Scenes/MapCars/Model/CarAnnotation.swift b/Cars Map/Scenes/MapCars/Model/CarAnnotation.swift new file mode 100644 index 0000000..b3489b0 --- /dev/null +++ b/Cars Map/Scenes/MapCars/Model/CarAnnotation.swift @@ -0,0 +1,20 @@ +// +// CarAnnotation.swift +// Cars Map +// +// Created by iMamad on 4/12/22. +// + +import MapKit + +class CarAnnotation : NSObject, MKAnnotation { + var coordinate: CLLocationCoordinate2D + var title: String? + var imageUrl: String + + init(coordinate: CLLocationCoordinate2D, title: String, imageUrl: String) { + self.coordinate = coordinate + self.title = title + self.imageUrl = imageUrl + } +} diff --git a/Cars Map/Scenes/MapCars/View/CarAnnotationView.swift b/Cars Map/Scenes/MapCars/View/CarAnnotationView.swift new file mode 100644 index 0000000..54562a2 --- /dev/null +++ b/Cars Map/Scenes/MapCars/View/CarAnnotationView.swift @@ -0,0 +1,33 @@ +// +// CarAnnotation.swift +// Cars Map +// +// Created by iMamad on 4/12/22. +// + +import MapKit + +class CarAnnotationView: MKAnnotationView { + var imageView: UIImageView! + + override init(annotation: MKAnnotation?, reuseIdentifier: String?) { + super.init(annotation: annotation, reuseIdentifier: reuseIdentifier) + + self.frame = CGRect(x: 0, y: 0, width: 70, height: 70) + self.imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 70, height: 70)) + self.addSubview(self.imageView) + + self.imageView.layer.cornerRadius = 5.0 + self.imageView.layer.masksToBounds = true + } + + override var image: UIImage? { + get { return self.imageView.image } + + set { self.imageView.image = newValue } + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} diff --git a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift index b34c859..967d547 100644 --- a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift +++ b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift @@ -24,13 +24,14 @@ class MapCarsVC: UIViewController { // MARK: UIViewController override func viewDidLoad() { super.viewDidLoad() + mapView.delegate = self viewModel.start() } } // MARK: - ViewModel Delegate extension MapCarsVC: MapCarsViewModelViewDelegate { - func refreshScreen(with annotations: [MKPointAnnotation]) { + func refreshScreen(with annotations: [CarAnnotation]) { print("refreshScreen functino in MapCarsVC received \(annotations.count) cars") self.mapView.showAnnotations(annotations, animated: true) } @@ -39,3 +40,11 @@ extension MapCarsVC: MapCarsViewModelViewDelegate { print("selected function in MapCarsVC received \(car) cars") } } + +//MARK: Map Delegate +extension MapCarsVC: MKMapViewDelegate { + func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? { + let annView = viewModel.viewFor(annotation: annotation) + return annView + } +} diff --git a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift index 658348a..b0d0e1e 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift @@ -16,61 +16,50 @@ class MapCarsViewModel { // MARK: Properties var cars: [Car] = [] + // check if it must be weak or not // MARK: Init init() {} func start() { - // convert to viewType - var carAnnotations: [MKPointAnnotation] = [] + // convert cars to annotations + var carAnnotations: [CarAnnotation] = [] for car in cars { - let carAnnotation = CarViewData(car: car).coordinate + let carViewData = CarViewData(car: car) + let carAnnotation = CarAnnotation(coordinate: carViewData.coordinate, + title: carViewData.name, + imageUrl: carViewData.carImageUrl) carAnnotations.append(carAnnotation) } // call VC viewDelegate?.refreshScreen(with: carAnnotations) } - } // Implement interface below for MapVM -//// MARK: - ViewModelType -//extension CatsViewModel: CatsViewModelType { -// -// func numberOfItems() -> Int { -// return cats.count -// } -// -// func itemFor(row: Int) -> UITableViewCell { -// let cell = UITableViewCell(style: .value1, reuseIdentifier: "catID") -// let catViewData = CatViewData(cat: cats[row]) -// cell.textLabel?.text = catViewData._id -// cell.detailTextLabel?.text = catViewData.createdAt -// return cell -// } -// -// func add() { -// getNewCat() -// viewDelegate?.updateScreen() -// } -// -// func delete() { -// let row = self.viewDelegate?.selectedCatRow() -// self.remove(at: row) -// } -// -// func didSelectRow(_ row: Int, from controller: UIViewController) { -// print("Cat in \(row) selected") -// didSelect(cat: cats[row], from: controller) -// } -// -// func refreshView() { -// start() // to refresh arrays -// viewDelegate?.updateScreen() -// } -//} +// MARK: - ViewModelType +extension MapCarsViewModel: MapCarsViewModelType { + + func viewFor(annotation: MKAnnotation) -> MKAnnotationView? { + //Handle user location annotation.. + if annotation.isKind(of: MKUserLocation.self) { + return nil //Default is to let the system handle it. + } + + //Handle non-ImageAnnotations.. + if !(annotation.isKind(of: CarAnnotation.self)) { + return nil //Default is to let the system handle it. + } + + //Handle CarAnnotation.. + let carAnnotation = annotation as! CarAnnotation // force unwrap because we checked it above + let annotationView = CarAnnotationView(annotation: annotation, reuseIdentifier: "imageAnnotation") + annotationView.imageView.downloaded(from: carAnnotation.imageUrl) // Assign related image to it + return annotationView + } +} // MARK: - ViewModelCoordinator extension MapCarsViewModel: MapCarsViewModelCoordinatorDelegate { diff --git a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift index 2f7b340..719042b 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift @@ -9,14 +9,14 @@ import UIKit import MapKit // MARK: - ViewModelType -protocol CatsViewModelType { +protocol MapCarsViewModelType { var viewDelegate: MapCarsViewModelViewDelegate? { get set } // // Data Source // func numberOfItems() -> Int -// -// func itemFor(row: Int) -> UITableViewCell + + func viewFor(annotation: MKAnnotation) -> MKAnnotationView? // // // Events // func add() @@ -35,6 +35,6 @@ protocol MapCarsViewModelCoordinatorDelegate: class { // MARK: - ViewModelViewDelegate protocol MapCarsViewModelViewDelegate: class { - func refreshScreen(with annotaions: [MKPointAnnotation]) + func refreshScreen(with annotaions: [CarAnnotation]) // TODO: make it more abstract func selected(car: Car) } From c86a0689a720cb4eff1be1bb719b41a051e7776c Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 15:27:37 +0200 Subject: [PATCH 023/101] set navigation title for view controllers --- Cars Map/Scenes/ListCars/View/ListCars.storyboard | 4 ++-- Cars Map/Scenes/MapCars/View/MapCars.storyboard | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cars Map/Scenes/ListCars/View/ListCars.storyboard b/Cars Map/Scenes/ListCars/View/ListCars.storyboard index 9ea8087..47636ba 100644 --- a/Cars Map/Scenes/ListCars/View/ListCars.storyboard +++ b/Cars Map/Scenes/ListCars/View/ListCars.storyboard @@ -9,10 +9,10 @@ - + - + diff --git a/Cars Map/Scenes/MapCars/View/MapCars.storyboard b/Cars Map/Scenes/MapCars/View/MapCars.storyboard index 479d05a..b6c07ac 100644 --- a/Cars Map/Scenes/MapCars/View/MapCars.storyboard +++ b/Cars Map/Scenes/MapCars/View/MapCars.storyboard @@ -8,10 +8,10 @@ - + - + From 4f13594612969e8629701f9c96e5eabd419b648d Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 15:33:32 +0200 Subject: [PATCH 024/101] changed all ViewController names to VC and all ViewModel names to VM due to improve code readability. --- Cars Map.xcodeproj/project.pbxproj | 24 +++++++++---------- Cars Map/Scenes/AppCoordinator.swift | 2 +- .../Scenes/MapCars/MapCarsCoordinator.swift | 4 ++-- .../MapCars/ViewController/MapCarsVC.swift | 2 +- ...MapCarsViewModel.swift => MapCarsVM.swift} | 6 ++--- ...bstractions.swift => VMAbstractions.swift} | 2 +- Cars Map/Scenes/Waiting/WaitingVC.swift | 2 +- ...WaitingViewModel.swift => WaitingVM.swift} | 4 ++-- 8 files changed, 23 insertions(+), 23 deletions(-) rename Cars Map/Scenes/MapCars/ViewModel/{MapCarsViewModel.swift => MapCarsVM.swift} (93%) rename Cars Map/Scenes/MapCars/ViewModel/{ViewModelAbstractions.swift => VMAbstractions.swift} (96%) rename Cars Map/Scenes/Waiting/{WaitingViewModel.swift => WaitingVM.swift} (96%) diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 27d4fa7..4f10f74 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -10,7 +10,7 @@ 879EFF272804AAA000ED0CD0 /* MapCars.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 879EFF262804AAA000ED0CD0 /* MapCars.storyboard */; }; 879EFF2A2804B17900ED0CD0 /* Waiting.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 879EFF292804B17900ED0CD0 /* Waiting.storyboard */; }; 879EFF2E2804B6F900ED0CD0 /* WaitingVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF2D2804B6F900ED0CD0 /* WaitingVC.swift */; }; - 879EFF332804B85500ED0CD0 /* WaitingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF322804B85500ED0CD0 /* WaitingViewModel.swift */; }; + 879EFF332804B85500ED0CD0 /* WaitingVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF322804B85500ED0CD0 /* WaitingVM.swift */; }; 879EFF362804B95900ED0CD0 /* Car.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF352804B95900ED0CD0 /* Car.swift */; }; 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */; }; 87A8C5E02805B2FD00BF5775 /* CarAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */; }; @@ -20,8 +20,8 @@ 87B6E73228049E110059A1E3 /* MapCarsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73128049E110059A1E3 /* MapCarsVC.swift */; }; 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73A28049EF10059A1E3 /* Coordinator.swift */; }; 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */; }; - 87B6E7412804A1100059A1E3 /* MapCarsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */; }; - 87B6E7422804A1100059A1E3 /* ViewModelAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */; }; + 87B6E7412804A1100059A1E3 /* MapCarsVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */; }; + 87B6E7422804A1100059A1E3 /* VMAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */; }; 87B6E7492804A1310059A1E3 /* ListCars.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E7482804A1310059A1E3 /* ListCars.storyboard */; }; 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */; }; 87B6E7502804A4700059A1E3 /* MapCarsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */; }; @@ -33,7 +33,7 @@ 879EFF262804AAA000ED0CD0 /* MapCars.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = MapCars.storyboard; sourceTree = ""; }; 879EFF292804B17900ED0CD0 /* Waiting.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Waiting.storyboard; sourceTree = ""; }; 879EFF2D2804B6F900ED0CD0 /* WaitingVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingVC.swift; sourceTree = ""; }; - 879EFF322804B85500ED0CD0 /* WaitingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingViewModel.swift; sourceTree = ""; }; + 879EFF322804B85500ED0CD0 /* WaitingVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitingVM.swift; sourceTree = ""; }; 879EFF352804B95900ED0CD0 /* Car.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Car.swift; sourceTree = ""; }; 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarAnnotationView.swift; sourceTree = ""; }; 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarAnnotation.swift; sourceTree = ""; }; @@ -45,8 +45,8 @@ 87B6E73128049E110059A1E3 /* MapCarsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsVC.swift; sourceTree = ""; }; 87B6E73A28049EF10059A1E3 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; - 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsViewModel.swift; sourceTree = ""; }; - 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewModelAbstractions.swift; sourceTree = ""; }; + 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsVM.swift; sourceTree = ""; }; + 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMAbstractions.swift; sourceTree = ""; }; 87B6E7482804A1310059A1E3 /* ListCars.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ListCars.storyboard; sourceTree = ""; }; 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCarsVC.swift; sourceTree = ""; }; 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCarsCoordinator.swift; sourceTree = ""; }; @@ -70,7 +70,7 @@ children = ( 879EFF292804B17900ED0CD0 /* Waiting.storyboard */, 879EFF2D2804B6F900ED0CD0 /* WaitingVC.swift */, - 879EFF322804B85500ED0CD0 /* WaitingViewModel.swift */, + 879EFF322804B85500ED0CD0 /* WaitingVM.swift */, ); path = Waiting; sourceTree = ""; @@ -182,8 +182,8 @@ 87B6E72A28049DD00059A1E3 /* ViewModel */ = { isa = PBXGroup; children = ( - 87B6E7402804A1100059A1E3 /* ViewModelAbstractions.swift */, - 87B6E73F2804A1100059A1E3 /* MapCarsViewModel.swift */, + 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */, + 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */, ); path = ViewModel; sourceTree = ""; @@ -278,16 +278,16 @@ buildActionMask = 2147483647; files = ( 87A8C5E02805B2FD00BF5775 /* CarAnnotation.swift in Sources */, - 879EFF332804B85500ED0CD0 /* WaitingViewModel.swift in Sources */, + 879EFF332804B85500ED0CD0 /* WaitingVM.swift in Sources */, 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */, 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */, - 87B6E7422804A1100059A1E3 /* ViewModelAbstractions.swift in Sources */, + 87B6E7422804A1100059A1E3 /* VMAbstractions.swift in Sources */, 879EFF2E2804B6F900ED0CD0 /* WaitingVC.swift in Sources */, 879EFF362804B95900ED0CD0 /* Car.swift in Sources */, 87B6E73228049E110059A1E3 /* MapCarsVC.swift in Sources */, 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */, 87B6E70728049BE60059A1E3 /* AppDelegate.swift in Sources */, - 87B6E7412804A1100059A1E3 /* MapCarsViewModel.swift in Sources */, + 87B6E7412804A1100059A1E3 /* MapCarsVM.swift in Sources */, 87B6E7502804A4700059A1E3 /* MapCarsCoordinator.swift in Sources */, 87B6E7532804A4C50059A1E3 /* ListCarsCoordinator.swift in Sources */, 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */, diff --git a/Cars Map/Scenes/AppCoordinator.swift b/Cars Map/Scenes/AppCoordinator.swift index 4e89099..d777b9e 100644 --- a/Cars Map/Scenes/AppCoordinator.swift +++ b/Cars Map/Scenes/AppCoordinator.swift @@ -59,7 +59,7 @@ extension AppCoordinator { private func startWaitingVC() { let waitingStoryBoard = UIStoryboard.init(name: "Waiting", bundle: nil) let waitingVC = waitingStoryBoard.instantiateViewController(withIdentifier: "WaitingVC") as! WaitingVC - let waitingVM = WaitingViewModel(apiClient: apiClient) + let waitingVM = WaitingVM(apiClient: apiClient) waitingVM.appCoordinatorDelegate = self waitingVC.viewModel = waitingVM window?.rootViewController = waitingVC diff --git a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift index ed97681..aeafc5c 100644 --- a/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift +++ b/Cars Map/Scenes/MapCars/MapCarsCoordinator.swift @@ -17,8 +17,8 @@ class MapCarsCoordinator: Coordinator { // MARK: VM private var cars: [Car] - private var mapCarsVM: MapCarsViewModel { - let mapCarsVM = MapCarsViewModel() + private var mapCarsVM: MapCarsVM { + let mapCarsVM = MapCarsVM() mapCarsVM.mapCarsCoordinatorDelegate = self mapCarsVM.cars = self.cars return mapCarsVM diff --git a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift index 967d547..272312e 100644 --- a/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift +++ b/Cars Map/Scenes/MapCars/ViewController/MapCarsVC.swift @@ -15,7 +15,7 @@ class MapCarsVC: UIViewController { // MARK: Properties - var viewModel: MapCarsViewModel! { + var viewModel: MapCarsVM! { didSet { viewModel.viewDelegate = self } diff --git a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift b/Cars Map/Scenes/MapCars/ViewModel/MapCarsVM.swift similarity index 93% rename from Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift rename to Cars Map/Scenes/MapCars/ViewModel/MapCarsVM.swift index b0d0e1e..ba2318b 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/MapCarsViewModel.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/MapCarsVM.swift @@ -8,7 +8,7 @@ import UIKit import MapKit -class MapCarsViewModel { +class MapCarsVM { // MARK: Delegates var mapCarsCoordinatorDelegate: MapCarsViewModelCoordinatorDelegate? @@ -40,7 +40,7 @@ class MapCarsViewModel { // MARK: - ViewModelType -extension MapCarsViewModel: MapCarsViewModelType { +extension MapCarsVM: MapCarsVMType { func viewFor(annotation: MKAnnotation) -> MKAnnotationView? { //Handle user location annotation.. @@ -62,7 +62,7 @@ extension MapCarsViewModel: MapCarsViewModelType { } // MARK: - ViewModelCoordinator -extension MapCarsViewModel: MapCarsViewModelCoordinatorDelegate { +extension MapCarsVM: MapCarsViewModelCoordinatorDelegate { func didSelect(car: Car, from controller: UIViewController) { mapCarsCoordinatorDelegate?.didSelect(car: car, from: controller) diff --git a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift b/Cars Map/Scenes/MapCars/ViewModel/VMAbstractions.swift similarity index 96% rename from Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift rename to Cars Map/Scenes/MapCars/ViewModel/VMAbstractions.swift index 719042b..27b8418 100644 --- a/Cars Map/Scenes/MapCars/ViewModel/ViewModelAbstractions.swift +++ b/Cars Map/Scenes/MapCars/ViewModel/VMAbstractions.swift @@ -9,7 +9,7 @@ import UIKit import MapKit // MARK: - ViewModelType -protocol MapCarsViewModelType { +protocol MapCarsVMType { var viewDelegate: MapCarsViewModelViewDelegate? { get set } diff --git a/Cars Map/Scenes/Waiting/WaitingVC.swift b/Cars Map/Scenes/Waiting/WaitingVC.swift index 97f1549..f7bae22 100644 --- a/Cars Map/Scenes/Waiting/WaitingVC.swift +++ b/Cars Map/Scenes/Waiting/WaitingVC.swift @@ -15,7 +15,7 @@ class WaitingVC: UIViewController { @IBOutlet weak var retryBtn: UIButton! // MARK: Properties - var viewModel: WaitingViewModel! { + var viewModel: WaitingVM! { didSet { viewModel.viewDelegate = self } diff --git a/Cars Map/Scenes/Waiting/WaitingViewModel.swift b/Cars Map/Scenes/Waiting/WaitingVM.swift similarity index 96% rename from Cars Map/Scenes/Waiting/WaitingViewModel.swift rename to Cars Map/Scenes/Waiting/WaitingVM.swift index 2b5bc49..e06be84 100644 --- a/Cars Map/Scenes/Waiting/WaitingViewModel.swift +++ b/Cars Map/Scenes/Waiting/WaitingVM.swift @@ -8,7 +8,7 @@ import Foundation -class WaitingViewModel: WaitingViewModelType { +class WaitingVM: WaitingViewModelType { // MARK: Properties private let apiClient: Network @@ -38,7 +38,7 @@ class WaitingViewModel: WaitingViewModelType { } // MARK: Network -extension WaitingViewModel { +extension WaitingVM { func fetch() { apiClient.fetch { [weak self] From 7763e24e0bc3981a405247edfda81b859720aff8 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 16:49:37 +0200 Subject: [PATCH 025/101] ListCars page implemented as well as related files like VM and VC. AppCoordinator adjusted accordingly. --- Cars Map.xcodeproj/project.pbxproj | 16 +++-- Cars Map/Scenes/AppCoordinator.swift | 3 +- .../Scenes/ListCars/ListCarsCoordinator.swift | 35 +++++++---- .../Scenes/ListCars/View/ListCars.storyboard | 22 ++++++- .../ListCars/ViewController/ListCarsVC.swift | 62 ++++++++++++++++++- ...ions.swift => VMMapCarsAbstractions.swift} | 0 6 files changed, 118 insertions(+), 20 deletions(-) rename Cars Map/Scenes/MapCars/ViewModel/{VMAbstractions.swift => VMMapCarsAbstractions.swift} (100%) diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 4f10f74..926b5e3 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -14,6 +14,8 @@ 879EFF362804B95900ED0CD0 /* Car.swift in Sources */ = {isa = PBXBuildFile; fileRef = 879EFF352804B95900ED0CD0 /* Car.swift */; }; 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */; }; 87A8C5E02805B2FD00BF5775 /* CarAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */; }; + 87A8C5F62805B86A00BF5775 /* VMListCarsAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5F42805B86A00BF5775 /* VMListCarsAbstractions.swift */; }; + 87A8C5F72805B86A00BF5775 /* ListCarsVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87A8C5F52805B86A00BF5775 /* ListCarsVM.swift */; }; 87B6E70728049BE60059A1E3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E70628049BE60059A1E3 /* AppDelegate.swift */; }; 87B6E71028049BE70059A1E3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E70F28049BE70059A1E3 /* Assets.xcassets */; }; 87B6E71328049BE70059A1E3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E71128049BE70059A1E3 /* LaunchScreen.storyboard */; }; @@ -21,7 +23,7 @@ 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73A28049EF10059A1E3 /* Coordinator.swift */; }; 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */; }; 87B6E7412804A1100059A1E3 /* MapCarsVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */; }; - 87B6E7422804A1100059A1E3 /* VMAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */; }; + 87B6E7422804A1100059A1E3 /* VMMapCarsAbstractions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7402804A1100059A1E3 /* VMMapCarsAbstractions.swift */; }; 87B6E7492804A1310059A1E3 /* ListCars.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87B6E7482804A1310059A1E3 /* ListCars.storyboard */; }; 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */; }; 87B6E7502804A4700059A1E3 /* MapCarsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */; }; @@ -37,6 +39,8 @@ 879EFF352804B95900ED0CD0 /* Car.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Car.swift; sourceTree = ""; }; 87A8C5D52805B22000BF5775 /* CarAnnotationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarAnnotationView.swift; sourceTree = ""; }; 87A8C5DF2805B2FD00BF5775 /* CarAnnotation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarAnnotation.swift; sourceTree = ""; }; + 87A8C5F42805B86A00BF5775 /* VMListCarsAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMListCarsAbstractions.swift; sourceTree = ""; }; + 87A8C5F52805B86A00BF5775 /* ListCarsVM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCarsVM.swift; sourceTree = ""; }; 87B6E70328049BE60059A1E3 /* Cars Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Cars Map.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 87B6E70628049BE60059A1E3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 87B6E70F28049BE70059A1E3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -46,7 +50,7 @@ 87B6E73A28049EF10059A1E3 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; 87B6E73B28049EF10059A1E3 /* AppCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppCoordinator.swift; sourceTree = ""; }; 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MapCarsVM.swift; sourceTree = ""; }; - 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMAbstractions.swift; sourceTree = ""; }; + 87B6E7402804A1100059A1E3 /* VMMapCarsAbstractions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMMapCarsAbstractions.swift; sourceTree = ""; }; 87B6E7482804A1310059A1E3 /* ListCars.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = ListCars.storyboard; sourceTree = ""; }; 87B6E74B2804A1370059A1E3 /* ListCarsVC.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ListCarsVC.swift; sourceTree = ""; }; 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCarsCoordinator.swift; sourceTree = ""; }; @@ -175,6 +179,8 @@ 87B6E72928049DB30059A1E3 /* ViewModel */ = { isa = PBXGroup; children = ( + 87A8C5F42805B86A00BF5775 /* VMListCarsAbstractions.swift */, + 87A8C5F52805B86A00BF5775 /* ListCarsVM.swift */, ); path = ViewModel; sourceTree = ""; @@ -182,7 +188,7 @@ 87B6E72A28049DD00059A1E3 /* ViewModel */ = { isa = PBXGroup; children = ( - 87B6E7402804A1100059A1E3 /* VMAbstractions.swift */, + 87B6E7402804A1100059A1E3 /* VMMapCarsAbstractions.swift */, 87B6E73F2804A1100059A1E3 /* MapCarsVM.swift */, ); path = ViewModel; @@ -281,9 +287,10 @@ 879EFF332804B85500ED0CD0 /* WaitingVM.swift in Sources */, 87A8C5D62805B22000BF5775 /* CarAnnotationView.swift in Sources */, 87B6E73D28049EF10059A1E3 /* AppCoordinator.swift in Sources */, - 87B6E7422804A1100059A1E3 /* VMAbstractions.swift in Sources */, + 87B6E7422804A1100059A1E3 /* VMMapCarsAbstractions.swift in Sources */, 879EFF2E2804B6F900ED0CD0 /* WaitingVC.swift in Sources */, 879EFF362804B95900ED0CD0 /* Car.swift in Sources */, + 87A8C5F72805B86A00BF5775 /* ListCarsVM.swift in Sources */, 87B6E73228049E110059A1E3 /* MapCarsVC.swift in Sources */, 87B6E74C2804A1370059A1E3 /* ListCarsVC.swift in Sources */, 87B6E70728049BE60059A1E3 /* AppDelegate.swift in Sources */, @@ -292,6 +299,7 @@ 87B6E7532804A4C50059A1E3 /* ListCarsCoordinator.swift in Sources */, 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */, 87B6E7572804A5480059A1E3 /* ApiClient.swift in Sources */, + 87A8C5F62805B86A00BF5775 /* VMListCarsAbstractions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Cars Map/Scenes/AppCoordinator.swift b/Cars Map/Scenes/AppCoordinator.swift index d777b9e..8e4250f 100644 --- a/Cars Map/Scenes/AppCoordinator.swift +++ b/Cars Map/Scenes/AppCoordinator.swift @@ -50,7 +50,8 @@ extension AppCoordinator { mapCarsCoordinator.start() // second tab - let listCarsCoordinator = ListCarsCoordinator(rootTabBarController: rootTabBarController) + let listCarsCoordinator = ListCarsCoordinator(rootTabBarController: rootTabBarController, + cars: cars) self.addChildCoordinator(listCarsCoordinator) listCarsCoordinator.start() } diff --git a/Cars Map/Scenes/ListCars/ListCarsCoordinator.swift b/Cars Map/Scenes/ListCars/ListCarsCoordinator.swift index fc235bd..b70bbf7 100644 --- a/Cars Map/Scenes/ListCars/ListCarsCoordinator.swift +++ b/Cars Map/Scenes/ListCars/ListCarsCoordinator.swift @@ -14,25 +14,38 @@ class ListCarsCoordinator: Coordinator { private var listCarsNavigationContrller = UINavigationController() private let listCarsStoryboard = UIStoryboard(name: "ListCars", bundle: nil) - -// private let apiClient: Network - + // MARK: VM - + private var cars: [Car] + private var listCarsVM: ListCarsVM { + let listCarsVM = ListCarsVM() + listCarsVM.ListCarsCoordinatorDelegate = self + listCarsVM.cars = self.cars + return listCarsVM + } // MARK: Coordinator + init(rootTabBarController: UITabBarController, cars: [Car]) { + // set ListCarsVC to root VC + self.cars = cars + super.init() + self.rootTabBarController = rootTabBarController + } - init(rootTabBarController: UITabBarController) { - // set MapCarsVC to root VC - + override func start() { let listCarsVC = listCarsStoryboard.instantiateViewController(withIdentifier: "ListCarsVC") as! ListCarsVC - listCarsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .bookmarks, tag: 1) // write a test to check if the tab sequences are correct + listCarsVC.viewModel = listCarsVM + listCarsVC.tabBarItem = UITabBarItem(tabBarSystemItem: .recents, tag: 1) listCarsNavigationContrller.setViewControllers([listCarsVC], animated: true) rootTabBarController.viewControllers?.append(listCarsNavigationContrller) + // it appends to the previously set VC(tab) - in this case - MapCarsVC + // write a test to check order of the tabs! } - - override func start() { - print("I'm in ListCarsCoordinator") +} + +extension ListCarsCoordinator: ListCarsViewModelCoordinatorDelegate { + func didSelect(car: Car, from controller: UIViewController) { + print("I'm in ListCarsCoordinator and received a car using MapCarsVMDelegate") } } diff --git a/Cars Map/Scenes/ListCars/View/ListCars.storyboard b/Cars Map/Scenes/ListCars/View/ListCars.storyboard index 47636ba..51c5089 100644 --- a/Cars Map/Scenes/ListCars/View/ListCars.storyboard +++ b/Cars Map/Scenes/ListCars/View/ListCars.storyboard @@ -16,9 +16,25 @@ + + + + + + - + + + + + + + + + + + @@ -26,8 +42,8 @@ - - + + diff --git a/Cars Map/Scenes/ListCars/ViewController/ListCarsVC.swift b/Cars Map/Scenes/ListCars/ViewController/ListCarsVC.swift index 5844b86..0273472 100644 --- a/Cars Map/Scenes/ListCars/ViewController/ListCarsVC.swift +++ b/Cars Map/Scenes/ListCars/ViewController/ListCarsVC.swift @@ -7,4 +7,64 @@ import UIKit -class ListCarsVC: UIViewController {} +class ListCarsVC: UIViewController { + + + // MARK: Properties + var viewModel: ListCarsVM! { + didSet { + viewModel.viewDelegate = self + } + } + + // MARK: Outlets + @IBOutlet weak var tableViewCars: UITableView! + + // MARK: UIViewController + override func loadView() { + super.loadView() + viewModel.start() + } + override func viewDidLoad() { + super.viewDidLoad() + setup() + } + + // MARK: Setup + private func setup() { + setupDelegeates() + } + private func setupDelegeates() { + tableViewCars.delegate = self + tableViewCars.dataSource = self + } + + // MARK: Actions +} + +// MARK: - TableView Delegate & DataSource +extension ListCarsVC: UITableViewDelegate, UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + viewModel.numberOfItems() + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + viewModel.itemFor(row: indexPath.item) + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + viewModel.didSelectRow(indexPath.row, from: self) + } +} + +// MARK: - ViewModel Delegate +extension ListCarsVC: ListCarsViewModelViewDelegate { + func refreshScreen(with annotaions: [Car]) { + print("ListCarsVC received refreshScreen") + } + + func selected(car: Car) { + print("ListCarsVC received selected") + } + +} diff --git a/Cars Map/Scenes/MapCars/ViewModel/VMAbstractions.swift b/Cars Map/Scenes/MapCars/ViewModel/VMMapCarsAbstractions.swift similarity index 100% rename from Cars Map/Scenes/MapCars/ViewModel/VMAbstractions.swift rename to Cars Map/Scenes/MapCars/ViewModel/VMMapCarsAbstractions.swift From fa709af7c5039d7f2dc8ba22a9e18af86651d2c9 Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 17:03:06 +0200 Subject: [PATCH 026/101] view model folder of the ListCars page added to the git. --- .../ListCars/ViewModel/ListCarsVM.swift | 57 +++++++++++++++++++ .../ViewModel/VMListCarsAbstractions.swift | 36 ++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 Cars Map/Scenes/ListCars/ViewModel/ListCarsVM.swift create mode 100644 Cars Map/Scenes/ListCars/ViewModel/VMListCarsAbstractions.swift diff --git a/Cars Map/Scenes/ListCars/ViewModel/ListCarsVM.swift b/Cars Map/Scenes/ListCars/ViewModel/ListCarsVM.swift new file mode 100644 index 0000000..3881990 --- /dev/null +++ b/Cars Map/Scenes/ListCars/ViewModel/ListCarsVM.swift @@ -0,0 +1,57 @@ +// +// CatsViewModel.swift +// Cat Facts +// +// Created by iMamad on 4/8/22. +// + +import UIKit +import MapKit + +class ListCarsVM { + + // MARK: Delegates + var ListCarsCoordinatorDelegate: ListCarsViewModelCoordinatorDelegate? + var viewDelegate: ListCarsViewModelViewDelegate? + + // MARK: Properties + var cars: [Car] = [] + // check if it must be weak or not + + // MARK: Init + init() {} + + func start() {} +} + +// Implement interface below for MapVM + + +// MARK: - ViewModelType +extension ListCarsVM: ListCarsVMType { + func numberOfItems() -> Int { + cars.count + } + + func itemFor(row: Int) -> UITableViewCell { + let cell = UITableViewCell() + let carViewData = CarViewData(car: cars[row]) + cell.textLabel?.text = carViewData.name + return cell + } + + func didSelectRow(_ row: Int, from controller: UIViewController) { + print("row \(row) selected!") + } + + func refreshView() { + viewDelegate?.refreshScreen(with: cars) + } + + +} + +// MARK: - ViewModelCoordinator +extension MapCarsVM: ListCarsViewModelCoordinatorDelegate { + +} diff --git a/Cars Map/Scenes/ListCars/ViewModel/VMListCarsAbstractions.swift b/Cars Map/Scenes/ListCars/ViewModel/VMListCarsAbstractions.swift new file mode 100644 index 0000000..8dd40b0 --- /dev/null +++ b/Cars Map/Scenes/ListCars/ViewModel/VMListCarsAbstractions.swift @@ -0,0 +1,36 @@ +// +// ViewModelAbstractions.swift +// Cat Facts +// +// Created by iMamad on 4/9/22. +// + +import UIKit +import MapKit + +// MARK: - ViewModelType +protocol ListCarsVMType { + + var viewDelegate: ListCarsViewModelViewDelegate? { get set } + + // Data Source + func numberOfItems() -> Int + + func itemFor(row: Int) -> UITableViewCell + + // Events + func didSelectRow(_ row: Int, from controller: UIViewController) + + func refreshView() +} + +// MARK: - ViewModelCoordinator(delegate) +protocol ListCarsViewModelCoordinatorDelegate: class { + func didSelect(car: Car, from controller: UIViewController) +} + +// MARK: - ViewModelViewDelegate +protocol ListCarsViewModelViewDelegate: class { + func refreshScreen(with annotaions: [Car]) + func selected(car: Car) +} From 74bed692feade59d010b59df86fbaddb5b4c20fc Mon Sep 17 00:00:00 2001 From: Mamad Farrahi Date: Tue, 12 Apr 2022 19:08:10 +0200 Subject: [PATCH 027/101] CarInfoVC implemented to show detail about the selected car. --- Cars Map.xcodeproj/project.pbxproj | 24 +++++ .../fill.imageset/Contents.json | 23 +++++ .../Assets.xcassets/fill.imageset/fill@1x.png | Bin 0 -> 6472 bytes .../Assets.xcassets/fill.imageset/fill@2x.png | Bin 0 -> 22698 bytes .../Assets.xcassets/fill.imageset/fill@3x.png | Bin 0 -> 48325 bytes .../gas.imageset/Contents.json | 23 +++++ .../Assets.xcassets/gas.imageset/gas@1x.png | Bin 0 -> 20345 bytes .../Assets.xcassets/gas.imageset/gas@2x.png | Bin 0 -> 54340 bytes .../Assets.xcassets/gas.imageset/gas@3x.png | Bin 0 -> 89941 bytes Cars Map/Scenes/CarInfo/CarInfoVC.swift | 84 +++++++++++++++++ .../Scenes/CarInfo/View/CarInfo.storyboard | 88 ++++++++++++++++++ 11 files changed, 242 insertions(+) create mode 100644 Cars Map/Resources/Assets.xcassets/fill.imageset/Contents.json create mode 100644 Cars Map/Resources/Assets.xcassets/fill.imageset/fill@1x.png create mode 100644 Cars Map/Resources/Assets.xcassets/fill.imageset/fill@2x.png create mode 100644 Cars Map/Resources/Assets.xcassets/fill.imageset/fill@3x.png create mode 100644 Cars Map/Resources/Assets.xcassets/gas.imageset/Contents.json create mode 100644 Cars Map/Resources/Assets.xcassets/gas.imageset/gas@1x.png create mode 100644 Cars Map/Resources/Assets.xcassets/gas.imageset/gas@2x.png create mode 100644 Cars Map/Resources/Assets.xcassets/gas.imageset/gas@3x.png create mode 100644 Cars Map/Scenes/CarInfo/CarInfoVC.swift create mode 100644 Cars Map/Scenes/CarInfo/View/CarInfo.storyboard diff --git a/Cars Map.xcodeproj/project.pbxproj b/Cars Map.xcodeproj/project.pbxproj index 926b5e3..0f5640f 100644 --- a/Cars Map.xcodeproj/project.pbxproj +++ b/Cars Map.xcodeproj/project.pbxproj @@ -29,6 +29,8 @@ 87B6E7502804A4700059A1E3 /* MapCarsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */; }; 87B6E7532804A4C50059A1E3 /* ListCarsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7522804A4C50059A1E3 /* ListCarsCoordinator.swift */; }; 87B6E7572804A5480059A1E3 /* ApiClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87B6E7562804A5480059A1E3 /* ApiClient.swift */; }; + 87DF2AC02805CFDF00AC89BB /* CarInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87DF2ABF2805CFDF00AC89BB /* CarInfoVC.swift */; }; + 87DF2AC42805CFED00AC89BB /* CarInfo.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 87DF2AC32805CFED00AC89BB /* CarInfo.storyboard */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -56,6 +58,8 @@ 87B6E74F2804A4700059A1E3 /* MapCarsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCarsCoordinator.swift; sourceTree = ""; }; 87B6E7522804A4C50059A1E3 /* ListCarsCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCarsCoordinator.swift; sourceTree = ""; }; 87B6E7562804A5480059A1E3 /* ApiClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiClient.swift; sourceTree = ""; }; + 87DF2ABF2805CFDF00AC89BB /* CarInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CarInfoVC.swift; sourceTree = ""; }; + 87DF2AC32805CFED00AC89BB /* CarInfo.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = CarInfo.storyboard; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -69,6 +73,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 8753507F2805D5E100EC6E68 /* View */ = { + isa = PBXGroup; + children = ( + 87DF2AC32805CFED00AC89BB /* CarInfo.storyboard */, + ); + path = View; + sourceTree = ""; + }; 879EFF312804B7FE00ED0CD0 /* Waiting */ = { isa = PBXGroup; children = ( @@ -123,6 +135,7 @@ 879EFF312804B7FE00ED0CD0 /* Waiting */, 87B6E72428049D760059A1E3 /* MapCars */, 87B6E72528049D800059A1E3 /* ListCars */, + 87DF2ABE2805CFAB00AC89BB /* CarInfo */, ); path = Scenes; sourceTree = ""; @@ -211,6 +224,15 @@ path = ViewController; sourceTree = ""; }; + 87DF2ABE2805CFAB00AC89BB /* CarInfo */ = { + isa = PBXGroup; + children = ( + 8753507F2805D5E100EC6E68 /* View */, + 87DF2ABF2805CFDF00AC89BB /* CarInfoVC.swift */, + ); + path = CarInfo; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -273,6 +295,7 @@ 87B6E71328049BE70059A1E3 /* LaunchScreen.storyboard in Resources */, 87B6E71028049BE70059A1E3 /* Assets.xcassets in Resources */, 87B6E7492804A1310059A1E3 /* ListCars.storyboard in Resources */, + 87DF2AC42805CFED00AC89BB /* CarInfo.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -300,6 +323,7 @@ 87B6E73C28049EF10059A1E3 /* Coordinator.swift in Sources */, 87B6E7572804A5480059A1E3 /* ApiClient.swift in Sources */, 87A8C5F62805B86A00BF5775 /* VMListCarsAbstractions.swift in Sources */, + 87DF2AC02805CFDF00AC89BB /* CarInfoVC.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Cars Map/Resources/Assets.xcassets/fill.imageset/Contents.json b/Cars Map/Resources/Assets.xcassets/fill.imageset/Contents.json new file mode 100644 index 0000000..c1b68df --- /dev/null +++ b/Cars Map/Resources/Assets.xcassets/fill.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "fill@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "fill@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "fill@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Cars Map/Resources/Assets.xcassets/fill.imageset/fill@1x.png b/Cars Map/Resources/Assets.xcassets/fill.imageset/fill@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..1ad6e3666c8f1028b794dcd8476b771251b36a39 GIT binary patch literal 6472 zcmeAS@N?(olHy`uVBq!ia0y~yU=jsk4mO}j+7sS7AjMc5z;e%onhy_(MQF|KDF#^S(cJZ(v|#V&M?@ zvFw0%NKNpC`7@Wce^$9LVKYu>{xn}SD=Z};94|GFE<6IVWc0JK_ z;{dlR8dDhqH~#s$XD%>6SvX{b1Vr2I+Ln(>jfMtr+;%i=jAn-sUO4<>sSB) literal 0 HcmV?d00001 diff --git a/Cars Map/Resources/Assets.xcassets/fill.imageset/fill@2x.png b/Cars Map/Resources/Assets.xcassets/fill.imageset/fill@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..85ee6ce1c84161180d2dd085c14fd56ff44f9bac GIT binary patch literal 22698 zcmeHPUr1A76u+BGuPbd{0}Bd~q-AKCFOlU-LXjj0K_n5<-MpXO^94Tk5Wk19adx?X4(I&NIltfc z-EL=1Rgy8yC(%{5pL>5G282FRO1kxi}X( z8dtl`KJ&viF;Lu`>FmpXw4vm&A;;;fOMRJ|l63t1bmjKZ65Gtgx1as9kwZQ~r#FZu zNmhSc7x#E8b|jwrV_E%cv(DR<#T$3)ycN00FL-mrn6Y*~^@mFz>8Vfe&19ER%>i=} zXAy@C0rP{xbpFOHdCmN-Rwr8WT$c*3aZg}%f8i|BjV)I9(0tP-`lym8_=dB{(oRpX z`uRr-@rfjlH4MgSy`%p~<}G;7~YJ z8zzWQnxIH|U??dVz)(^`P?S*R31XscqFp2UQ1qc%#6)9HCml>m7(EGMVwP8_iq)|U zvpmQVf|y{NDA@-34t%H|niHV!$S`UEeYY-s zH|x4u$kqa|90HyKPgyKPTTw{LL?AIiQih~VI#5W;T7d-XoUm|=%&Q`LQuL&Z0*4#| zIfTVRtgV6^LctPp2;>mRA!<%w^u*}7Mn!#&-c45QfhiH)>`+Sg+>vvwH8&gB1itM}Z{z^<0fRwTjRgS}_5 z0QMdWacQpv=IJlyNp1|jdpq^Mvy9dd(bVS%m^(;o70st#iH1l|fvwFwcuP}(qXXMm zon&=S1q;F%$+nA2(+^7zo*f-MW9Lr0=#=u^2Uf!KpRRmvX{~H}@@j6WI53{GmAg`R zXg+0pVc*x*wx<{FPELK#%^+DV$sWIcAk^qzF6}4ufoNLZt8ns0?r3aLz!n7(CqPCZ zBU7&Mh7r7BgvCM(Brs6Ms&?ClkT*?vDPQO5l@rsE-Xf`MBP*-zcYNW-V}2{w1Ng$v z;%5L4OA33rR$>nb3;`k%M|+D2qafKtYzYiWMRRK}4NXcITU7h(JrP4w9ZzdgnvNAh#B>cyFx~HR6jZQTF zpp>t=@-4l)ADRB9rCv2>lPBKXIQ8ULg#(+@1D8)99Jy;qd5Kge=Lg03e{xqpoa|hE z|M9twMQ1-ct`249zBVWZ-Q()@SI)M%ju*ZB!a097{OZzyqIcu9M_+Os_mYE)op-gt zOLpGev*fBdo+!HObZ&*~P^v$X9^U-!^-Z(EnE52@-|0G(xtPlOA9dcHpM_)PiiTjP z>rn30qK2S<|1*Q}CD1?CT>Rd3$RA$XT)f!yOU|{71@)!tP~fjBsL$JfTx(7|>bMh6Q#(7__7po2wD35^6Dj1JZ#RWa&DZ#Wh= z5)9x)rJ@<)feuCoGk~WZ0+JOAmNXj-vG^+_>#^-0{-I|9Y>K8$jC>q3G4io7F}8M) zSWuT1`o9beb?KP6;mv=c`grr-&bTxl1RgXVM4>%oAT%BX9yA{8L`&mA;4wc!~KAHCi4;F=oS zknO0wmDqc|zJSldd|XYYtq;W0M||_4qk(-l=7gg6S-nBWJZl7gnU1i)2A<|33tTm{ zh>qaF)hJa^cyQHK2+?-HB}7-_MrLL^PjYaTXOM%daicN^0m(8%5*A%ev9M_2C=n{@ z95P9}N;C(7OftkH>!X9UW=01a&0lHCQRt|;imTAkIP~h{dKN&ROg<(bQ?{m6M_ zEQE=MqK5z@WU3TBD0}WvLIQIta!Z(lBKI%x_bc0f@DFm zAX&Q5DdGbpOMnqFOU8GEkubg^zz9t;)*Q|s0S6BEZ4`ft5+e9ZAh7fuF!YR}{zZ}P13i_WP-J+24(r#MgP zfoFGZN-6cJf9B*9&poyOgJT;r8UJ<%rm4(yDm8rmz7@`0$hg)}S+Fv7)w1BhHn0s7 zd9e)_7FRNE>|FQuPf9hmb>&-n@kjj8WrNrTwt;QTAb>C63-|)Q5EsQ)lBW@Kh&jX@ zVh%BfdZVlvlNZPf^R+Xik} R`>hK9bhUTqM_UW~{sb%a8cF~F literal 0 HcmV?d00001 diff --git a/Cars Map/Resources/Assets.xcassets/gas.imageset/Contents.json b/Cars Map/Resources/Assets.xcassets/gas.imageset/Contents.json new file mode 100644 index 0000000..f89a6e6 --- /dev/null +++ b/Cars Map/Resources/Assets.xcassets/gas.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "gas@1x.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "gas@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "gas@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Cars Map/Resources/Assets.xcassets/gas.imageset/gas@1x.png b/Cars Map/Resources/Assets.xcassets/gas.imageset/gas@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..22ea39471ed66f5e696db123b49cb0d1fc08516b GIT binary patch literal 20345 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

J- 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 literal 0 HcmV?d00001 diff --git a/Cars Map/Resources/Assets.xcassets/gas.imageset/gas@3x.png b/Cars Map/Resources/Assets.xcassets/gas.imageset/gas@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..1c3d588858013339e88ede0f7998749c84869661 GIT binary patch literal 89941 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≶Ik4_q*JLWyb;#% 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