From 0989d60768c1ea513b2613550bf178efaf1c3a0f Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 10:54:40 +1100 Subject: [PATCH 1/6] Update `.editorconfig` to indent Swift with 4 spaces outside Xcode --- .editorconfig | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.editorconfig b/.editorconfig index db1bf43b9db9..7f956a19cc9d 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,9 +1,23 @@ +root = true + # Apply to all files [*] +indent_style = space +indent_size = 2 end_of_line = lf insert_final_newline = true +charset = utf-8 trim_trailing_whitespace = true +[*.md] +trim_trailing_whitespace = false + +[*.swift] +indent_size = 4 + +[{*.h,*.m}] +indent_size = 4 + # Ruby specific rules [{*.rb,Fastfile,Gemfile}] indent_style = space From 0d9bde1f29d52c620d15655fc49b33da1daab39b Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 11:27:25 +1100 Subject: [PATCH 2/6] Move `ImmuTableCell` to new Swift package `ImmuTable` --- Modules/Package.swift | 3 ++ Modules/Sources/ImmuTable/ImmuTableCell.swift | 41 +++++++++++++++++ .../Classes/Utility/ImmuTable/ImmuTable.swift | 44 +------------------ .../Utility/ImmuTable/WPImmuTableRows.swift | 3 +- .../Activity/ActivityListRow.swift | 1 + .../Activity/RewindStatusRow.swift | 1 + .../SharingAccountViewController.swift | 3 +- .../LanguageSelectorViewController.swift | 1 + .../AppSettingsViewController.swift | 9 ++-- .../PrivacySettingsViewController.swift | 3 +- .../My Profile/Gravatar/GravatarInfoRow.swift | 3 +- .../ParentPageSettingsViewController.swift | 1 + .../Plans/ViewModels/FeatureItemRow.swift | 1 + .../Plans/ViewModels/PlanListRow.swift | 1 + .../Plugins/ViewModels/PluginListRow.swift | 1 + .../Views/CollectionViewContainerRow.swift | 2 + .../Views/PluginDetailViewHeaderCell.swift | 3 +- .../ReferrerDetailsHeaderRow.swift | 1 + .../Referrer Details/ReferrerDetailsRow.swift | 1 + .../ReferrerDetailsSpamActionRow.swift | 1 + .../GhostViews/StatsGhostTableViewRows.swift | 1 + .../Stats/SiteStatsTableViewCells.swift | 5 ++- .../Support/SupportTableViewController.swift | 1 + .../Tools/Time Zone/Views/TimeZoneRow.swift | 1 + WordPress/WordPressTest/ImmuTableTest.swift | 2 + 25 files changed, 81 insertions(+), 53 deletions(-) create mode 100644 Modules/Sources/ImmuTable/ImmuTableCell.swift diff --git a/Modules/Package.swift b/Modules/Package.swift index e31e80a5a80b..54a4ac0f3d8a 100644 --- a/Modules/Package.swift +++ b/Modules/Package.swift @@ -10,6 +10,7 @@ let package = Package( products: XcodeSupport.products + [ .library(name: "AsyncImageKit", targets: ["AsyncImageKit"]), .library(name: "DesignSystem", targets: ["DesignSystem"]), + .library(name: "ImmuTable", targets: ["ImmuTable"]), .library(name: "JetpackStatsWidgetsCore", targets: ["JetpackStatsWidgetsCore"]), .library(name: "WordPressFlux", targets: ["WordPressFlux"]), .library(name: "WordPressShared", targets: ["WordPressShared"]), @@ -58,6 +59,7 @@ let package = Package( .product(name: "Gifu", package: "Gifu"), ]), .target(name: "DesignSystem", swiftSettings: [.swiftLanguageMode(.v5)]), + .target(name: "ImmuTable", swiftSettings: [.swiftLanguageMode(.v5)]), .target(name: "JetpackStatsWidgetsCore", swiftSettings: [.swiftLanguageMode(.v5)]), .target(name: "UITestsFoundation", dependencies: [ .product(name: "ScreenObject", package: "ScreenObject"), @@ -158,6 +160,7 @@ enum XcodeSupport { return [ .xcodeTarget("XcodeTarget_App", dependencies: [ "DesignSystem", + "ImmuTable", "JetpackStatsWidgetsCore", "WordPressFlux", "WordPressShared", diff --git a/Modules/Sources/ImmuTable/ImmuTableCell.swift b/Modules/Sources/ImmuTable/ImmuTableCell.swift new file mode 100644 index 000000000000..97be8937982b --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableCell.swift @@ -0,0 +1,41 @@ +import UIKit + +// ImmuTableCell describes cell types so they can be registered with a table view. +/// +/// It supports two options: +/// - Nib for Interface Builder defined cells. +/// - Class for cells defined in code. +/// Both cases presume a custom UITableViewCell subclass. If you aren't subclassing, +/// you can also use UITableViewCell as the type. +/// +/// - Note: If you need to use any cell style other than .Default we recommend you +/// subclass UITableViewCell and override init(style:reuseIdentifier:). +/// +public enum ImmuTableCell { + + /// A cell using a UINib. Values are the UINib object and the custom cell class. + case nib(UINib, UITableViewCell.Type) + + /// A cell using a custom class. The associated value is the custom cell class. + case `class`(UITableViewCell.Type) + + /// A String that uniquely identifies the cell type + public var reusableIdentifier: String { + switch self { + case .class(let cellClass): + return NSStringFromClass(cellClass) + case .nib(_, let cellClass): + return NSStringFromClass(cellClass) + } + } + + /// The class of the custom cell + public var cellClass: UITableViewCell.Type { + switch self { + case .class(let cellClass): + return cellClass + case .nib(_, let cellClass): + return cellClass + } + } +} diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift index e3f3dfb78fde..76440c515419 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift @@ -1,4 +1,6 @@ +import ImmuTable import UIKit + /** ImmuTable represents the view model for a static UITableView. @@ -173,48 +175,6 @@ extension ImmuTableRow { } } -// MARK: - ImmuTableCell - -/// ImmuTableCell describes cell types so they can be registered with a table view. -/// -/// It supports two options: -/// - Nib for Interface Builder defined cells. -/// - Class for cells defined in code. -/// Both cases presume a custom UITableViewCell subclass. If you aren't subclassing, -/// you can also use UITableViewCell as the type. -/// -/// - Note: If you need to use any cell style other than .Default we recommend you -/// subclass UITableViewCell and override init(style:reuseIdentifier:). -/// -public enum ImmuTableCell { - - /// A cell using a UINib. Values are the UINib object and the custom cell class. - case nib(UINib, UITableViewCell.Type) - - /// A cell using a custom class. The associated value is the custom cell class. - case `class`(UITableViewCell.Type) - - /// A String that uniquely identifies the cell type - public var reusableIdentifier: String { - switch self { - case .class(let cellClass): - return NSStringFromClass(cellClass) - case .nib(_, let cellClass): - return NSStringFromClass(cellClass) - } - } - - /// The class of the custom cell - public var cellClass: UITableViewCell.Type { - switch self { - case .class(let cellClass): - return cellClass - case .nib(_, let cellClass): - return cellClass - } - } -} - // MARK: - /// ImmuTableViewHandler is a helper to facilitate integration of ImmuTable in your diff --git a/WordPress/Classes/Utility/ImmuTable/WPImmuTableRows.swift b/WordPress/Classes/Utility/ImmuTable/WPImmuTableRows.swift index b31d6636a968..29a47788a252 100644 --- a/WordPress/Classes/Utility/ImmuTable/WPImmuTableRows.swift +++ b/WordPress/Classes/Utility/ImmuTable/WPImmuTableRows.swift @@ -1,6 +1,7 @@ import Foundation -import WordPressShared import Gridicons +import ImmuTable +import WordPressShared struct NavigationItemRow: ImmuTableRow { static let cell = ImmuTableCell.class(WPTableViewCellValue1.self) diff --git a/WordPress/Classes/ViewRelated/Activity/ActivityListRow.swift b/WordPress/Classes/ViewRelated/Activity/ActivityListRow.swift index bbfcdbaf3990..f7f4f549fea4 100644 --- a/WordPress/Classes/ViewRelated/Activity/ActivityListRow.swift +++ b/WordPress/Classes/ViewRelated/Activity/ActivityListRow.swift @@ -1,3 +1,4 @@ +import ImmuTable struct ActivityListRow: ImmuTableRow { typealias CellType = ActivityTableViewCell diff --git a/WordPress/Classes/ViewRelated/Activity/RewindStatusRow.swift b/WordPress/Classes/ViewRelated/Activity/RewindStatusRow.swift index f49312bdb059..91215c48ad3e 100644 --- a/WordPress/Classes/ViewRelated/Activity/RewindStatusRow.swift +++ b/WordPress/Classes/ViewRelated/Activity/RewindStatusRow.swift @@ -1,3 +1,4 @@ +import ImmuTable struct RewindStatusRow: ImmuTableRow { diff --git a/WordPress/Classes/ViewRelated/Blog/Sharing/SharingAccountViewController.swift b/WordPress/Classes/ViewRelated/Blog/Sharing/SharingAccountViewController.swift index d0d096a20361..83a2ea579412 100644 --- a/WordPress/Classes/ViewRelated/Blog/Sharing/SharingAccountViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/Sharing/SharingAccountViewController.swift @@ -1,5 +1,6 @@ -import UIKit +import ImmuTable import Gridicons +import UIKit import WordPressShared /// Displays a list of available keyring connection accounts that can be used to diff --git a/WordPress/Classes/ViewRelated/Blog/Site Settings/LanguageSelectorViewController.swift b/WordPress/Classes/ViewRelated/Blog/Site Settings/LanguageSelectorViewController.swift index f82da0e6754f..7300a475e63a 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Settings/LanguageSelectorViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/Site Settings/LanguageSelectorViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift b/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift index e7e9fad47ccd..6203c990da03 100644 --- a/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/App Settings/AppSettingsViewController.swift @@ -1,11 +1,12 @@ +import DesignSystem import Foundation -import UIKit -import SwiftUI import Gridicons -import WordPressShared +import ImmuTable import SVProgressHUD +import SwiftUI +import UIKit import WordPressFlux -import DesignSystem +import WordPressShared import WordPressUI class AppSettingsViewController: UITableViewController { diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/Privacy Settings/PrivacySettingsViewController.swift b/WordPress/Classes/ViewRelated/Me/App Settings/Privacy Settings/PrivacySettingsViewController.swift index 50be801ad3d7..18f782e38be2 100644 --- a/WordPress/Classes/ViewRelated/Me/App Settings/Privacy Settings/PrivacySettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/App Settings/Privacy Settings/PrivacySettingsViewController.swift @@ -1,6 +1,7 @@ +import AutomatticTracks import Gridicons +import ImmuTable import UIKit -import AutomatticTracks class PrivacySettingsViewController: UITableViewController { fileprivate var handler: ImmuTableViewHandler! diff --git a/WordPress/Classes/ViewRelated/Me/My Profile/Gravatar/GravatarInfoRow.swift b/WordPress/Classes/ViewRelated/Me/My Profile/Gravatar/GravatarInfoRow.swift index 21d7ef93eabd..b50510bb1a11 100644 --- a/WordPress/Classes/ViewRelated/Me/My Profile/Gravatar/GravatarInfoRow.swift +++ b/WordPress/Classes/ViewRelated/Me/My Profile/Gravatar/GravatarInfoRow.swift @@ -1,6 +1,7 @@ +import DesignSystem import Foundation +import ImmuTable import WordPressShared -import DesignSystem struct GravatarInfoRow: ImmuTableRow { diff --git a/WordPress/Classes/ViewRelated/Pages/Controllers/ParentPageSettingsViewController.swift b/WordPress/Classes/ViewRelated/Pages/Controllers/ParentPageSettingsViewController.swift index 927cc792fc8c..f4631b9f8290 100644 --- a/WordPress/Classes/ViewRelated/Pages/Controllers/ParentPageSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Pages/Controllers/ParentPageSettingsViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit private struct Row: ImmuTableRow { diff --git a/WordPress/Classes/ViewRelated/Plans/ViewModels/FeatureItemRow.swift b/WordPress/Classes/ViewRelated/Plans/ViewModels/FeatureItemRow.swift index 45c6b6697f96..b93be1d8fb1e 100644 --- a/WordPress/Classes/ViewRelated/Plans/ViewModels/FeatureItemRow.swift +++ b/WordPress/Classes/ViewRelated/Plans/ViewModels/FeatureItemRow.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListRow.swift b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListRow.swift index ebc458fb5797..9bc423754b25 100644 --- a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListRow.swift +++ b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListRow.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListRow.swift b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListRow.swift index 6950ff27c93c..f0457be0322d 100644 --- a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListRow.swift +++ b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListRow.swift @@ -1,4 +1,5 @@ import Gridicons +import ImmuTable struct PluginListRow: ImmuTableRow { static let cell: ImmuTableCell = { diff --git a/WordPress/Classes/ViewRelated/Plugins/Views/CollectionViewContainerRow.swift b/WordPress/Classes/ViewRelated/Plugins/Views/CollectionViewContainerRow.swift index 9e166ab4f049..579c4327b83f 100644 --- a/WordPress/Classes/ViewRelated/Plugins/Views/CollectionViewContainerRow.swift +++ b/WordPress/Classes/ViewRelated/Plugins/Views/CollectionViewContainerRow.swift @@ -1,3 +1,5 @@ +import ImmuTable + /// A `ImmuTableRow` that contains a `UICollectionView`. /// Currently the `CollectionViewContainerCell` is fairly specific to needs of Plugin Directory, /// but it should be possible to make it more generic in the future if there's a need to — it was deliberately diff --git a/WordPress/Classes/ViewRelated/Plugins/Views/PluginDetailViewHeaderCell.swift b/WordPress/Classes/ViewRelated/Plugins/Views/PluginDetailViewHeaderCell.swift index fbcef418f5e4..fd703cf97232 100644 --- a/WordPress/Classes/ViewRelated/Plugins/Views/PluginDetailViewHeaderCell.swift +++ b/WordPress/Classes/ViewRelated/Plugins/Views/PluginDetailViewHeaderCell.swift @@ -1,6 +1,7 @@ +import Gridicons +import ImmuTable import UIKit import WordPressShared -import Gridicons class PluginDetailViewHeaderCell: UITableViewCell { diff --git a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsHeaderRow.swift b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsHeaderRow.swift index 2e56bc1b1fa3..ed0df3d26f33 100644 --- a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsHeaderRow.swift +++ b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsHeaderRow.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable struct ReferrerDetailsHeaderRow: ImmuTableRow { private typealias CellType = ReferrerDetailsHeaderCell diff --git a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsRow.swift b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsRow.swift index 2b400ff7a3d1..af93f905fe63 100644 --- a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsRow.swift +++ b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsRow.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable struct ReferrerDetailsRow: ImmuTableRow { private typealias CellType = ReferrerDetailsCell diff --git a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsSpamActionRow.swift b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsSpamActionRow.swift index 1eacf837cd05..23c11c97adb8 100644 --- a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsSpamActionRow.swift +++ b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsSpamActionRow.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable struct ReferrerDetailsSpamActionRow: ImmuTableRow { private typealias CellType = ReferrerDetailsSpamActionCell diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift index 24142e7ee0ed..910ec6cb70c8 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/GhostViews/StatsGhostTableViewRows.swift @@ -1,3 +1,4 @@ +import ImmuTable import WordPressUI protocol StatsRowGhostable: StatsHashableImmuTableRow { diff --git a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift index 2034041a03ce..66e45a5261da 100644 --- a/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift +++ b/WordPress/Classes/ViewRelated/Stats/SiteStatsTableViewCells.swift @@ -1,6 +1,7 @@ -import UIKit -import Gridicons import DGCharts +import Gridicons +import ImmuTable +import UIKit // MARK: - Shared Rows diff --git a/WordPress/Classes/ViewRelated/Support/SupportTableViewController.swift b/WordPress/Classes/ViewRelated/Support/SupportTableViewController.swift index d8b3773b1b90..bda1886c748f 100644 --- a/WordPress/Classes/ViewRelated/Support/SupportTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Support/SupportTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressAuthenticator diff --git a/WordPress/Classes/ViewRelated/Tools/Time Zone/Views/TimeZoneRow.swift b/WordPress/Classes/ViewRelated/Tools/Time Zone/Views/TimeZoneRow.swift index ae83130b07b1..aae9398b9f84 100644 --- a/WordPress/Classes/ViewRelated/Tools/Time Zone/Views/TimeZoneRow.swift +++ b/WordPress/Classes/ViewRelated/Tools/Time Zone/Views/TimeZoneRow.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit struct TimeZoneRow: ImmuTableRow { diff --git a/WordPress/WordPressTest/ImmuTableTest.swift b/WordPress/WordPressTest/ImmuTableTest.swift index cc3a5b566da3..2ac188795d30 100644 --- a/WordPress/WordPressTest/ImmuTableTest.swift +++ b/WordPress/WordPressTest/ImmuTableTest.swift @@ -1,3 +1,5 @@ +// FIXME: Move to ImmuTable package tests +import ImmuTable import XCTest @testable import WordPress From 94a9f18e93feafde528e34ccaa54aa08bfca6189 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 11:47:07 +1100 Subject: [PATCH 3/6] Move `ImmuTableRow` to `ImmuTable` package --- .../Sources/ImmuTable/ImmuTableAction.swift | 1 + Modules/Sources/ImmuTable/ImmuTableRow.swift | 73 +++++++++++++++++ .../ImmuTable/ImmuTable+Optional.swift | 1 + .../Classes/Utility/ImmuTable/ImmuTable.swift | 78 ------------------- .../ImmuTable/ImmuTableViewController.swift | 1 + .../AztecAttachmentViewController.swift | 3 +- .../LinkSettingsViewController.swift | 1 + ...eAndTimeFormatSettingsViewController.swift | 1 + .../HomepageSettingsViewController.swift | 1 + .../JetpackConnectionViewController.swift | 1 + .../JetpackSettingsViewController.swift | 1 + .../AccountSettingsViewController.swift | 3 +- .../Me/Me Main/MeViewController.swift | 3 +- .../My Profile/MyProfileViewController.swift | 1 + .../Media/MediaItemViewController.swift | 3 +- .../Controllers/PlanListViewController.swift | 1 + .../ViewModels/PlanDetailViewModel.swift | 1 + .../Plans/ViewModels/PlanListViewModel.swift | 1 + .../ViewModels/PluginDirectoryViewModel.swift | 3 +- .../ViewModels/PluginListViewModel.swift | 1 + .../Plugins/ViewModels/PluginViewModel.swift | 1 + .../InsightsManagementViewController.swift | 3 +- ...SiteStatsInsightsTableViewController.swift | 1 + .../Period Stats/HashableImmutableRow.swift | 1 + .../SiteStatsPeriodTableViewController.swift | 3 +- .../SiteStatsPeriodViewModel.swift | 1 + .../ReferrerDetailsTableViewController.swift | 1 + .../ReferrerDetailsViewModel.swift | 1 + .../PostStatsTableViewController.swift | 1 + .../SiteStatsDetailTableViewController.swift | 1 + ...tsInsightsDetailsTableViewController.swift | 1 + .../StatsSubscribersViewController.swift | 3 +- .../ViewRelated/Tools/SettingsCommon.swift | 1 + .../Time Zone/TimeZoneSelectorViewModel.swift | 1 + 34 files changed, 113 insertions(+), 86 deletions(-) create mode 100644 Modules/Sources/ImmuTable/ImmuTableAction.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTableRow.swift diff --git a/Modules/Sources/ImmuTable/ImmuTableAction.swift b/Modules/Sources/ImmuTable/ImmuTableAction.swift new file mode 100644 index 000000000000..6cde4f57274f --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableAction.swift @@ -0,0 +1 @@ +public typealias ImmuTableAction = (ImmuTableRow) -> Void diff --git a/Modules/Sources/ImmuTable/ImmuTableRow.swift b/Modules/Sources/ImmuTable/ImmuTableRow.swift new file mode 100644 index 000000000000..a2d672c231a7 --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableRow.swift @@ -0,0 +1,73 @@ +import UIKit + +/// ImmuTableRow represents the minimum common elements of a row model. +/// +/// You should implement your own types that conform to ImmuTableRow to define your custom rows. +/// +public protocol ImmuTableRow { + + /** + The closure to call when the row is tapped. The row is passed as an argument to the closure. + + To improve readability, we recommend that you implement the action logic in one of + your view controller methods, instead of including the closure inline. + + Also, be mindful of retain cycles. If your closure needs to reference `self` in + any way, make sure to use `[unowned self]` in the parameter list. + + An example row with its action could look like this: + + class ViewController: UITableViewController { + + func buildViewModel() { + let item1Row = NavigationItemRow(title: "Item 1", action: navigationAction()) + ... + } + + func navigationAction() -> ImmuTableRow -> Void { + return { [unowned self] row in + let controller = self.controllerForRow(row) + self.navigationController?.pushViewController(controller, animated: true) + } + } + + ... + + } + + */ + var action: ImmuTableAction? { get } + + /// This method is called when an associated cell needs to be configured. + /// + /// - Precondition: You can assume that the passed cell is of the type defined + /// by cell.cellClass and force downcast accordingly. + /// + func configureCell(_ cell: UITableViewCell) + + /// An ImmuTableCell value defining the associated cell type. + /// + /// - Seealso: See ImmuTableCell for possible options. + /// + static var cell: ImmuTableCell { get } + + /// The desired row height (Optional) + /// + /// If not defined or nil, the default height will be used. + /// + static var customHeight: Float? { get } +} + +extension ImmuTableRow { + public var reusableIdentifier: String { + return type(of: self).cell.reusableIdentifier + } + + public var cellClass: UITableViewCell.Type { + return type(of: self).cell.cellClass + } + + public static var customHeight: Float? { + return nil + } +} diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable+Optional.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTable+Optional.swift index 8b10c3e986e9..d60585ecf5b4 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable+Optional.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTable+Optional.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable extension ImmuTableSection { /// Initializes a ImmuTableSection with the given rows (skipping nil ones) diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift index 76440c515419..e8f5d0c19d4a 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift @@ -101,80 +101,6 @@ public struct ImmuTableSection { } } -// MARK: - ImmuTableRow - -/// ImmuTableRow represents the minimum common elements of a row model. -/// -/// You should implement your own types that conform to ImmuTableRow to define your custom rows. -/// -public protocol ImmuTableRow { - - /** - The closure to call when the row is tapped. The row is passed as an argument to the closure. - - To improve readability, we recommend that you implement the action logic in one of - your view controller methods, instead of including the closure inline. - - Also, be mindful of retain cycles. If your closure needs to reference `self` in - any way, make sure to use `[unowned self]` in the parameter list. - - An example row with its action could look like this: - - class ViewController: UITableViewController { - - func buildViewModel() { - let item1Row = NavigationItemRow(title: "Item 1", action: navigationAction()) - ... - } - - func navigationAction() -> ImmuTableRow -> Void { - return { [unowned self] row in - let controller = self.controllerForRow(row) - self.navigationController?.pushViewController(controller, animated: true) - } - } - - ... - - } - - */ - var action: ImmuTableAction? { get } - - /// This method is called when an associated cell needs to be configured. - /// - /// - Precondition: You can assume that the passed cell is of the type defined - /// by cell.cellClass and force downcast accordingly. - /// - func configureCell(_ cell: UITableViewCell) - - /// An ImmuTableCell value defining the associated cell type. - /// - /// - Seealso: See ImmuTableCell for possible options. - /// - static var cell: ImmuTableCell { get } - - /// The desired row height (Optional) - /// - /// If not defined or nil, the default height will be used. - /// - static var customHeight: Float? { get } -} - -extension ImmuTableRow { - public var reusableIdentifier: String { - return type(of: self).cell.reusableIdentifier - } - - public var cellClass: UITableViewCell.Type { - return type(of: self).cell.cellClass - } - - public static var customHeight: Float? { - return nil - } -} - // MARK: - /// ImmuTableViewHandler is a helper to facilitate integration of ImmuTable in your @@ -415,10 +341,6 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel } } -// MARK: - Type aliases - -public typealias ImmuTableAction = (ImmuTableRow) -> Void - // MARK: - Internal testing helpers protocol CellRegistrar { diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTableViewController.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTableViewController.swift index ff99b9cc9e5e..0364b995d9fd 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTableViewController.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecAttachmentViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecAttachmentViewController.swift index 9572bf7f62c9..b13766417565 100644 --- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecAttachmentViewController.swift +++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecAttachmentViewController.swift @@ -1,7 +1,8 @@ +import Aztec import Foundation +import ImmuTable import UIKit import WordPressShared -import Aztec class AztecAttachmentViewController: UITableViewController { diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/LinkSettingsViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/LinkSettingsViewController.swift index 95cd50715be0..cc7ba9220e67 100644 --- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/LinkSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/LinkSettingsViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit struct LinkSettings { diff --git a/WordPress/Classes/ViewRelated/Blog/Site Settings/DateAndTimeFormatSettingsViewController.swift b/WordPress/Classes/ViewRelated/Blog/Site Settings/DateAndTimeFormatSettingsViewController.swift index d254b26056c5..cd64813d4a7b 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Settings/DateAndTimeFormatSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/Site Settings/DateAndTimeFormatSettingsViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared /// This class will display the Blog's date and time settings, and will allow the user to modify them. diff --git a/WordPress/Classes/ViewRelated/Blog/Site Settings/HomepageSettingsViewController.swift b/WordPress/Classes/ViewRelated/Blog/Site Settings/HomepageSettingsViewController.swift index 4e9c6f4265d5..afbbe9fe0249 100644 --- a/WordPress/Classes/ViewRelated/Blog/Site Settings/HomepageSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Blog/Site Settings/HomepageSettingsViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackConnectionViewController.swift b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackConnectionViewController.swift index c58a799b2bed..c7caaef5c1c8 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackConnectionViewController.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackConnectionViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable @objc protocol JetpackConnectionDelegate { diff --git a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSettingsViewController.swift b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSettingsViewController.swift index 0bbd8aa6ed89..a70c14759768 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSettingsViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared /// The purpose of this class is to render and modify the Jetpack Settings associated to a site. diff --git a/WordPress/Classes/ViewRelated/Me/Account Settings/AccountSettingsViewController.swift b/WordPress/Classes/ViewRelated/Me/Account Settings/AccountSettingsViewController.swift index 90c655e99f05..fd6e21dbbf0a 100644 --- a/WordPress/Classes/ViewRelated/Me/Account Settings/AccountSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/Account Settings/AccountSettingsViewController.swift @@ -1,6 +1,7 @@ import Foundation -import UIKit +import ImmuTable import SwiftUI +import UIKit import WordPressShared import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift b/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift index acfa1c52053f..760be7fb6180 100644 --- a/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/Me Main/MeViewController.swift @@ -1,6 +1,7 @@ +import AutomatticAbout +import ImmuTable import UIKit import WordPressShared -import AutomatticAbout class MeViewController: UITableViewController { var handler: ImmuTableViewHandler! diff --git a/WordPress/Classes/ViewRelated/Me/My Profile/MyProfileViewController.swift b/WordPress/Classes/ViewRelated/Me/My Profile/MyProfileViewController.swift index 8c010ce5d3a4..0aa4828de052 100644 --- a/WordPress/Classes/ViewRelated/Me/My Profile/MyProfileViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/My Profile/MyProfileViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Media/MediaItemViewController.swift b/WordPress/Classes/ViewRelated/Media/MediaItemViewController.swift index 59f28acbb564..c745e9f88574 100644 --- a/WordPress/Classes/ViewRelated/Media/MediaItemViewController.swift +++ b/WordPress/Classes/ViewRelated/Media/MediaItemViewController.swift @@ -1,8 +1,9 @@ import AVKit import Combine -import UIKit import Gridicons +import ImmuTable import SVProgressHUD +import UIKit import WordPressShared /// Displays an image preview and metadata for a single Media asset. diff --git a/WordPress/Classes/ViewRelated/Plans/Controllers/PlanListViewController.swift b/WordPress/Classes/ViewRelated/Plans/Controllers/PlanListViewController.swift index f00a8db416fa..ee525b586c37 100644 --- a/WordPress/Classes/ViewRelated/Plans/Controllers/PlanListViewController.swift +++ b/WordPress/Classes/ViewRelated/Plans/Controllers/PlanListViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanDetailViewModel.swift b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanDetailViewModel.swift index 781d607107c4..2584bc06b52c 100644 --- a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanDetailViewModel.swift +++ b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanDetailViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared struct PlanDetailViewModel { diff --git a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListViewModel.swift b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListViewModel.swift index 3213a6fefcd2..bbd15aef83a8 100644 --- a/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListViewModel.swift +++ b/WordPress/Classes/ViewRelated/Plans/ViewModels/PlanListViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared import WordPressUI diff --git a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginDirectoryViewModel.swift b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginDirectoryViewModel.swift index 953f7624419b..051393e9d14e 100644 --- a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginDirectoryViewModel.swift +++ b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginDirectoryViewModel.swift @@ -1,6 +1,7 @@ import Foundation -import WordPressFlux import Gridicons +import ImmuTable +import WordPressFlux protocol PluginListPresenter: AnyObject { func present(site: JetpackSiteRef, query: PluginQuery) diff --git a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListViewModel.swift b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListViewModel.swift index 5622d17f0789..0521e489a4ef 100644 --- a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListViewModel.swift +++ b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginListViewModel.swift @@ -1,3 +1,4 @@ +import ImmuTable import WordPressKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginViewModel.swift b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginViewModel.swift index 0ff49bf63ee5..e5b8dfa62792 100644 --- a/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginViewModel.swift +++ b/WordPress/Classes/ViewRelated/Plugins/ViewModels/PluginViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressFlux class PluginViewModel: Observable { diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift b/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift index 37701dd51fee..a8da2f97bba3 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/Insights Management/InsightsManagementViewController.swift @@ -1,5 +1,6 @@ -import UIKit import Gridicons +import ImmuTable +import UIKit // This exists in addition to `SiteStatsInsightsDelegate` because `[StatSection]` // can't be represented in an Obj-C protocol. diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsTableViewController.swift index 51b910907a46..cfecf0d4f7c7 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift index 701b126895ed..18767835be15 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/HashableImmutableRow.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable protocol HashableImmutableRow: ImmuTableRow, Hashable {} diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodTableViewController.swift index 1393b85e18cc..93edd635aa45 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodTableViewController.swift @@ -1,6 +1,7 @@ +import Combine +import ImmuTable import UIKit import WordPressFlux -import Combine @objc protocol SiteStatsPeriodDelegate { @objc optional func displayWebViewWithURL(_ url: URL) diff --git a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift index 4f84a6d10334..958a7b4cad8e 100644 --- a/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Period Stats/SiteStatsPeriodViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressFlux struct StatsTrafficSection: Hashable { diff --git a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsTableViewController.swift index fe8cf8a1f27e..790b67d48d1c 100644 --- a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit final class ReferrerDetailsTableViewController: UITableViewController { diff --git a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsViewModel.swift index 76e4101ce77e..c4b951407743 100644 --- a/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Referrer Details/ReferrerDetailsViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable protocol ReferrerDetailsViewModelDelegate: AnyObject { func displayWebViewWithURL(_ url: URL) diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift index d377449f4cdf..667c28d92010 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift index 420d761315e0..aa2e0288e85f 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsTableViewController.swift index 42a2b52d9154..3246b4fc16c2 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsTableViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift index 7ec437c37f12..78d6d9800daf 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewController.swift @@ -1,5 +1,6 @@ -import Foundation import Combine +import Foundation +import ImmuTable final class StatsSubscribersViewController: SiteStatsBaseTableViewController { private let viewModel: StatsSubscribersViewModel diff --git a/WordPress/Classes/ViewRelated/Tools/SettingsCommon.swift b/WordPress/Classes/ViewRelated/Tools/SettingsCommon.swift index c4d9045de2d5..5c209f2167fc 100644 --- a/WordPress/Classes/ViewRelated/Tools/SettingsCommon.swift +++ b/WordPress/Classes/ViewRelated/Tools/SettingsCommon.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux import WordPressKit diff --git a/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModel.swift b/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModel.swift index 5b362175a3cf..559509e0c431 100644 --- a/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModel.swift +++ b/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModel.swift @@ -1,3 +1,4 @@ +import ImmuTable import WordPressFlux struct TimeZoneSelectorViewModel: Observable { From 511bdfdd27159f7c872a8e11602767917c556be1 Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 12:34:07 +1100 Subject: [PATCH 4/6] Move `ImmuTable` plus a few dependencies to the `ImmuTable` package --- Modules/Sources/ImmuTable/CellRegistrar.swift | 3 + Modules/Sources/ImmuTable/ImmuTable.swift | 74 ++++++++++++ .../Sources/ImmuTable/ImmuTableSection.swift | 17 +++ .../ImmuTable/UITableView+CellRegistrar.swift | 12 ++ .../Classes/Utility/ImmuTable/ImmuTable.swift | 110 ------------------ .../Activity/ActivityListViewModel.swift | 1 + .../BaseActivityListViewController.swift | 1 + .../BaseRestoreOptionsViewController.swift | 1 + ...ackSpeedUpSiteSettingsViewController.swift | 1 + .../MediaCacheSettingsViewController.swift | 1 + .../PlanDetailViewController.swift | 1 + .../PluginDirectoryViewController.swift | 3 +- .../PluginListViewController.swift | 1 + .../Controllers/PluginViewController.swift | 1 + .../TimeZoneSelectorViewController.swift | 1 + 15 files changed, 117 insertions(+), 111 deletions(-) create mode 100644 Modules/Sources/ImmuTable/CellRegistrar.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTable.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTableSection.swift create mode 100644 Modules/Sources/ImmuTable/UITableView+CellRegistrar.swift diff --git a/Modules/Sources/ImmuTable/CellRegistrar.swift b/Modules/Sources/ImmuTable/CellRegistrar.swift new file mode 100644 index 000000000000..b7e05c9417ba --- /dev/null +++ b/Modules/Sources/ImmuTable/CellRegistrar.swift @@ -0,0 +1,3 @@ +protocol CellRegistrar { + func register(_ cell: ImmuTableCell, cellReuseIdentifier: String) +} diff --git a/Modules/Sources/ImmuTable/ImmuTable.swift b/Modules/Sources/ImmuTable/ImmuTable.swift new file mode 100644 index 000000000000..406038891a6a --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTable.swift @@ -0,0 +1,74 @@ +import UIKit + +/** + ImmuTable represents the view model for a static UITableView. + + ImmuTable consists of zero or more sections, each one containing zero or more rows, + and an optional header and footer text. + + Each row contains the model necessary to configure a specific type of UITableViewCell. + + To use ImmuTable, first you need to create some custom rows. An example row for a cell + that acts as a button which performs a destructive action could look like this: + + struct DestructiveButtonRow: ImmuTableRow { + static let cell = ImmuTableCell.Class(UITableViewCell.self) + let title: String + let action: ImmuTableAction? + + func configureCell(cell: UITableViewCell) { + cell.textLabel?.text = title + cell.textLabel?.textAlignment = .Center + cell.textLabel?.textColor = UIColor.redColor() + } + } + + The easiest way to use ImmuTable is through ImmuTableViewHandler, which takes a + UITableViewController as an argument, and acts as the table view delegate and data + source. You would then assign an ImmuTable object to the handler's `viewModel` + property. + + - attention: before using any ImmuTableRow type, you need to call `registerRows(_:tableView:)` + passing the row type. This is needed so ImmuTable can register the class or nib with the table view. + If you fail to do this, UIKit will raise an exception when it tries to load the row. + */ +public struct ImmuTable { + /// An array of the sections to be represented in the table view + public let sections: [ImmuTableSection] + + /// Initializes an ImmuTable object with the given sections + public init(sections: [ImmuTableSection]) { + self.sections = sections + } + + /// Returns the row model for a specific index path. + /// + /// - Precondition: `indexPath` should represent a valid section and row, otherwise this method + /// will raise an exception. + /// + public func rowAtIndexPath(_ indexPath: IndexPath) -> ImmuTableRow { + return sections[indexPath.section].rows[indexPath.row] + } + + /// Registers the row custom class or nib with the table view so it can later be + /// dequeued with `dequeueReusableCellWithIdentifier(_:forIndexPath:)` + /// + public static func registerRows(_ rows: [ImmuTableRow.Type], tableView: UITableView) { + registerRows(rows, registrator: tableView) + } + + /// This function exists for testing purposes + /// - seealso: registerRows(_:tableView:) + internal static func registerRows(_ rows: [ImmuTableRow.Type], registrator: CellRegistrar) { + let registrables = rows.reduce([:]) { + (classes, row) -> [String: ImmuTableCell] in + + var classes = classes + classes[row.cell.reusableIdentifier] = row.cell + return classes + } + for (identifier, registrable) in registrables { + registrator.register(registrable, cellReuseIdentifier: identifier) + } + } +} diff --git a/Modules/Sources/ImmuTable/ImmuTableSection.swift b/Modules/Sources/ImmuTable/ImmuTableSection.swift new file mode 100644 index 000000000000..bb6e7e0b90cf --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableSection.swift @@ -0,0 +1,17 @@ +/// ImmuTableSection represents the view model for a table view section. +/// +/// A section has an optional header and footer text, and zero or more rows. +/// - seealso: ImmuTableRow +/// +public struct ImmuTableSection { + public let headerText: String? + public let rows: [ImmuTableRow] + public let footerText: String? + + /// Initializes a ImmuTableSection with the given rows and optionally header and footer text + public init(headerText: String? = nil, rows: [ImmuTableRow], footerText: String? = nil) { + self.headerText = headerText + self.rows = rows + self.footerText = footerText + } +} diff --git a/Modules/Sources/ImmuTable/UITableView+CellRegistrar.swift b/Modules/Sources/ImmuTable/UITableView+CellRegistrar.swift new file mode 100644 index 000000000000..9214cf31e8b5 --- /dev/null +++ b/Modules/Sources/ImmuTable/UITableView+CellRegistrar.swift @@ -0,0 +1,12 @@ +import UIKit + +extension UITableView: CellRegistrar { + public func register(_ cell: ImmuTableCell, cellReuseIdentifier: String) { + switch cell { + case .nib(let nib, _): + self.register(nib, forCellReuseIdentifier: cell.reusableIdentifier) + case .class(let cellClass): + self.register(cellClass, forCellReuseIdentifier: cell.reusableIdentifier) + } + } +} diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift index e8f5d0c19d4a..3799904c3ad1 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift @@ -1,79 +1,6 @@ import ImmuTable import UIKit -/** - ImmuTable represents the view model for a static UITableView. - - ImmuTable consists of zero or more sections, each one containing zero or more rows, - and an optional header and footer text. - - Each row contains the model necessary to configure a specific type of UITableViewCell. - - To use ImmuTable, first you need to create some custom rows. An example row for a cell - that acts as a button which performs a destructive action could look like this: - - struct DestructiveButtonRow: ImmuTableRow { - static let cell = ImmuTableCell.Class(UITableViewCell.self) - let title: String - let action: ImmuTableAction? - - func configureCell(cell: UITableViewCell) { - cell.textLabel?.text = title - cell.textLabel?.textAlignment = .Center - cell.textLabel?.textColor = UIColor.redColor() - } - } - - The easiest way to use ImmuTable is through ImmuTableViewHandler, which takes a - UITableViewController as an argument, and acts as the table view delegate and data - source. You would then assign an ImmuTable object to the handler's `viewModel` - property. - - - attention: before using any ImmuTableRow type, you need to call `registerRows(_:tableView:)` - passing the row type. This is needed so ImmuTable can register the class or nib with the table view. - If you fail to do this, UIKit will raise an exception when it tries to load the row. - */ -public struct ImmuTable { - /// An array of the sections to be represented in the table view - public let sections: [ImmuTableSection] - - /// Initializes an ImmuTable object with the given sections - public init(sections: [ImmuTableSection]) { - self.sections = sections - } - - /// Returns the row model for a specific index path. - /// - /// - Precondition: `indexPath` should represent a valid section and row, otherwise this method - /// will raise an exception. - /// - public func rowAtIndexPath(_ indexPath: IndexPath) -> ImmuTableRow { - return sections[indexPath.section].rows[indexPath.row] - } - - /// Registers the row custom class or nib with the table view so it can later be - /// dequeued with `dequeueReusableCellWithIdentifier(_:forIndexPath:)` - /// - public static func registerRows(_ rows: [ImmuTableRow.Type], tableView: UITableView) { - registerRows(rows, registrator: tableView) - } - - /// This function exists for testing purposes - /// - seealso: registerRows(_:tableView:) - internal static func registerRows(_ rows: [ImmuTableRow.Type], registrator: CellRegistrar) { - let registrables = rows.reduce([:]) { - (classes, row) -> [String: ImmuTableCell] in - - var classes = classes - classes[row.cell.reusableIdentifier] = row.cell - return classes - } - for (identifier, registrable) in registrables { - registrator.register(registrable, cellReuseIdentifier: identifier) - } - } -} - extension ImmuTable { /// Alias for an ImmuTable with no sections static var Empty: ImmuTable { @@ -83,26 +10,6 @@ extension ImmuTable { // MARK: - -/// ImmuTableSection represents the view model for a table view section. -/// -/// A section has an optional header and footer text, and zero or more rows. -/// - seealso: ImmuTableRow -/// -public struct ImmuTableSection { - let headerText: String? - let rows: [ImmuTableRow] - let footerText: String? - - /// Initializes a ImmuTableSection with the given rows and optionally header and footer text - public init(headerText: String? = nil, rows: [ImmuTableRow], footerText: String? = nil) { - self.headerText = headerText - self.rows = rows - self.footerText = footerText - } -} - -// MARK: - - /// ImmuTableViewHandler is a helper to facilitate integration of ImmuTable in your /// table view controllers. /// @@ -341,23 +248,6 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel } } -// MARK: - Internal testing helpers - -protocol CellRegistrar { - func register(_ cell: ImmuTableCell, cellReuseIdentifier: String) -} - -extension UITableView: CellRegistrar { - public func register(_ cell: ImmuTableCell, cellReuseIdentifier: String) { - switch cell { - case .nib(let nib, _): - self.register(nib, forCellReuseIdentifier: cell.reusableIdentifier) - case .class(let cellClass): - self.register(cellClass, forCellReuseIdentifier: cell.reusableIdentifier) - } - } -} - // MARK: - UITableViewController conformance @objc public protocol TableViewContainer: AnyObject { diff --git a/WordPress/Classes/ViewRelated/Activity/ActivityListViewModel.swift b/WordPress/Classes/ViewRelated/Activity/ActivityListViewModel.swift index a00bed415f02..8e742322d2cc 100644 --- a/WordPress/Classes/ViewRelated/Activity/ActivityListViewModel.swift +++ b/WordPress/Classes/ViewRelated/Activity/ActivityListViewModel.swift @@ -1,3 +1,4 @@ +import ImmuTable import WordPressFlux protocol ActivityPresenter: AnyObject { diff --git a/WordPress/Classes/ViewRelated/Activity/BaseActivityListViewController.swift b/WordPress/Classes/ViewRelated/Activity/BaseActivityListViewController.swift index 8199b7612001..ab2d53db6b68 100644 --- a/WordPress/Classes/ViewRelated/Activity/BaseActivityListViewController.swift +++ b/WordPress/Classes/ViewRelated/Activity/BaseActivityListViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import SVProgressHUD import WordPressShared import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Restore/Restore Options/BaseRestoreOptionsViewController.swift b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Restore/Restore Options/BaseRestoreOptionsViewController.swift index f46f00dfe171..d6bee5a801c7 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Restore/Restore Options/BaseRestoreOptionsViewController.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Restore/Restore Options/BaseRestoreOptionsViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared typealias HighlightedText = (substring: String, string: String) diff --git a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSpeedUpSiteSettingsViewController.swift b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSpeedUpSiteSettingsViewController.swift index b15d4f330b2d..01667ec9aa5f 100644 --- a/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSpeedUpSiteSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Jetpack/Jetpack Settings/JetpackSpeedUpSiteSettingsViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressShared /// This class will display the Blog's "Speed you site" settings, and will allow the user to modify them. diff --git a/WordPress/Classes/ViewRelated/Me/App Settings/MediaCacheSettingsViewController.swift b/WordPress/Classes/ViewRelated/Me/App Settings/MediaCacheSettingsViewController.swift index b4da4027e9bb..3eb996287996 100644 --- a/WordPress/Classes/ViewRelated/Me/App Settings/MediaCacheSettingsViewController.swift +++ b/WordPress/Classes/ViewRelated/Me/App Settings/MediaCacheSettingsViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable class MediaCacheSettingsViewController: UITableViewController { fileprivate var handler: ImmuTableViewHandler? diff --git a/WordPress/Classes/ViewRelated/Plans/Controllers/PlanDetailViewController.swift b/WordPress/Classes/ViewRelated/Plans/Controllers/PlanDetailViewController.swift index bae3bcd68b48..814b84ca52f8 100644 --- a/WordPress/Classes/ViewRelated/Plans/Controllers/PlanDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Plans/Controllers/PlanDetailViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressShared diff --git a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginDirectoryViewController.swift b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginDirectoryViewController.swift index e63884e7f77f..e108c448cbba 100644 --- a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginDirectoryViewController.swift +++ b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginDirectoryViewController.swift @@ -1,6 +1,7 @@ +import Gridicons +import ImmuTable import UIKit import WordPressFlux -import Gridicons class PluginDirectoryViewController: UITableViewController { diff --git a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginListViewController.swift b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginListViewController.swift index 53e6e25baf4a..7f5556bd097d 100644 --- a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginListViewController.swift +++ b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginListViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginViewController.swift b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginViewController.swift index 0b6db79f9ba0..04b8df9c7344 100644 --- a/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginViewController.swift +++ b/WordPress/Classes/ViewRelated/Plugins/Controllers/PluginViewController.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressFlux class PluginViewController: UITableViewController { diff --git a/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewController.swift b/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewController.swift index 5aea2e6f0226..6a34a271c079 100644 --- a/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewController.swift +++ b/WordPress/Classes/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewController.swift @@ -1,3 +1,4 @@ +import ImmuTable import UIKit import WordPressFlux From 1936b6c2ca15dd93585f7336da1bea6d14b0167b Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 14:05:29 +1100 Subject: [PATCH 5/6] Move the remaining bulk of `ImmuTable` in package It was easier than identifying the access control relationships, change them for the purpose of moving the files, then updating once the move was completed. --- .../ImmuTable/AnyHashableImmuTableRow.swift | 15 ++++ .../Hashable+Xcode16Workaround.swift | 3 + .../Sources/ImmuTable/ImmuTable+Empty.swift | 6 ++ .../ImmuTableDiffableDataSource.swift | 3 + .../ImmuTableDiffableDataSourceSnapshot.swift | 3 + .../ImmuTableDiffableViewHandler.swift | 45 ++++++++++ .../ImmuTable/ImmuTableViewHandler.swift | 90 +------------------ .../ImmuTable/TableViewContainer.swift | 5 ++ ...bleViewController+TableViewContainer.swift | 3 + .../UIViewControllerWithTableView.swift | 3 + .../ImmuTable/ImmuTable+WordPress.swift | 1 + .../SiteStatsBaseTableViewController.swift | 3 +- .../Insights/SiteStatsInsightsViewModel.swift | 1 + .../Post Stats/PostStatsViewModel.swift | 1 + .../SiteStatsDetailsViewModel.swift | 3 +- .../SiteStatsInsightsDetailsViewModel.swift | 1 + .../StatsSubscribersViewModel.swift | 3 +- 17 files changed, 99 insertions(+), 90 deletions(-) create mode 100644 Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift create mode 100644 Modules/Sources/ImmuTable/Hashable+Xcode16Workaround.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTable+Empty.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTableDiffableDataSource.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTableDiffableDataSourceSnapshot.swift create mode 100644 Modules/Sources/ImmuTable/ImmuTableDiffableViewHandler.swift rename WordPress/Classes/Utility/ImmuTable/ImmuTable.swift => Modules/Sources/ImmuTable/ImmuTableViewHandler.swift (74%) create mode 100644 Modules/Sources/ImmuTable/TableViewContainer.swift create mode 100644 Modules/Sources/ImmuTable/UITableViewController+TableViewContainer.swift create mode 100644 Modules/Sources/ImmuTable/UIViewControllerWithTableView.swift diff --git a/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift b/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift new file mode 100644 index 000000000000..f50cddd65646 --- /dev/null +++ b/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift @@ -0,0 +1,15 @@ +public struct AnyHashableImmuTableRow: Hashable { + let immuTableRow: any (ImmuTableRow & Hashable) + + public init(immuTableRow: any (ImmuTableRow & Hashable)) { + self.immuTableRow = immuTableRow + } + + public static func == (lhs: AnyHashableImmuTableRow, rhs: AnyHashableImmuTableRow) -> Bool { + return AnyHashable(lhs.immuTableRow) == AnyHashable(rhs.immuTableRow) + } + + public func hash(into hasher: inout Hasher) { + hasher.combine(AnyHashable(immuTableRow)) + } +} diff --git a/Modules/Sources/ImmuTable/Hashable+Xcode16Workaround.swift b/Modules/Sources/ImmuTable/Hashable+Xcode16Workaround.swift new file mode 100644 index 000000000000..e5b6a36b0dab --- /dev/null +++ b/Modules/Sources/ImmuTable/Hashable+Xcode16Workaround.swift @@ -0,0 +1,3 @@ +// This conformance was added during the Xcode 16 migration to silence the +// dozens of false-positive warnings (any @unchecked conformance is tech debt). +extension AnyHashable: @retroactive @unchecked Sendable {} diff --git a/Modules/Sources/ImmuTable/ImmuTable+Empty.swift b/Modules/Sources/ImmuTable/ImmuTable+Empty.swift new file mode 100644 index 000000000000..86c377a98f7f --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTable+Empty.swift @@ -0,0 +1,6 @@ +public extension ImmuTable { + /// Alias for an ImmuTable with no sections + static var Empty: ImmuTable { + return ImmuTable(sections: []) + } +} diff --git a/Modules/Sources/ImmuTable/ImmuTableDiffableDataSource.swift b/Modules/Sources/ImmuTable/ImmuTableDiffableDataSource.swift new file mode 100644 index 000000000000..9a61ed1f00b6 --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableDiffableDataSource.swift @@ -0,0 +1,3 @@ +import UIKit + +public typealias ImmuTableDiffableDataSource = UITableViewDiffableDataSource diff --git a/Modules/Sources/ImmuTable/ImmuTableDiffableDataSourceSnapshot.swift b/Modules/Sources/ImmuTable/ImmuTableDiffableDataSourceSnapshot.swift new file mode 100644 index 000000000000..71e8bd364763 --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableDiffableDataSourceSnapshot.swift @@ -0,0 +1,3 @@ +import UIKit + +public typealias ImmuTableDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot diff --git a/Modules/Sources/ImmuTable/ImmuTableDiffableViewHandler.swift b/Modules/Sources/ImmuTable/ImmuTableDiffableViewHandler.swift new file mode 100644 index 000000000000..e73301f816c3 --- /dev/null +++ b/Modules/Sources/ImmuTable/ImmuTableDiffableViewHandler.swift @@ -0,0 +1,45 @@ +import UIKit + +public class ImmuTableDiffableViewHandler: ImmuTableViewHandler { + public lazy var diffableDataSource: ImmuTableDiffableDataSource = { + return ImmuTableDiffableDataSource(tableView: target.tableView) { tableView, indexPath, item in + let row = item.immuTableRow + let cell = tableView.dequeueReusableCell(withIdentifier: row.reusableIdentifier, for: indexPath) + row.configureCell(cell) + return cell + } + }() + + public override init(takeOver target: UIViewControllerWithTableView, with passthroughScrollViewDelegate: UIScrollViewDelegate? = nil) { + super.init(takeOver: target, with: passthroughScrollViewDelegate) + + self.target.tableView.dataSource = diffableDataSource + self.automaticallyReloadTableView = false + } + + func item(for indexPath: IndexPath) -> ImmuTableRow? { + guard let diffableDataSource = target.tableView.dataSource as? UITableViewDiffableDataSource else { + return nil + } + + return diffableDataSource.itemIdentifier(for: indexPath)?.immuTableRow + } + + open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + if target.responds(to: #selector(UITableViewDelegate.tableView(_:didSelectRowAt:))) { + target.tableView?(tableView, didSelectRowAt: indexPath) + } else if let item = item(for: indexPath) { + item.action?(item) + } + if automaticallyDeselectCells { + tableView.deselectRow(at: indexPath, animated: true) + } + } + + open override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + if let item = item(for: indexPath), let customHeight = type(of: item).customHeight { + return CGFloat(customHeight) + } + return tableView.rowHeight + } +} diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift b/Modules/Sources/ImmuTable/ImmuTableViewHandler.swift similarity index 74% rename from WordPress/Classes/Utility/ImmuTable/ImmuTable.swift rename to Modules/Sources/ImmuTable/ImmuTableViewHandler.swift index 3799904c3ad1..10b823c1695c 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable.swift +++ b/Modules/Sources/ImmuTable/ImmuTableViewHandler.swift @@ -1,15 +1,5 @@ -import ImmuTable import UIKit -extension ImmuTable { - /// Alias for an ImmuTable with no sections - static var Empty: ImmuTable { - return ImmuTable(sections: []) - } -} - -// MARK: - - /// ImmuTableViewHandler is a helper to facilitate integration of ImmuTable in your /// table view controllers. /// @@ -20,7 +10,6 @@ extension ImmuTable { /// reference to the handler from your view controller. /// open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDelegate { - typealias UIViewControllerWithTableView = TableViewContainer & UITableViewDataSource & UITableViewDelegate & UIViewController @objc unowned let target: UIViewControllerWithTableView private weak var passthroughScrollViewDelegate: UIScrollViewDelegate? @@ -28,7 +17,7 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel /// Initializes the handler with a target table view controller. /// - postcondition: After initialization, it becomse the data source and /// delegate for the the target's table view. - @objc init(takeOver target: UIViewControllerWithTableView, with passthroughScrollViewDelegate: UIScrollViewDelegate? = nil) { + @objc public init(takeOver target: UIViewControllerWithTableView, with passthroughScrollViewDelegate: UIScrollViewDelegate? = nil) { self.target = target self.passthroughScrollViewDelegate = passthroughScrollViewDelegate @@ -48,10 +37,10 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel } /// Configure the handler to automatically deselect any cell after tapping it. - @objc var automaticallyDeselectCells = false + @objc public var automaticallyDeselectCells = false /// Automatically reload table view when view model changes - @objc var automaticallyReloadTableView = true + @objc public var automaticallyReloadTableView = true // MARK: UITableViewDataSource @@ -247,76 +236,3 @@ open class ImmuTableViewHandler: NSObject, UITableViewDataSource, UITableViewDel passthroughScrollViewDelegate?.scrollViewDidChangeAdjustedContentInset?(scrollView) } } - -// MARK: - UITableViewController conformance - -@objc public protocol TableViewContainer: AnyObject { - var tableView: UITableView! { get } -} - -extension UITableViewController: TableViewContainer {} - -// MARK: - Diffable - -// This conformance was added during the Xcode 16 migration to silence the -// dozens of false-positive warnings (any @unchecked conformance is tech debt). -extension AnyHashable: @retroactive @unchecked Sendable {} - -typealias ImmuTableDiffableDataSourceSnapshot = NSDiffableDataSourceSnapshot -typealias ImmuTableDiffableDataSource = UITableViewDiffableDataSource - -struct AnyHashableImmuTableRow: Hashable { - let immuTableRow: any (ImmuTableRow & Hashable) - - static func == (lhs: AnyHashableImmuTableRow, rhs: AnyHashableImmuTableRow) -> Bool { - return AnyHashable(lhs.immuTableRow) == AnyHashable(rhs.immuTableRow) - } - - func hash(into hasher: inout Hasher) { - hasher.combine(AnyHashable(immuTableRow)) - } -} - -class ImmuTableDiffableViewHandler: ImmuTableViewHandler { - lazy var diffableDataSource: ImmuTableDiffableDataSource = { - return ImmuTableDiffableDataSource(tableView: target.tableView) { tableView, indexPath, item in - let row = item.immuTableRow - let cell = tableView.dequeueReusableCell(withIdentifier: row.reusableIdentifier, for: indexPath) - row.configureCell(cell) - return cell - } - }() - - override init(takeOver target: ImmuTableViewHandler.UIViewControllerWithTableView, with passthroughScrollViewDelegate: UIScrollViewDelegate? = nil) { - super.init(takeOver: target, with: passthroughScrollViewDelegate) - - self.target.tableView.dataSource = diffableDataSource - self.automaticallyReloadTableView = false - } - - func item(for indexPath: IndexPath) -> ImmuTableRow? { - guard let diffableDataSource = target.tableView.dataSource as? UITableViewDiffableDataSource else { - return nil - } - - return diffableDataSource.itemIdentifier(for: indexPath)?.immuTableRow - } - - open override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - if target.responds(to: #selector(UITableViewDelegate.tableView(_:didSelectRowAt:))) { - target.tableView?(tableView, didSelectRowAt: indexPath) - } else if let item = item(for: indexPath) { - item.action?(item) - } - if automaticallyDeselectCells { - tableView.deselectRow(at: indexPath, animated: true) - } - } - - open override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - if let item = item(for: indexPath), let customHeight = type(of: item).customHeight { - return CGFloat(customHeight) - } - return tableView.rowHeight - } -} diff --git a/Modules/Sources/ImmuTable/TableViewContainer.swift b/Modules/Sources/ImmuTable/TableViewContainer.swift new file mode 100644 index 000000000000..e393038cbb86 --- /dev/null +++ b/Modules/Sources/ImmuTable/TableViewContainer.swift @@ -0,0 +1,5 @@ +import UIKit + +@objc public protocol TableViewContainer: AnyObject { + var tableView: UITableView! { get } +} diff --git a/Modules/Sources/ImmuTable/UITableViewController+TableViewContainer.swift b/Modules/Sources/ImmuTable/UITableViewController+TableViewContainer.swift new file mode 100644 index 000000000000..eadbc4fe73a4 --- /dev/null +++ b/Modules/Sources/ImmuTable/UITableViewController+TableViewContainer.swift @@ -0,0 +1,3 @@ +import UIKit + +extension UITableViewController: TableViewContainer {} diff --git a/Modules/Sources/ImmuTable/UIViewControllerWithTableView.swift b/Modules/Sources/ImmuTable/UIViewControllerWithTableView.swift new file mode 100644 index 000000000000..e7139061f8a9 --- /dev/null +++ b/Modules/Sources/ImmuTable/UIViewControllerWithTableView.swift @@ -0,0 +1,3 @@ +import UIKit + +public typealias UIViewControllerWithTableView = TableViewContainer & UITableViewDataSource & UITableViewDelegate & UIViewController diff --git a/WordPress/Classes/Utility/ImmuTable/ImmuTable+WordPress.swift b/WordPress/Classes/Utility/ImmuTable/ImmuTable+WordPress.swift index 056a637a43ad..3b150c44920c 100644 --- a/WordPress/Classes/Utility/ImmuTable/ImmuTable+WordPress.swift +++ b/WordPress/Classes/Utility/ImmuTable/ImmuTable+WordPress.swift @@ -1,3 +1,4 @@ +import ImmuTable import WordPressShared /// This lives as an extension on a separate file because it's specific to our UI diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsBaseTableViewController.swift b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsBaseTableViewController.swift index 3bd927749911..2c49c0ddcc41 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsBaseTableViewController.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsBaseTableViewController.swift @@ -1,5 +1,6 @@ -import UIKit import DesignSystem +import ImmuTable +import UIKit /// Base class for site stats table view controllers /// diff --git a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift index 2a150411a04e..be686e0be132 100644 --- a/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Insights/SiteStatsInsightsViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressFlux enum StatsSummaryTimeIntervalDataAsAWeek { diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift index 0f2f53990a91..1135d67b167a 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Post Stats/PostStatsViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import WordPressFlux /// The view model used by PostStatsTableViewController to show diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift index c98da7e7d8d8..980f2a778d6a 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsDetailsViewModel.swift @@ -1,6 +1,7 @@ +import Combine import Foundation +import ImmuTable import WordPressFlux -import Combine /// The view model used by SiteStatsDetailTableViewController to show /// all data for a selected stat. diff --git a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsViewModel.swift index 5a50c435f68a..d2849d395aaa 100644 --- a/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Shared Views/Stats Detail/SiteStatsInsightsDetailsViewModel.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable import UIKit import WordPressFlux diff --git a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift index 5e6405e72558..e03949b5d2ea 100644 --- a/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift +++ b/WordPress/Classes/ViewRelated/Stats/Subscribers/StatsSubscribersViewModel.swift @@ -1,5 +1,6 @@ -import Foundation import Combine +import Foundation +import ImmuTable import WordPressKit final class StatsSubscribersViewModel { From d4441a77af28be9160a31634343d00f8d58b007d Mon Sep 17 00:00:00 2001 From: Gio Lodi Date: Thu, 6 Mar 2025 14:18:15 +1100 Subject: [PATCH 6/6] Update WordPressTest imports and source to build with new ImmuTable I run the extraction building the Jetpack target which clearly misses some of the tests. --- Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift | 2 +- WordPress/WordPressTest/ImmuTableTest.swift | 2 +- WordPress/WordPressTest/ImmuTableTestUtils.swift | 1 + .../Stats/Traffic/SiteStatsPeriodViewModelTests.swift | 5 +++-- .../Tools/Time Zone/TimeZoneSelectorViewModelTests.swift | 3 ++- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift b/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift index f50cddd65646..3718d1008e52 100644 --- a/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift +++ b/Modules/Sources/ImmuTable/AnyHashableImmuTableRow.swift @@ -1,5 +1,5 @@ public struct AnyHashableImmuTableRow: Hashable { - let immuTableRow: any (ImmuTableRow & Hashable) + public let immuTableRow: any (ImmuTableRow & Hashable) public init(immuTableRow: any (ImmuTableRow & Hashable)) { self.immuTableRow = immuTableRow diff --git a/WordPress/WordPressTest/ImmuTableTest.swift b/WordPress/WordPressTest/ImmuTableTest.swift index 2ac188795d30..01ffcc4c9c13 100644 --- a/WordPress/WordPressTest/ImmuTableTest.swift +++ b/WordPress/WordPressTest/ImmuTableTest.swift @@ -1,6 +1,6 @@ // FIXME: Move to ImmuTable package tests -import ImmuTable import XCTest +@testable import ImmuTable @testable import WordPress class ImmuTableTest: XCTestCase { diff --git a/WordPress/WordPressTest/ImmuTableTestUtils.swift b/WordPress/WordPressTest/ImmuTableTestUtils.swift index 0cc19c4821e7..bd4f820c1255 100644 --- a/WordPress/WordPressTest/ImmuTableTestUtils.swift +++ b/WordPress/WordPressTest/ImmuTableTestUtils.swift @@ -1,4 +1,5 @@ import Foundation +import ImmuTable @testable import WordPress class MockImmuTablePresenter: ImmuTablePresenter { diff --git a/WordPress/WordPressTest/ViewRelated/Stats/Traffic/SiteStatsPeriodViewModelTests.swift b/WordPress/WordPressTest/ViewRelated/Stats/Traffic/SiteStatsPeriodViewModelTests.swift index fa5615a37cf5..d31adc6e650d 100644 --- a/WordPress/WordPressTest/ViewRelated/Stats/Traffic/SiteStatsPeriodViewModelTests.swift +++ b/WordPress/WordPressTest/ViewRelated/Stats/Traffic/SiteStatsPeriodViewModelTests.swift @@ -1,6 +1,7 @@ -import XCTest -import WordPressFlux import DGCharts +import ImmuTable +import WordPressFlux +import XCTest @testable import WordPress final class SiteStatsPeriodViewModelTests: XCTestCase { diff --git a/WordPress/WordPressTest/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModelTests.swift b/WordPress/WordPressTest/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModelTests.swift index 4acec6c9f726..4bacc807da53 100644 --- a/WordPress/WordPressTest/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModelTests.swift +++ b/WordPress/WordPressTest/ViewRelated/Tools/Time Zone/TimeZoneSelectorViewModelTests.swift @@ -1,6 +1,7 @@ import Foundation -import XCTest +import ImmuTable import WordPressKit +import XCTest @testable import WordPress