From d8624dbf8a9111f931802cbb2759ebd009096552 Mon Sep 17 00:00:00 2001 From: Nicholas Tay Date: Thu, 24 Mar 2022 17:07:51 +1100 Subject: First attempt at Presenter This is admittedly pretty hard for me to wrap my head around, and I'm not even using background threading explicitly yet. Will improve. --- foray.xcodeproj/project.pbxproj | 24 ++++++++++++++--- foray/Coordinators/ForayCoordinator.swift | 6 ++--- foray/ForayItems.swift | 25 ------------------ foray/ForayModels.swift | 20 +++++++++++++++ foray/ForayViewModels.swift | 16 ++++++++++++ foray/Presenters/PenguinItemPresenter.swift | 32 +++++++++++++++++++++++ foray/Scenes/ForayTableViewController.swift | 40 +++++++++++++---------------- 7 files changed, 108 insertions(+), 55 deletions(-) delete mode 100644 foray/ForayItems.swift create mode 100644 foray/ForayModels.swift create mode 100644 foray/ForayViewModels.swift create mode 100644 foray/Presenters/PenguinItemPresenter.swift diff --git a/foray.xcodeproj/project.pbxproj b/foray.xcodeproj/project.pbxproj index e25da72..176b21c 100644 --- a/foray.xcodeproj/project.pbxproj +++ b/foray.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ C011E4F127E6211400C248D6 /* ForayNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C011E4F027E6211400C248D6 /* ForayNetworkManager.swift */; }; - C011E4F327E6216C00C248D6 /* ForayItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = C011E4F227E6216C00C248D6 /* ForayItems.swift */; }; + C011E4F327E6216C00C248D6 /* ForayModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C011E4F227E6216C00C248D6 /* ForayModels.swift */; }; C049BBFE27E82B9E003820A9 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C049BBFD27E82B9E003820A9 /* Coordinator.swift */; }; C049BC0027E82C90003820A9 /* ForayCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C049BBFF27E82C90003820A9 /* ForayCoordinator.swift */; }; C04B45A427DEF117001451A3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = C04B45A327DEF117001451A3 /* AppDelegate.swift */; }; @@ -20,12 +20,14 @@ C09676BA27E86B6E00353D46 /* ForayLoadingOverlay.swift in Sources */ = {isa = PBXBuildFile; fileRef = C09676B927E86B6E00353D46 /* ForayLoadingOverlay.swift */; }; C09676BC27EC27E700353D46 /* UIViewController+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C09676BB27EC27E700353D46 /* UIViewController+Extensions.swift */; }; C09676BE27EC28B100353D46 /* ForayDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C09676BD27EC28B100353D46 /* ForayDetailView.swift */; }; + C0C73E6427EC3A650015497D /* ForayViewModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C73E6327EC3A650015497D /* ForayViewModels.swift */; }; + C0C73E6727EC3BA50015497D /* PenguinItemPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0C73E6627EC3BA50015497D /* PenguinItemPresenter.swift */; }; C0FEAF5F27E14C52000A7648 /* ForayDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FEAF5E27E14C52000A7648 /* ForayDetailViewController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ C011E4F027E6211400C248D6 /* ForayNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayNetworkManager.swift; sourceTree = ""; }; - C011E4F227E6216C00C248D6 /* ForayItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayItems.swift; sourceTree = ""; }; + C011E4F227E6216C00C248D6 /* ForayModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayModels.swift; sourceTree = ""; }; C049BBFD27E82B9E003820A9 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; C049BBFF27E82C90003820A9 /* ForayCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayCoordinator.swift; sourceTree = ""; }; C04B45A027DEF117001451A3 /* foray.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = foray.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -38,6 +40,8 @@ C09676B927E86B6E00353D46 /* ForayLoadingOverlay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayLoadingOverlay.swift; sourceTree = ""; }; C09676BB27EC27E700353D46 /* UIViewController+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extensions.swift"; sourceTree = ""; }; C09676BD27EC28B100353D46 /* ForayDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayDetailView.swift; sourceTree = ""; }; + C0C73E6327EC3A650015497D /* ForayViewModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayViewModels.swift; sourceTree = ""; }; + C0C73E6627EC3BA50015497D /* PenguinItemPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinItemPresenter.swift; sourceTree = ""; }; C0FEAF5E27E14C52000A7648 /* ForayDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayDetailViewController.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -72,12 +76,14 @@ C04B45A227DEF117001451A3 /* foray */ = { isa = PBXGroup; children = ( + C0C73E6527EC3B8F0015497D /* Presenters */, C09676C327EC358F00353D46 /* Coordinators */, C09676C227EC354700353D46 /* Extensions */, C09676C127EC353D00353D46 /* Scenes */, C04B45A327DEF117001451A3 /* AppDelegate.swift */, C04B45A527DEF117001451A3 /* SceneDelegate.swift */, - C011E4F227E6216C00C248D6 /* ForayItems.swift */, + C011E4F227E6216C00C248D6 /* ForayModels.swift */, + C0C73E6327EC3A650015497D /* ForayViewModels.swift */, C011E4F027E6211400C248D6 /* ForayNetworkManager.swift */, C04B45AC27DEF118001451A3 /* Assets.xcassets */, C04B45B127DEF118001451A3 /* Info.plist */, @@ -114,6 +120,14 @@ path = Coordinators; sourceTree = ""; }; + C0C73E6527EC3B8F0015497D /* Presenters */ = { + isa = PBXGroup; + children = ( + C0C73E6627EC3BA50015497D /* PenguinItemPresenter.swift */, + ); + path = Presenters; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -195,11 +209,13 @@ C09676BE27EC28B100353D46 /* ForayDetailView.swift in Sources */, C04B45B827DEF2ED001451A3 /* ForayTableViewController.swift in Sources */, C0FEAF5F27E14C52000A7648 /* ForayDetailViewController.swift in Sources */, + C0C73E6427EC3A650015497D /* ForayViewModels.swift in Sources */, C04EDE4427E4298D00D83005 /* ForayNewTableViewCell.swift in Sources */, C09676BC27EC27E700353D46 /* UIViewController+Extensions.swift in Sources */, C04B45A427DEF117001451A3 /* AppDelegate.swift in Sources */, C09676BA27E86B6E00353D46 /* ForayLoadingOverlay.swift in Sources */, - C011E4F327E6216C00C248D6 /* ForayItems.swift in Sources */, + C011E4F327E6216C00C248D6 /* ForayModels.swift in Sources */, + C0C73E6727EC3BA50015497D /* PenguinItemPresenter.swift in Sources */, C04B45A627DEF117001451A3 /* SceneDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/foray/Coordinators/ForayCoordinator.swift b/foray/Coordinators/ForayCoordinator.swift index 6497ec0..6c4bb3f 100644 --- a/foray/Coordinators/ForayCoordinator.swift +++ b/foray/Coordinators/ForayCoordinator.swift @@ -33,7 +33,7 @@ class ForayCoordinator: Coordinator { let detailViewController = ForayDetailViewController() - func showDetails(item: PenguinItem) { + func showDetails(item: PenguinItemViewModel) { let image: UIImage var description: String = "Type: " switch item.type { @@ -45,9 +45,7 @@ class ForayCoordinator: Coordinator { image = UIImage(named: "spy")! } description += "\nID: " + item.id - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyy-MM-dd" - description += "\nReleased: " + dateFormatter.string(from: item.releaseDate) + description += "\nReleased: " + item.releaseDateFormatted detailViewController.setDetails(name: item.name, description: description, image: image) push(vc: detailViewController) diff --git a/foray/ForayItems.swift b/foray/ForayItems.swift deleted file mode 100644 index a8786ee..0000000 --- a/foray/ForayItems.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// ForayItems.swift -// foray -// -// Created by Nicholas Tay on 20/3/2022. -// - -import Foundation - -enum ItemType: String, Decodable { - case item - case quest -} - -struct YearSection { - var year: Int - var items: [PenguinItem] -} - -struct PenguinItem: Decodable { - var type: ItemType - var releaseDate: Date - var id: String - var name: String -} diff --git a/foray/ForayModels.swift b/foray/ForayModels.swift new file mode 100644 index 0000000..c721b95 --- /dev/null +++ b/foray/ForayModels.swift @@ -0,0 +1,20 @@ +// +// ForayItems.swift +// foray +// +// Created by Nicholas Tay on 20/3/2022. +// + +import Foundation + +enum PenguinItemType: String, Decodable { + case item + case quest +} + +struct PenguinItemModel: Decodable { + let type: PenguinItemType + let releaseDate: Date + let id: String + let name: String +} diff --git a/foray/ForayViewModels.swift b/foray/ForayViewModels.swift new file mode 100644 index 0000000..9b9b058 --- /dev/null +++ b/foray/ForayViewModels.swift @@ -0,0 +1,16 @@ +// +// ForayViewModels.swift +// foray +// +// Created by Nicholas Tay on 24/3/2022. +// + +import Foundation + +struct PenguinItemViewModel { + let type: PenguinItemType + let releaseDateFormatted: String + let year: Int + let id: String + let name: String +} diff --git a/foray/Presenters/PenguinItemPresenter.swift b/foray/Presenters/PenguinItemPresenter.swift new file mode 100644 index 0000000..0420825 --- /dev/null +++ b/foray/Presenters/PenguinItemPresenter.swift @@ -0,0 +1,32 @@ +// +// PenguinItemPresenter.swift +// foray +// +// Created by Nicholas Tay on 24/3/2022. +// + +import Foundation + +class PenguinItemPresenter { + func getData(onComplete: @escaping ([PenguinItemViewModel]) -> ()) { + ForayNetworkManager.shared.get( + url: "https://users.windblume.net/~nick/upload/dummy.json", + onComplete: { (apiItems: [PenguinItemModel]) in + onComplete(self.transform(models: apiItems)) + }) + } + + func transform(models: [PenguinItemModel]) -> [PenguinItemViewModel] { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" + + return models + .sorted { $0.releaseDate < $1.releaseDate } + .map { + PenguinItemViewModel(type: $0.type, + releaseDateFormatted: dateFormatter.string(from: $0.releaseDate), + year: Calendar.current.component(.year, from: $0.releaseDate), + id: $0.id, name: $0.name) + } + } +} diff --git a/foray/Scenes/ForayTableViewController.swift b/foray/Scenes/ForayTableViewController.swift index 37ac2e5..ed44139 100644 --- a/foray/Scenes/ForayTableViewController.swift +++ b/foray/Scenes/ForayTableViewController.swift @@ -7,8 +7,14 @@ import UIKit +struct YearSection { + var year: Int + var items: [PenguinItemViewModel] +} + class ForayTableViewController: UITableViewController, ForayCoordinated { + let presenter: PenguinItemPresenter = PenguinItemPresenter() var coordinator: ForayCoordinator? // MARK: - Static data TEMP @@ -39,28 +45,18 @@ class ForayTableViewController: UITableViewController, ForayCoordinated { } func reloadApiData() { - ForayNetworkManager.shared.get( - url: "https://users.windblume.net/~nick/upload/dummy.json", - onComplete: { (apiItems: [PenguinItem]) in - var items = apiItems - - // Show items in chronological order within sections - items.sort { (lhs, rhs) in lhs.releaseDate < rhs.releaseDate } - - // Group by year sections - let groups = Dictionary(grouping: apiItems) { (item) in - return Calendar.current.component(.year, from: item.releaseDate) - } - self.sections = groups.map { (key, values) in - return YearSection(year: key, items: values) - } - // Sort the sections from oldest year to newest - self.sections.sort { (lhs, rhs) in lhs.year < rhs.year } - - self.tableView.reloadData() - self.refreshControl?.endRefreshing() - self.coordinator?.hideLoading() - }) + presenter.getData(onComplete: { (data: [PenguinItemViewModel]) in + let groups = Dictionary(grouping: data) { $0.year } + self.sections = groups.map { (key, values) in + return YearSection(year: key, items: values) + } + // Sort the sections from oldest year to newest + self.sections.sort { (lhs, rhs) in lhs.year < rhs.year } + + self.tableView.reloadData() + self.refreshControl?.endRefreshing() + self.coordinator?.hideLoading() + }) } // MARK: - Table view data source -- cgit