From 55b930a38eb8c5a4228660aca049ad145e7dd7ac Mon Sep 17 00:00:00 2001
From: Marceau TONELLI <marceau.tonelli@gmail.com>
Date: Thu, 23 Jan 2025 08:23:23 +0100
Subject: [PATCH] Refactor FXIOS-10205 [Swiftlint] Enable
 implicitly_unwrapped_optional rule but keep it disabled for test files using
 nested Swiftlint configurations (#24031)

* Enable implicitly_unwrapped_optional rule in swiftlint configuration

* Disable rule for test files using swiftlint nested configuration

* Disable rule for focus-ios using swiftlint nested config

* Resolve implicitly_unwrapped_optional violation in SampleComponentLibraryApp

* Resolve implicitly_unwrapped_optional violations in SampleBrowser
---
 .swiftlint.yml                                           | 1 +
 BrowserKit/Tests/.swiftlint.yml                          | 9 +++++++++
 SampleBrowser/SampleBrowser/Engine/EngineProvider.swift  | 8 ++++----
 SampleBrowser/SampleBrowser/SceneDelegate.swift          | 4 ++--
 .../SampleBrowser/UI/Browser/BrowserViewController.swift | 4 ++--
 .../UI/Suggestion/SuggestionViewController.swift         | 6 +++---
 .../BottomSheet/BottomSheetChildViewController.swift     | 2 --
 firefox-ios/firefox-ios-tests/Tests/.swiftlint.yml       | 9 +++++++++
 focus-ios/.swiftlint.yml                                 | 1 +
 9 files changed, 31 insertions(+), 13 deletions(-)
 create mode 100644 BrowserKit/Tests/.swiftlint.yml
 create mode 100644 firefox-ios/firefox-ios-tests/Tests/.swiftlint.yml

diff --git a/.swiftlint.yml b/.swiftlint.yml
index ab4253536231..01503755c9d6 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -86,6 +86,7 @@ only_rules: # Only enforce these rules, ignore all others
   - accessibility_trait_for_button
   - force_cast
   - closure_body_length
+  - implicitly_unwrapped_optional
 
 # These rules were originally opted into. Disabling for now to get
 # Swiftlint up and running.
diff --git a/BrowserKit/Tests/.swiftlint.yml b/BrowserKit/Tests/.swiftlint.yml
new file mode 100644
index 000000000000..e23a7921dc34
--- /dev/null
+++ b/BrowserKit/Tests/.swiftlint.yml
@@ -0,0 +1,9 @@
+# Swiftlint configuration overloads that will be applied to all files
+# in this folder and all its children recursively, unless another
+# nested configuration takes over.
+# https://github.com/realm/SwiftLint#nested-configurations.
+
+disabled_rules:
+  # Many test use implicitly unwrapped optional properties that 
+  # initialized in setUp and reset in tearDown.
+  - implicitly_unwrapped_optional
diff --git a/SampleBrowser/SampleBrowser/Engine/EngineProvider.swift b/SampleBrowser/SampleBrowser/Engine/EngineProvider.swift
index 39e4d3000825..2ec0b59ed83b 100644
--- a/SampleBrowser/SampleBrowser/Engine/EngineProvider.swift
+++ b/SampleBrowser/SampleBrowser/Engine/EngineProvider.swift
@@ -7,15 +7,15 @@ import WebEngine
 
 struct EngineProvider {
     // We only have one session in the SampleBrowser
-    private(set) var session: EngineSession?
+    private(set) var session: EngineSession
     let view: EngineView
 
-    init(engine: Engine = WKEngine.factory(),
-         sessionDependencies: EngineSessionDependencies? = nil) {
+    init?(engine: Engine = WKEngine.factory(),
+          sessionDependencies: EngineSessionDependencies? = nil) {
         do {
             session = try engine.createSession(dependencies: sessionDependencies)
         } catch {
-            session = nil
+            return nil
         }
 
         view = engine.createView()
diff --git a/SampleBrowser/SampleBrowser/SceneDelegate.swift b/SampleBrowser/SampleBrowser/SceneDelegate.swift
index 62d64bdd8501..4d655c5982ad 100644
--- a/SampleBrowser/SampleBrowser/SceneDelegate.swift
+++ b/SampleBrowser/SampleBrowser/SceneDelegate.swift
@@ -8,7 +8,7 @@ import WebEngine
 
 class SceneDelegate: UIResponder, UIWindowSceneDelegate {
     var window: UIWindow?
-    var engineProvider: EngineProvider = {
+    var engineProvider: EngineProvider? = {
         let dependencies = EngineSessionDependencies(telemetryProxy: TelemetryHandler())
         return EngineProvider(sessionDependencies: dependencies)
     }()
@@ -16,7 +16,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
     func scene(_ scene: UIScene,
                willConnectTo session: UISceneSession,
                options connectionOptions: UIScene.ConnectionOptions) {
-        guard let windowScene = (scene as? UIWindowScene) else { return }
+        guard let windowScene = (scene as? UIWindowScene), let engineProvider else { return }
         let windowUUID = UUID()
         let baseViewController = RootViewController(engineProvider: engineProvider, windowUUID: windowUUID)
         window = UIWindow(windowScene: windowScene)
diff --git a/SampleBrowser/SampleBrowser/UI/Browser/BrowserViewController.swift b/SampleBrowser/SampleBrowser/UI/Browser/BrowserViewController.swift
index 3acb6a6f3508..76a67d49e781 100644
--- a/SampleBrowser/SampleBrowser/UI/Browser/BrowserViewController.swift
+++ b/SampleBrowser/SampleBrowser/UI/Browser/BrowserViewController.swift
@@ -21,7 +21,7 @@ class BrowserViewController: UIViewController,
                              FindInPageHelperDelegate {
     weak var navigationDelegate: NavigationDelegate?
     private lazy var progressView: UIProgressView = .build { _ in }
-    private var engineSession: EngineSession!
+    private var engineSession: EngineSession
     private var engineView: EngineView
     private let urlFormatter: URLFormatter
 
@@ -203,7 +203,7 @@ class BrowserViewController: UIViewController,
         guard let url = linkURL else { return nil }
 
         let previewProvider: UIContextMenuContentPreviewProvider = {
-            let previewEngineProvider = EngineProvider()
+            guard let previewEngineProvider = EngineProvider() else { return nil }
             let previewVC = BrowserViewController(engineProvider: previewEngineProvider)
             previewVC.engineSession.load(url: url.absoluteString)
             return previewVC
diff --git a/SampleBrowser/SampleBrowser/UI/Suggestion/SuggestionViewController.swift b/SampleBrowser/SampleBrowser/UI/Suggestion/SuggestionViewController.swift
index eaa38f61fb8c..ab9b72b3c8d7 100644
--- a/SampleBrowser/SampleBrowser/UI/Suggestion/SuggestionViewController.swift
+++ b/SampleBrowser/SampleBrowser/UI/Suggestion/SuggestionViewController.swift
@@ -10,7 +10,7 @@ protocol SuggestionViewControllerDelegate: AnyObject {
 
 class SuggestionViewController: UIViewController, UITableViewDelegate {
     private var tableView: UITableView
-    private var dataSource: SuggestionDataSource!
+    private var dataSource: SuggestionDataSource?
     private weak var delegate: SuggestionViewControllerDelegate?
 
     private var gradientLayer: CAGradientLayer?
@@ -87,14 +87,14 @@ class SuggestionViewController: UIViewController, UITableViewDelegate {
 
     func updateUI(for suggestions: [String]) {
         tableView.isHidden = suggestions.isEmpty
-        dataSource.suggestions = suggestions
+        dataSource?.suggestions = suggestions
         tableView.reloadData()
     }
 
     // MARK: - UITableViewDelegate
 
     func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
-        let term = dataSource.suggestions[indexPath.row]
+        guard let term = dataSource?.suggestions[indexPath.row] else { return }
         delegate?.tapOnSuggestion(term: term)
     }
 }
diff --git a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetChildViewController.swift b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetChildViewController.swift
index 8d990afefba9..2c931f5d2b1c 100644
--- a/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetChildViewController.swift
+++ b/SampleComponentLibraryApp/SampleComponentLibraryApp/BottomSheet/BottomSheetChildViewController.swift
@@ -25,8 +25,6 @@ class BottomSheetChildViewController: UIViewController, BottomSheetChild, Themea
         label.numberOfLines = 0
     }
 
-    private var heightConstraint: NSLayoutConstraint!
-
     init(themeManager: ThemeManager = AppContainer.shared.resolve()) {
         self.themeManager = themeManager
         super.init(nibName: nil, bundle: nil)
diff --git a/firefox-ios/firefox-ios-tests/Tests/.swiftlint.yml b/firefox-ios/firefox-ios-tests/Tests/.swiftlint.yml
new file mode 100644
index 000000000000..e23a7921dc34
--- /dev/null
+++ b/firefox-ios/firefox-ios-tests/Tests/.swiftlint.yml
@@ -0,0 +1,9 @@
+# Swiftlint configuration overloads that will be applied to all files
+# in this folder and all its children recursively, unless another
+# nested configuration takes over.
+# https://github.com/realm/SwiftLint#nested-configurations.
+
+disabled_rules:
+  # Many test use implicitly unwrapped optional properties that 
+  # initialized in setUp and reset in tearDown.
+  - implicitly_unwrapped_optional
diff --git a/focus-ios/.swiftlint.yml b/focus-ios/.swiftlint.yml
index fe1ba0ba9625..5f978abca4ab 100644
--- a/focus-ios/.swiftlint.yml
+++ b/focus-ios/.swiftlint.yml
@@ -14,4 +14,5 @@ disabled_rules:
   - attributes
   - trailing_whitespace
   - vertical_whitespace
+  - implicitly_unwrapped_optional
   
\ No newline at end of file