From bd7761216a065b0dd859cb19d709996739a240cd Mon Sep 17 00:00:00 2001
From: Nicholas Tay <nick@windblume.net>
Date: Sun, 17 Jul 2022 02:25:12 +1000
Subject: Clean up force unwraps and lets

Wow, I didn't know `if let` was a thing back then, haha. Also made
UIImage a bit safer in case asset is missing by unwrapping in one common
place.
---
 foray.xcodeproj/project.pbxproj              |  4 ++++
 foray/Extensions/UIImage+Extensions.swift    | 21 +++++++++++++++++++++
 foray/Fetchers/ForayFetcher.swift            | 14 ++++++++------
 foray/Presenters/PenguinItemPresenter.swift  |  9 +++------
 foray/Scenes/ForayDetailViewController.swift |  4 ++--
 foray/Scenes/ForayTableViewController.swift  |  6 +++---
 6 files changed, 41 insertions(+), 17 deletions(-)
 create mode 100644 foray/Extensions/UIImage+Extensions.swift

diff --git a/foray.xcodeproj/project.pbxproj b/foray.xcodeproj/project.pbxproj
index 3901dc6..df040dd 100644
--- a/foray.xcodeproj/project.pbxproj
+++ b/foray.xcodeproj/project.pbxproj
@@ -7,6 +7,7 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		014E13B128831B6A00C9C353 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 014E13B028831B6A00C9C353 /* UIImage+Extensions.swift */; };
 		C011E4F127E6211400C248D6 /* ForayFetcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = C011E4F027E6211400C248D6 /* ForayFetcher.swift */; };
 		C011E4F327E6216C00C248D6 /* PenguinItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C011E4F227E6216C00C248D6 /* PenguinItemModel.swift */; };
 		C049BBFE27E82B9E003820A9 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C049BBFD27E82B9E003820A9 /* Coordinator.swift */; };
@@ -26,6 +27,7 @@
 /* End PBXBuildFile section */
 
 /* Begin PBXFileReference section */
+		014E13B028831B6A00C9C353 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = "<group>"; };
 		C011E4F027E6211400C248D6 /* ForayFetcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ForayFetcher.swift; sourceTree = "<group>"; };
 		C011E4F227E6216C00C248D6 /* PenguinItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PenguinItemModel.swift; sourceTree = "<group>"; };
 		C049BBFD27E82B9E003820A9 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = "<group>"; };
@@ -106,6 +108,7 @@
 			isa = PBXGroup;
 			children = (
 				C09676BB27EC27E700353D46 /* UIViewController+Extensions.swift */,
+				014E13B028831B6A00C9C353 /* UIImage+Extensions.swift */,
 			);
 			path = Extensions;
 			sourceTree = "<group>";
@@ -231,6 +234,7 @@
 				C04B45A427DEF117001451A3 /* AppDelegate.swift in Sources */,
 				C09676BA27E86B6E00353D46 /* ForayLoadingOverlay.swift in Sources */,
 				C011E4F327E6216C00C248D6 /* PenguinItemModel.swift in Sources */,
+				014E13B128831B6A00C9C353 /* UIImage+Extensions.swift in Sources */,
 				C0C73E6727EC3BA50015497D /* PenguinItemPresenter.swift in Sources */,
 				C04B45A627DEF117001451A3 /* SceneDelegate.swift in Sources */,
 			);
diff --git a/foray/Extensions/UIImage+Extensions.swift b/foray/Extensions/UIImage+Extensions.swift
new file mode 100644
index 0000000..252c3fb
--- /dev/null
+++ b/foray/Extensions/UIImage+Extensions.swift
@@ -0,0 +1,21 @@
+//
+//  UIImage+Extensions.swift
+//  foray
+//
+//  Created by Nicholas Tay on 17/7/2022.
+//
+
+import UIKit
+
+extension UIImage {
+    static func fromAsset(_ assetImage: AssetImage) -> UIImage {
+        return UIImage(named: assetImage.rawValue)!
+    }
+}
+
+/// Known asset images that we can safely unwrap
+enum AssetImage: String {
+    case AppIcon
+    case it
+    case spy
+}
diff --git a/foray/Fetchers/ForayFetcher.swift b/foray/Fetchers/ForayFetcher.swift
index e4c6fd9..15db4c1 100644
--- a/foray/Fetchers/ForayFetcher.swift
+++ b/foray/Fetchers/ForayFetcher.swift
@@ -21,20 +21,22 @@ class ForayFetcher {
             
             let dateFormat = DateFormatter()
             dateFormat.dateFormat = "yyyy-MM-dd"
-            
+
+            // OK to throw as I believe it just errors out the decode; it isn't what we expected schema wise
             return dateFormat.date(from: dateStr)!
         })
         return jd
     }()
     
