summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--ios/MullvadVPN.xcodeproj/project.pbxproj54
-rw-r--r--ios/MullvadVPN/CellFactoryProtocol.swift20
-rw-r--r--ios/MullvadVPN/SettingsCellFactory.swift113
-rw-r--r--ios/MullvadVPN/SettingsDataSource.swift212
-rw-r--r--ios/MullvadVPN/SettingsViewController.swift11
5 files changed, 225 insertions, 185 deletions
diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj
index 428c2fe55d..f6755cfec3 100644
--- a/ios/MullvadVPN.xcodeproj/project.pbxproj
+++ b/ios/MullvadVPN.xcodeproj/project.pbxproj
@@ -340,6 +340,8 @@
58FEEB46260A028D00A621A8 /* GeoJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB45260A028D00A621A8 /* GeoJSON.swift */; };
58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */; };
58FF2C03281BDE02009EF542 /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FF2C02281BDE02009EF542 /* SettingsManager.swift */; };
+ 7AD8490D29BA1EC500878E53 /* SettingsCellFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD8490C29BA1EC500878E53 /* SettingsCellFactory.swift */; };
+ 7AD8490F29BA26B000878E53 /* CellFactoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AD8490E29BA26B000878E53 /* CellFactoryProtocol.swift */; };
E1187ABC289BBB850024E748 /* OutOfTimeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */; };
E1187ABD289BBB850024E748 /* OutOfTimeContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */; };
E158B360285381C60002F069 /* StringFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = E158B35F285381C60002F069 /* StringFormatter.swift */; };
@@ -903,6 +905,8 @@
58FEEB45260A028D00A621A8 /* GeoJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeoJSON.swift; sourceTree = "<group>"; };
58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomaticKeyboardResponder.swift; sourceTree = "<group>"; };
58FF2C02281BDE02009EF542 /* SettingsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = "<group>"; };
+ 7AD8490C29BA1EC500878E53 /* SettingsCellFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsCellFactory.swift; sourceTree = "<group>"; };
+ 7AD8490E29BA26B000878E53 /* CellFactoryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CellFactoryProtocol.swift; sourceTree = "<group>"; };
E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeViewController.swift; sourceTree = "<group>"; };
E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OutOfTimeContentView.swift; sourceTree = "<group>"; };
E158B35F285381C60002F069 /* StringFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringFormatter.swift; sourceTree = "<group>"; };
@@ -1374,47 +1378,39 @@
58CE5E62224146200008646E /* MullvadVPN */ = {
isa = PBXGroup;
children = (
- 06AC114028F841390037AF9A /* AddressCacheTracker.swift */,
5896CEF126972DEB00B0FAE8 /* AccountContentView.swift */,
587988C628A2A01F00E3DF54 /* AccountDataThrottling.swift */,
- 58138E60294871C600684F0C /* DeviceDataThrottling.swift */,
58C3A4B122456F1A00340BDB /* AccountInputGroupView.swift */,
+ 5878A27029091CF20096FC88 /* AccountInteractor.swift */,
58CCA01D2242787B004F3011 /* AccountTextField.swift */,
582AE30F2440A6CA00E6733A /* AccountTokenInput.swift */,
- 5878A27029091CF20096FC88 /* AccountInteractor.swift */,
58CCA01722426713004F3011 /* AccountViewController.swift */,
- 5867771329097BCD006F721F /* PaymentState.swift */,
- 5867771529097C5B006F721F /* ProductState.swift */,
+ 06AC114028F841390037AF9A /* AddressCacheTracker.swift */,
5868585424054096000B8131 /* AppButton.swift */,
58CE5E63224146200008646E /* AppDelegate.swift */,
- 5846226F26E229CD0035F7C2 /* StorePaymentManager */,
58CE5E6A224146210008646E /* Assets.xcassets */,
58FEEB57260B662E00A621A8 /* AutomaticKeyboardResponder.swift */,
5891BF1B25E3E3EB006D6FB0 /* Bundle+ProductVersion.swift */,
+ 7AD8490E29BA26B000878E53 /* CellFactoryProtocol.swift */,
587EB669270EFACB00123C75 /* CharacterSet+IPAddress.swift */,
58E511E528DDDEAC00B0BCDE /* CodingErrors+CustomErrorDescription.swift */,
- 58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */,
- 58C3F4F82964B08300D72515 /* MapViewController.swift */,
58A1AA8B23F5584B009F7EA6 /* ConnectionPanelView.swift */,
- 58CCA00F224249A1004F3011 /* TunnelViewController.swift */,
- 5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */,
- 5878A278290954790096FC88 /* TunnelViewControllerInteractor.swift */,
5871FB95254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift */,
5896AE83246D5889005B36CB /* CustomDateComponentsFormatting.swift */,
582BB1B0229569620055B6EF /* CustomNavigationBar.swift */,
+ 5878A27A2909649A0096FC88 /* CustomOverlayRenderer.swift */,
5868BD32261DCD2600E6027F /* CustomSplitViewController.swift */,
58ACF64C26567A4F00ACE4B7 /* CustomSwitch.swift */,
58ACF64E26567A7100ACE4B7 /* CustomSwitchContainer.swift */,
58293FB025124117005D0BB5 /* CustomTextField.swift */,
58293FB2251241B3005D0BB5 /* CustomTextView.swift */,
587EB66F27143B6500123C75 /* DataSourceSnapshot.swift */,
+ 58138E60294871C600684F0C /* DeviceDataThrottling.swift */,
587D96732886D87C00CD8F1C /* DeviceManagementContentView.swift */,
5820EDA8288FE064006BF4E4 /* DeviceManagementInteractor.swift */,
5893716928817A45004EE76C /* DeviceManagementViewController.swift */,
5820EDAA288FF0D2006BF4E4 /* DeviceRowView.swift */,
58907D9424D17B4E00CFC3F5 /* DisconnectSplitButton.swift */,
- 58B9EB142489139B00095626 /* RESTError+Display.swift */,
- 58A8EE5D2976DB00009C0F8D /* StorePaymentManagerError+Display.swift */,
580F8B8528197958002E0998 /* DNSSettings.swift */,
5892A45D265FABFF00890742 /* EmptyTableViewHeaderFooterView.swift */,
58FEEB45260A028D00A621A8 /* GeoJSON.swift */,
@@ -1427,6 +1423,8 @@
583DA21325FA4B5C00318683 /* LocationDataSource.swift */,
58B993B02608A34500BA7811 /* LoginContentView.swift */,
58CE5E65224146200008646E /* LoginViewController.swift */,
+ 58C3F4F82964B08300D72515 /* MapViewController.swift */,
+ 58ACA9EC2979569500B5825C /* ModalRootAdaptivePresentationDelegate.swift */,
5866F39B2243B82D00168AE5 /* MullvadVPN.entitlements */,
58906DDF2445C7A5002F0673 /* NEProviderStopReason+Debug.swift */,
5811DE4F239014550011EB53 /* NEVPNStatus+Debug.swift */,
@@ -1437,22 +1435,25 @@
58CC40EE24A601900019D96E /* ObserverList.swift */,
586A950B2901250A007BAF2B /* Operations */,
E1187ABB289BBB850024E748 /* OutOfTimeContentView.swift */,
- E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */,
5867770D29096984006F721F /* OutOfTimeInteractor.swift */,
+ E1187ABA289BBB850024E748 /* OutOfTimeViewController.swift */,
+ 5867771329097BCD006F721F /* PaymentState.swift */,
584D26C3270C855A004EA533 /* PreferencesDataSource.swift */,
587EB6732714520600123C75 /* PreferencesDataSourceDelegate.swift */,
- 58ACF6482655365700ACE4B7 /* PreferencesViewController.swift */,
5871167E2910035700D41AAC /* PreferencesInteractor.swift */,
+ 58ACF6482655365700ACE4B7 /* PreferencesViewController.swift */,
587EB671271451E300123C75 /* PreferencesViewModel.swift */,
+ 5878A26E2907E7E00096FC88 /* ProblemReportInteractor.swift */,
58F8AC0D25D3F8CE002BE0ED /* ProblemReportReviewViewController.swift */,
58EF580A25D69D7A00AEBA94 /* ProblemReportSubmissionOverlayView.swift */,
58293FAC2510CA58005D0BB5 /* ProblemReportViewController.swift */,
- 5878A26E2907E7E00096FC88 /* ProblemReportInteractor.swift */,
+ 5867771529097C5B006F721F /* ProductState.swift */,
585DA87526B0249A00B8C587 /* RelayCacheTracker */,
06FAE67828F83CA50033DD93 /* RESTCreateApplePaymentResponse+Localization.swift */,
+ 58B9EB142489139B00095626 /* RESTError+Display.swift */,
58F1311427E0B2AB007AC5BC /* Result+Extensions.swift */,
- 580909D22876D09A0078138D /* RevokedDeviceViewController.swift */,
5878A27C2909657C0096FC88 /* RevokedDeviceInteractor.swift */,
+ 580909D22876D09A0078138D /* RevokedDeviceViewController.swift */,
587425C02299833500CA2045 /* RootContainerViewController.swift */,
58E25F802837BBBB002CFB2C /* SceneDelegate.swift */,
5888AD82227B11080051EB06 /* SelectLocationCell.swift */,
@@ -1462,22 +1463,27 @@
582BB1B2229574F40055B6EF /* SettingsAccountCell.swift */,
5819C2162729595500D6EC38 /* SettingsAddDNSEntryCell.swift */,
582BB1AE229566420055B6EF /* SettingsCell.swift */,
+ 7AD8490C29BA1EC500878E53 /* SettingsCellFactory.swift */,
58EE2E38272FF814003BFF93 /* SettingsDataSource.swift */,
58EE2E39272FF814003BFF93 /* SettingsDataSourceDelegate.swift */,
584D26C5270C8741004EA533 /* SettingsDNSTextCell.swift */,
+ 58677711290976FB006F721F /* SettingsInteractor.swift */,
+ 5867770F290975E8006F721F /* SettingsInteractorFactory.swift */,
580F8B88281A79A7002E0998 /* SettingsManager */,
+ 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */,
58E6771E24ADFE7800AA26E7 /* SettingsNavigationController.swift */,
- 5867770F290975E8006F721F /* SettingsInteractorFactory.swift */,
584D26C1270C8542004EA533 /* SettingsStaticTextFooterView.swift */,
58ACF64A26553C3F00ACE4B7 /* SettingsSwitchCell.swift */,
58CCA01122424D11004F3011 /* SettingsViewController.swift */,
- 58677711290976FB006F721F /* SettingsInteractor.swift */,
58BA693023EADA6A009DC256 /* SimulatorTunnelProvider.swift */,
587A01FB23F1F0BE00B68763 /* SimulatorTunnelProviderHost.swift */,
+ 58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */,
58FD5BEF24238EB300112C88 /* SKProduct+Formatting.swift */,
58F19E34228C15BA00C7710B /* SpinnerActivityIndicatorView.swift */,
E1FD0DF428AA7CE400299DB4 /* StatusActivityView.swift */,
58EF581025D69DB400AEBA94 /* StatusImageView.swift */,
+ 5846226F26E229CD0035F7C2 /* StorePaymentManager */,
+ 58A8EE5D2976DB00009C0F8D /* StorePaymentManagerError+Display.swift */,
5807E2BF2432038B00F5FF30 /* String+Split.swift */,
E158B35F285381C60002F069 /* StringFormatter.swift */,
589A454B28DDF5E100565204 /* Swizzle.swift */,
@@ -1486,16 +1492,16 @@
58A99ED2240014A0006599E9 /* TermsOfServiceViewController.swift */,
5862805322428EF100F5A6E1 /* TranslucentButtonBlurView.swift */,
589E63DA28F7E9E7005FAB05 /* TransportMonitor */,
+ 58B43C1825F77DB60002C8C3 /* TunnelControlView.swift */,
5823FA5726CE4A4100283BF8 /* TunnelManager */,
+ 58CCA00F224249A1004F3011 /* TunnelViewController.swift */,
+ 5878A278290954790096FC88 /* TunnelViewControllerInteractor.swift */,
5891BF5025E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift */,
587CBFE222807F530028DED3 /* UIColor+Helpers.swift */,
58CCA0152242560B004F3011 /* UIColor+Palette.swift */,
585CA70E25F8C44600B47C62 /* UIMetrics.swift */,
- 58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */,
- 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */,
- 58A8EE592976BFBB009C0F8D /* SKError+Localized.swift */,
- 58ACA9EC2979569500B5825C /* ModalRootAdaptivePresentationDelegate.swift */,
584555F32991176200DD0657 /* UIPresentationController+Private.swift */,
+ 58F7CA872692E34000FC59FD /* WireguardKeysContentView.swift */,
);
path = MullvadVPN;
sourceTree = "<group>";
@@ -2281,6 +2287,7 @@
buildActionMask = 2147483647;
files = (
58BFA5CC22A7CE1F00A6173D /* ApplicationConfiguration.swift in Sources */,
+ 7AD8490D29BA1EC500878E53 /* SettingsCellFactory.swift in Sources */,
5891BF5125E66B1E006D6FB0 /* UIBarButtonItem+KeyboardNavigation.swift in Sources */,
58E511E628DDDEAC00B0BCDE /* CodingErrors+CustomErrorDescription.swift in Sources */,
587B75412668FD7800DEF7E9 /* AccountExpirySystemNotificationProvider.swift in Sources */,
@@ -2404,6 +2411,7 @@
58E0A98827C8F46300FE6BDD /* Tunnel.swift in Sources */,
58ACF64F26567A7100ACE4B7 /* CustomSwitchContainer.swift in Sources */,
5857F24324C8662600CF6F47 /* SelectLocationHeaderView.swift in Sources */,
+ 7AD8490F29BA26B000878E53 /* CellFactoryProtocol.swift in Sources */,
58EE2E3A272FF814003BFF93 /* SettingsDataSource.swift in Sources */,
58B26E1E2943514300D5980C /* InAppNotificationDescriptor.swift in Sources */,
58421032282E42B000F24E46 /* UpdateDeviceDataOperation.swift in Sources */,
diff --git a/ios/MullvadVPN/CellFactoryProtocol.swift b/ios/MullvadVPN/CellFactoryProtocol.swift
new file mode 100644
index 0000000000..7bf3cbda21
--- /dev/null
+++ b/ios/MullvadVPN/CellFactoryProtocol.swift
@@ -0,0 +1,20 @@
+//
+// CellFactoryProtocol.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2023-03-09.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import UIKit
+
+/// Protocol for creating factories to make ``UITableViewCell``s of various kinds.
+/// Typically used in conjunction with a ``UITableViewDiffableDataSource.CellProvider``.
+protocol CellFactoryProtocol {
+ associatedtype ItemIdentifier
+
+ var tableView: UITableView { get }
+
+ func makeCell(for item: ItemIdentifier, indexPath: IndexPath) -> UITableViewCell
+ func configureCell(_ cell: UITableViewCell, item: ItemIdentifier, indexPath: IndexPath)
+}
diff --git a/ios/MullvadVPN/SettingsCellFactory.swift b/ios/MullvadVPN/SettingsCellFactory.swift
new file mode 100644
index 0000000000..2420004921
--- /dev/null
+++ b/ios/MullvadVPN/SettingsCellFactory.swift
@@ -0,0 +1,113 @@
+//
+// SettingsCellFactory.swift
+// MullvadVPN
+//
+// Created by Jon Petersson on 2023-03-09.
+// Copyright © 2023 Mullvad VPN AB. All rights reserved.
+//
+
+import UIKit
+
+struct SettingsCellFactory: CellFactoryProtocol {
+ let tableView: UITableView
+ private let interactor: SettingsInteractor
+
+ init(tableView: UITableView, interactor: SettingsInteractor) {
+ self.tableView = tableView
+ self.interactor = interactor
+ }
+
+ func makeCell(for item: SettingsDataSource.Item, indexPath: IndexPath) -> UITableViewCell {
+ let cell: UITableViewCell
+
+ switch item {
+ case .account:
+ cell = tableView.dequeueReusableCell(
+ withIdentifier: SettingsDataSource.CellReuseIdentifiers.accountCell.rawValue,
+ for: indexPath
+ )
+ default:
+ cell = tableView.dequeueReusableCell(
+ withIdentifier: SettingsDataSource.CellReuseIdentifiers.basicCell.rawValue,
+ for: indexPath
+ )
+ }
+
+ configureCell(cell, item: item, indexPath: indexPath)
+
+ return cell
+ }
+
+ func configureCell(
+ _ cell: UITableViewCell,
+ item: SettingsDataSource.Item,
+ indexPath: IndexPath
+ ) {
+ switch item {
+ case .account:
+ guard let cell = cell as? SettingsAccountCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "ACCOUNT_CELL_LABEL",
+ tableName: "Settings",
+ value: "Account",
+ comment: ""
+ )
+ cell.accountExpiryDate = interactor.deviceState.accountData?.expiry
+ cell.accessibilityIdentifier = "AccountCell"
+ cell.disclosureType = .chevron
+
+ case .preferences:
+ guard let cell = cell as? SettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "PREFERENCES_CELL_LABEL",
+ tableName: "Settings",
+ value: "Preferences",
+ comment: ""
+ )
+ cell.detailTitleLabel.text = nil
+ cell.accessibilityIdentifier = "PreferencesCell"
+ cell.disclosureType = .chevron
+
+ case .version:
+ guard let cell = cell as? SettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "APP_VERSION_CELL_LABEL",
+ tableName: "Settings",
+ value: "App version",
+ comment: ""
+ )
+ cell.detailTitleLabel.text = Bundle.main.productVersion
+ cell.accessibilityIdentifier = nil
+ cell.disclosureType = .none
+
+ case .problemReport:
+ guard let cell = cell as? SettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "REPORT_PROBLEM_CELL_LABEL",
+ tableName: "Settings",
+ value: "Report a problem",
+ comment: ""
+ )
+ cell.detailTitleLabel.text = nil
+ cell.accessibilityIdentifier = nil
+ cell.disclosureType = .chevron
+
+ case .faq:
+ guard let cell = cell as? SettingsCell else { return }
+
+ cell.titleLabel.text = NSLocalizedString(
+ "FAQ_AND_GUIDES_CELL_LABEL",
+ tableName: "Settings",
+ value: "FAQ & Guides",
+ comment: ""
+ )
+ cell.detailTitleLabel.text = nil
+ cell.accessibilityIdentifier = nil
+ cell.disclosureType = .externalLink
+ }
+ }
+}
diff --git a/ios/MullvadVPN/SettingsDataSource.swift b/ios/MullvadVPN/SettingsDataSource.swift
index 15027f8ae4..96466cf6f2 100644
--- a/ios/MullvadVPN/SettingsDataSource.swift
+++ b/ios/MullvadVPN/SettingsDataSource.swift
@@ -8,8 +8,11 @@
import UIKit
-final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {
- private enum CellReuseIdentifiers: String, CaseIterable {
+final class SettingsDataSource: UITableViewDiffableDataSource<
+ SettingsDataSource.Section,
+ SettingsDataSource.Item
+>, UITableViewDelegate {
+ enum CellReuseIdentifiers: String, CaseIterable {
case accountCell
case basicCell
@@ -48,172 +51,38 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele
case faq
}
- private var snapshot = DataSourceSnapshot<Section, Item>()
private let interactor: SettingsInteractor
+ private let settingsCellFactory: SettingsCellFactory
private var storedAccountData: StoredAccountData?
+ private weak var tableView: UITableView?
weak var delegate: SettingsDataSourceDelegate?
- weak var tableView: UITableView? {
- didSet {
- tableView?.delegate = self
- tableView?.dataSource = self
-
- registerClasses()
- }
- }
-
- init(interactor: SettingsInteractor) {
+ init(tableView: UITableView, interactor: SettingsInteractor) {
+ self.tableView = tableView
self.interactor = interactor
- super.init()
+ let settingsCellFactory = SettingsCellFactory(tableView: tableView, interactor: interactor)
+ self.settingsCellFactory = settingsCellFactory
- interactor.didUpdateDeviceState = { [weak self] deviceState in
- self?.didUpdateDeviceState(deviceState)
+ super.init(tableView: tableView) { tableView, indexPath, itemIdentifier in
+ settingsCellFactory.makeCell(for: itemIdentifier, indexPath: indexPath)
}
- storedAccountData = interactor.deviceState.accountData
+ tableView.delegate = self
+ registerClasses()
updateDataSnapshot()
- }
-
- private func registerClasses() {
- CellReuseIdentifiers.allCases.forEach { cellIdentifier in
- tableView?.register(
- cellIdentifier.reusableViewClass,
- forCellReuseIdentifier: cellIdentifier.rawValue
- )
- }
-
- HeaderFooterReuseIdentifier.allCases.forEach { reuseIdentifier in
- tableView?.register(
- reuseIdentifier.reusableViewClass,
- forHeaderFooterViewReuseIdentifier: reuseIdentifier.rawValue
- )
- }
- }
-
- private func updateDataSnapshot() {
- var newSnapshot = DataSourceSnapshot<Section, Item>()
-
- if interactor.deviceState.isLoggedIn {
- newSnapshot.appendSections([.main])
- newSnapshot.appendItems([.account, .preferences], in: .main)
- }
-
- newSnapshot.appendSections([.version, .problemReport])
- newSnapshot.appendItems([.version], in: .version)
- newSnapshot.appendItems([.problemReport, .faq], in: .problemReport)
-
- snapshot = newSnapshot
- }
-
- // MARK: - UITableViewDataSource
-
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
- let sectionIdentifier = snapshot.section(at: section)!
-
- return snapshot.numberOfItems(in: sectionIdentifier) ?? 0
- }
-
- func numberOfSections(in tableView: UITableView) -> Int {
- return snapshot.numberOfSections()
- }
-
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
- let item = snapshot.itemForIndexPath(indexPath)!
-
- switch item {
- case .account:
- let cell = tableView.dequeueReusableCell(
- withIdentifier: CellReuseIdentifiers.accountCell.rawValue,
- for: indexPath
- ) as! SettingsAccountCell
- cell.titleLabel.text = NSLocalizedString(
- "ACCOUNT_CELL_LABEL",
- tableName: "Settings",
- value: "Account",
- comment: ""
- )
- cell.accountExpiryDate = interactor.deviceState.accountData?.expiry
- cell.accessibilityIdentifier = "AccountCell"
- cell.disclosureType = .chevron
- return cell
-
- case .preferences:
- let cell = tableView.dequeueReusableCell(
- withIdentifier: CellReuseIdentifiers.basicCell.rawValue,
- for: indexPath
- ) as! SettingsCell
- cell.titleLabel.text = NSLocalizedString(
- "PREFERENCES_CELL_LABEL",
- tableName: "Settings",
- value: "Preferences",
- comment: ""
- )
- cell.detailTitleLabel.text = nil
- cell.accessibilityIdentifier = "PreferencesCell"
- cell.disclosureType = .chevron
-
- return cell
-
- case .version:
- let cell = tableView.dequeueReusableCell(
- withIdentifier: CellReuseIdentifiers.basicCell.rawValue,
- for: indexPath
- ) as! SettingsCell
- cell.titleLabel.text = NSLocalizedString(
- "APP_VERSION_CELL_LABEL",
- tableName: "Settings",
- value: "App version",
- comment: ""
- )
- cell.detailTitleLabel.text = Bundle.main.productVersion
- cell.accessibilityIdentifier = nil
- cell.disclosureType = .none
-
- return cell
-
- case .problemReport:
- let cell = tableView.dequeueReusableCell(
- withIdentifier: CellReuseIdentifiers.basicCell.rawValue,
- for: indexPath
- ) as! SettingsCell
- cell.titleLabel.text = NSLocalizedString(
- "REPORT_PROBLEM_CELL_LABEL",
- tableName: "Settings",
- value: "Report a problem",
- comment: ""
- )
- cell.detailTitleLabel.text = nil
- cell.accessibilityIdentifier = nil
- cell.disclosureType = .chevron
-
- return cell
-
- case .faq:
- let cell = tableView.dequeueReusableCell(
- withIdentifier: CellReuseIdentifiers.basicCell.rawValue,
- for: indexPath
- ) as! SettingsCell
- cell.titleLabel.text = NSLocalizedString(
- "FAQ_AND_GUIDES_CELL_LABEL",
- tableName: "Settings",
- value: "FAQ & Guides",
- comment: ""
- )
- cell.detailTitleLabel.text = nil
- cell.accessibilityIdentifier = nil
- cell.disclosureType = .externalLink
-
- return cell
+ interactor.didUpdateDeviceState = { [weak self] deviceState in
+ self?.didUpdateDeviceState(deviceState)
}
+ storedAccountData = interactor.deviceState.accountData
}
// MARK: - UITableViewDelegate
func tableView(_ tableView: UITableView, shouldHighlightRowAt indexPath: IndexPath) -> Bool {
- if case .version = snapshot.itemForIndexPath(indexPath) {
+ if case .version = itemIdentifier(for: indexPath) {
return false
} else {
return true
@@ -221,7 +90,7 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
- guard let item = snapshot.itemForIndexPath(indexPath) else { return }
+ guard let item = itemIdentifier(for: indexPath) else { return }
delegate?.settingsDataSource(self, didSelectItem: item)
}
@@ -246,6 +115,37 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele
// MARK: - Private
+ private func registerClasses() {
+ CellReuseIdentifiers.allCases.forEach { cellIdentifier in
+ tableView?.register(
+ cellIdentifier.reusableViewClass,
+ forCellReuseIdentifier: cellIdentifier.rawValue
+ )
+ }
+
+ HeaderFooterReuseIdentifier.allCases.forEach { reuseIdentifier in
+ tableView?.register(
+ reuseIdentifier.reusableViewClass,
+ forHeaderFooterViewReuseIdentifier: reuseIdentifier.rawValue
+ )
+ }
+ }
+
+ private func updateDataSnapshot() {
+ var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
+
+ if interactor.deviceState.isLoggedIn {
+ snapshot.appendSections([.main])
+ snapshot.appendItems([.account, .preferences], toSection: .main)
+ }
+
+ snapshot.appendSections([.version, .problemReport])
+ snapshot.appendItems([.version], toSection: .version)
+ snapshot.appendItems([.problemReport, .faq], toSection: .problemReport)
+
+ apply(snapshot)
+ }
+
private func didUpdateDeviceState(_ deviceState: DeviceState) {
let newAccountData = deviceState.accountData
let oldAccountData = storedAccountData
@@ -257,15 +157,15 @@ final class SettingsDataSource: NSObject, UITableViewDataSource, UITableViewDele
oldAccountData.number == newAccountData.number,
oldAccountData.expiry != newAccountData.expiry
{
- tableView?.performBatchUpdates {
- if let indexPath = snapshot.indexPathForItem(.account) {
- tableView?.reloadRows(at: [indexPath], with: .none)
- }
+ if let indexPath = indexPath(for: .account),
+ let cell = tableView?.cellForRow(at: indexPath)
+ {
+ settingsCellFactory.configureCell(cell, item: .account, indexPath: indexPath)
}
+
return
}
updateDataSnapshot()
- tableView?.reloadData()
}
}
diff --git a/ios/MullvadVPN/SettingsViewController.swift b/ios/MullvadVPN/SettingsViewController.swift
index 2dd09d4d20..e43c960b56 100644
--- a/ios/MullvadVPN/SettingsViewController.swift
+++ b/ios/MullvadVPN/SettingsViewController.swift
@@ -18,16 +18,15 @@ class SettingsViewController: UITableViewController, SettingsDataSourceDelegate,
SFSafariViewControllerDelegate
{
weak var delegate: SettingsViewControllerDelegate?
+ private var dataSource: SettingsDataSource?
+ private let interactor: SettingsInteractor
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
- private let dataSource: SettingsDataSource
-
init(interactor: SettingsInteractor) {
- dataSource = SettingsDataSource(interactor: interactor)
-
+ self.interactor = interactor
super.init(style: .grouped)
}
@@ -55,8 +54,8 @@ class SettingsViewController: UITableViewController, SettingsDataSourceDelegate,
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 60
- dataSource.tableView = tableView
- dataSource.delegate = self
+ dataSource = SettingsDataSource(tableView: tableView, interactor: interactor)
+ dataSource?.delegate = self
}
// MARK: - IBActions