-    func fetch<T: Decodable>(url: String) async throws -> T {
-        var request = URLRequest(url: URL(string: url)!)
+    func fetch<T: Decodable>(url: URL) async throws -> T {
+        var request = URLRequest(url: url)
         request.cachePolicy = .reloadRevalidatingCacheData // Needed otherwise default caching policy seems not to check properly
 
         // Basic auth if required
-        if (self.basicUsername != nil && self.basicPassword != nil) {
-            let authData = (self.basicUsername! + ":" + self.basicPassword!).data(using: .utf8)!.base64EncodedString()
-            request.addValue("Basic \(authData)", forHTTPHeaderField: "Authorization")
+        if let basicUsername = basicUsername,
+           let basicPassword = basicPassword,
+           let authData = (basicUsername + ":" + basicPassword).data(using: .utf8) {
+            request.addValue("Basic \(authData.base64EncodedString())", forHTTPHeaderField: "Authorization")
         }
 
         let (data, _) = try await URLSession.shared.data(for: request)
diff --git a/foray/Presenters/PenguinItemPresenter.swift b/foray/Presenters/PenguinItemPresenter.swift
index 1d617bf..45b970c 100644
--- a/foray/Presenters/PenguinItemPresenter.swift
+++ b/foray/Presenters/PenguinItemPresenter.swift
@@ -16,12 +16,9 @@ class PenguinItemPresenter {
     let fetcher = ForayFetcher()
     
     func fetch() async -> [PenguinItemViewModel] {
-        do {
-            let apiItems: [PenguinItemModel] = try await fetcher.fetch(url: Constants.apiEndpoint)
-            return transform(models: apiItems)
-        } catch {
-            return []
-        }
+        guard let endpoint = URL(string: Constants.apiEndpoint),
+              let apiItems: [PenguinItemModel] = try? await fetcher.fetch(url: endpoint) else { return [] }
+        return transform(models: apiItems)
     }
     
     func transform(models: [PenguinItemModel]) -> [PenguinItemViewModel] {
diff --git a/foray/Scenes/ForayDetailViewController.swift b/foray/Scenes/ForayDetailViewController.swift
index 1e2f9ca..52aa6b8 100644
--- a/foray/Scenes/ForayDetailViewController.swift
+++ b/foray/Scenes/ForayDetailViewController.swift
@@ -29,10 +29,10 @@ class ForayDetailViewController: UIViewController, HasCustomView, Coordinated {
         switch item.type {
         case .item:
             description += "Item"
-            image = UIImage(named: item.id)!
+            image = UIImage(named: item.id) ?? UIImage.fromAsset(.it)
         case .quest:
             description += "Quest"
-            image = UIImage(named: "spy")!
+            image = UIImage.fromAsset(.spy)
         }
         description += "\nID: " + item.id
         description += "\nReleased: " + item.releaseDateFormatted
diff --git a/foray/Scenes/ForayTableViewController.swift b/foray/Scenes/ForayTableViewController.swift
index 849553c..3d839a1 100644
--- a/foray/Scenes/ForayTableViewController.swift
+++ b/foray/Scenes/ForayTableViewController.swift
@@ -87,13 +87,13 @@ class ForayTableViewController: UITableViewController, Coordinated {
         switch item.type {
         case .item:
             type = "Item"
-            icon = UIImage(named: item.id)!
+            icon = UIImage(named: item.id) ?? UIImage.fromAsset(.it)
         case .quest:
             type = "Quest"
-            icon = UIImage(named: "spy")!
+            icon = UIImage.fromAsset(.spy)
         }
         
-        let cell: ForayTableViewCell = tableView.dequeueReusableCell(withIdentifier: "ForayTableViewCell", for: indexPath) as! ForayTableViewCell
+        guard let cell = tableView.dequeueReusableCell(withIdentifier: "ForayTableViewCell", for: indexPath) as? ForayTableViewCell else { return ForayTableViewCell() }
         cell.setData(name: item.name, desc: type + "ID: " + item.id, img: icon)
         return cell
     }
-- 
cgit