diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 0f492fe39d..14bbc72c20 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -739,6 +739,7 @@ CBD4F140279EBFB300B20FD7 /* SwiftUICollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1AEFB02799AA940031AE3D /* SwiftUICollectionViewCell.swift */; }; CBDD5DDF29A6736A00832877 /* APIHeadersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DDE29A6736A00832877 /* APIHeadersTests.swift */; }; CBDD5DE129A6741300832877 /* MockBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBDD5DE029A6741300832877 /* MockBundle.swift */; }; + CBFCB30E2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */; }; D63657192A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */; }; EA39B7E2268A1A35000C62CD /* privacy-reference-tests in Resources */ = {isa = PBXBuildFile; fileRef = EA39B7E1268A1A35000C62CD /* privacy-reference-tests */; }; EAB19EDA268963510015D3EA /* DomainMatchingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */; }; @@ -2307,6 +2308,7 @@ CBF14FC227970072001D94D0 /* HomeMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageView.swift; sourceTree = ""; }; CBF14FC427970AB0001D94D0 /* HomeMessageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageViewModel.swift; sourceTree = ""; }; CBF14FC627970C8A001D94D0 /* HomeMessageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeMessageCollectionViewCell.swift; sourceTree = ""; }; + CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationURLDebugViewController.swift; sourceTree = ""; }; D63657182A7BAE7C001AF19D /* EmailManagerRequestDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmailManagerRequestDelegate.swift; sourceTree = ""; }; EA39B7E1268A1A35000C62CD /* privacy-reference-tests */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "privacy-reference-tests"; path = "submodules/privacy-reference-tests"; sourceTree = SOURCE_ROOT; }; EAB19ED9268963510015D3EA /* DomainMatchingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DomainMatchingTests.swift; sourceTree = ""; }; @@ -3741,6 +3743,7 @@ F46FEC5627987A5F0061D9DF /* KeychainItemsDebugViewController.swift */, 983D71B02A286E810072E26D /* SyncDebugViewController.swift */, EE72CA842A862D000043B5B3 /* NetworkProtectionDebugViewController.swift */, + CBFCB30D2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift */, ); name = Debug; sourceTree = ""; @@ -6258,6 +6261,7 @@ 85582E0029D7409700E9AE35 /* SyncSettingsViewController+PDFRendering.swift in Sources */, EE0153EF2A70021E002A8B26 /* NetworkProtectionInviteView.swift in Sources */, 9888F77B2224980500C46159 /* FeedbackViewController.swift in Sources */, + CBFCB30E2B2CD47800253E9E /* ConfigurationURLDebugViewController.swift in Sources */, 982686AD2600C0850011A8D6 /* ActionMessageView.swift in Sources */, F446B9B5251150AC00324016 /* HomeMessageViewSectionRenderer.swift in Sources */, 98D98A8225ED88E300D8E3DF /* BrowsingMenuSeparatorViewCell.swift in Sources */, diff --git a/DuckDuckGo/ConfigurationDebugViewController.swift b/DuckDuckGo/ConfigurationDebugViewController.swift index fdb2a73840..70ac6aa424 100644 --- a/DuckDuckGo/ConfigurationDebugViewController.swift +++ b/DuckDuckGo/ConfigurationDebugViewController.swift @@ -20,12 +20,10 @@ import UIKit import BackgroundTasks import Core -import Configuration class ConfigurationDebugViewController: UITableViewController { private let titles = [ - Sections.customURLs: "Custom URLs", Sections.refreshInformation: "Background Refresh Info", Sections.queuedTasks: "Queued Tasks (Earliest Execution Date)", Sections.etags: "ETags" @@ -33,25 +31,12 @@ class ConfigurationDebugViewController: UITableViewController { enum Sections: Int, CaseIterable { - case customURLs case refreshInformation case queuedTasks case etags } - enum CustomURLsRows: Int, CaseIterable { - - case privacyConfigURL - - var title: String { - switch self { - case .privacyConfigURL: return "Privacy Config" - } - } - - } - enum RefreshInformationRows: Int, CaseIterable { case lastRefreshDate @@ -77,18 +62,6 @@ class ConfigurationDebugViewController: UITableViewController { } - private var customURLProvider = CustomConfigurationURLProvider() - - @UserDefaultsWrapper(key: .privacyConfigCustomURL, defaultValue: nil) - private var privacyConfigCustomURL: String? { - didSet { - customURLProvider.customPrivacyConfigurationURL = privacyConfigCustomURL.flatMap { URL(string: $0) } - Configuration.setURLProvider(customURLProvider) - lastConfigurationRefreshDate = Date.distantPast - fetchAssets() - } - } - private func fetchAssets() { AppConfigurationFetch().start(isDebug: true) { [weak tableView] result in switch result { @@ -105,18 +78,6 @@ class ConfigurationDebugViewController: UITableViewController { } } - private func customURL(for row: CustomURLsRows) -> String? { - switch row { - case .privacyConfigURL: return privacyConfigCustomURL - } - } - - private func setCustomURL(_ urlString: String?, for row: CustomURLsRows) { - switch row { - case .privacyConfigURL: privacyConfigCustomURL = urlString - } - } - @UserDefaultsWrapper(key: .lastConfigurationRefreshDate, defaultValue: .distantPast) private var lastConfigurationRefreshDate: Date private var queuedTasks: [BGTaskRequest] = [] @@ -155,12 +116,6 @@ class ConfigurationDebugViewController: UITableViewController { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) switch Sections(rawValue: indexPath.section) { - - case .customURLs: - let row = CustomURLsRows(rawValue: indexPath.row)! - cell.textLabel?.text = row.title - cell.detailTextLabel?.text = customURL(for: row) - cell.accessoryView = configureSwitchView(for: row) case .refreshInformation: switch RefreshInformationRows(rawValue: indexPath.row) { case .lastRefreshDate: @@ -205,25 +160,8 @@ class ConfigurationDebugViewController: UITableViewController { } // swiftlint:enable cyclomatic_complexity - private func configureSwitchView(for row: CustomURLsRows) -> UISwitch { - let switchView = UISwitch(frame: .zero) - switchView.isOn = customURL(for: row) != nil - let action = UIAction() { [weak self] _ in - guard let self else { return } - if switchView.isOn { - presentCustomURLAlert(for: row) - } else { - setCustomURL(nil, for: row) - tableView.reloadData() - } - } - switchView.addAction(action, for: .valueChanged) - return switchView - } - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { switch Sections(rawValue: section) { - case .customURLs: return CustomURLsRows.allCases.count case .refreshInformation: return RefreshInformationRows.allCases.count case .queuedTasks: return queuedTasks.isEmpty ? 1 : queuedTasks.count case .etags: return ETagRows.allCases.count @@ -234,8 +172,6 @@ class ConfigurationDebugViewController: UITableViewController { // swiftlint:disable:next cyclomatic_complexity override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch Sections(rawValue: indexPath.section) { - case .customURLs: - presentCustomURLAlert(for: CustomURLsRows(rawValue: indexPath.row)!) case .refreshInformation: switch RefreshInformationRows(rawValue: indexPath.row) { case .resetLastRefreshDate: @@ -261,53 +197,4 @@ class ConfigurationDebugViewController: UITableViewController { tableView.deselectRow(at: indexPath, animated: true) } - func presentCustomURLAlert(for row: CustomURLsRows) { - let alert = UIAlertController(title: row.title, message: "Provide custom URL", preferredStyle: .alert) - alert.addTextField { textField in - textField.tag = row.rawValue - } - let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in - self.tableView.reloadData() - } - alert.addAction(cancelAction) - - let submitAction = UIAlertAction(title: "Override", style: .default) { _ in - self.setCustomURL(alert.textFields?.first?.text, for: row) - self.tableView.reloadData() - } - alert.addAction(submitAction) - let cell = self.tableView.cellForRow(at: IndexPath(row: row.rawValue, - section: Sections.customURLs.rawValue))! - present(controller: alert, fromView: cell) - } - -} - -struct CustomConfigurationURLProvider: ConfigurationURLProviding { - - var customBloomFilterSpecURL: URL? - var customBloomFilterBinaryURL: URL? - var customBloomFilterExcludedDomainsURL: URL? - var customPrivacyConfigurationURL: URL? - var customTrackerDataSetURL: URL? - var customSurrogatesURL: URL? - var customFBConfigURL: URL? - - let defaultProvider = AppConfigurationURLProvider() - - func url(for configuration: Configuration) -> URL { - let defaultURL = defaultProvider.url(for: configuration) - let customURL: URL? - switch configuration { - case .bloomFilterSpec: customURL = customBloomFilterSpecURL - case .bloomFilterBinary: customURL = customBloomFilterBinaryURL - case .bloomFilterExcludedDomains:customURL = customBloomFilterExcludedDomainsURL - case .privacyConfiguration:customURL = customPrivacyConfigurationURL - case .trackerDataSet:customURL = customTrackerDataSetURL - case .surrogates:customURL = customSurrogatesURL - case .FBConfig: customURL = nil - } - return customURL ?? defaultURL - } - } diff --git a/DuckDuckGo/ConfigurationURLDebugViewController.swift b/DuckDuckGo/ConfigurationURLDebugViewController.swift new file mode 100644 index 0000000000..6f2e6eaacc --- /dev/null +++ b/DuckDuckGo/ConfigurationURLDebugViewController.swift @@ -0,0 +1,199 @@ +// +// ConfigurationURLDebugViewController.swift +// DuckDuckGo +// +// Copyright © 2023 DuckDuckGo. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import UIKit +import WebKit +import Core +import Configuration + +final class ConfigurationURLDebugViewController: UITableViewController { + + enum Sections: Int, CaseIterable { + + case customURLs + + } + + enum CustomURLsRows: Int, CaseIterable { + + case privacyConfigURL + + var title: String { + switch self { + case .privacyConfigURL: return "Privacy Config" + } + } + + } + + private var customURLProvider = CustomConfigurationURLProvider() + + @UserDefaultsWrapper(key: .lastConfigurationRefreshDate, defaultValue: .distantPast) + private var lastConfigurationRefreshDate: Date + + @UserDefaultsWrapper(key: .privacyConfigCustomURL, defaultValue: nil) + private var privacyConfigCustomURL: String? { + didSet { + customURLProvider.customPrivacyConfigurationURL = privacyConfigCustomURL.flatMap { URL(string: $0) } + Configuration.setURLProvider(customURLProvider) + lastConfigurationRefreshDate = Date.distantPast + fetchAssets() + } + } + + private func customURL(for row: CustomURLsRows) -> String? { + switch row { + case .privacyConfigURL: return privacyConfigCustomURL + } + } + + private func url(for row: CustomURLsRows) -> String { + switch row { + case .privacyConfigURL: return customURLProvider.url(for: .privacyConfiguration).absoluteString + } + } + + private func setCustomURL(_ urlString: String?, for row: CustomURLsRows) { + switch row { + case .privacyConfigURL: privacyConfigCustomURL = urlString + } + } + + private func fetchAssets() { + AppConfigurationFetch().start(isDebug: true) { [weak tableView] result in + switch result { + case .assetsUpdated(let protectionsUpdated): + if protectionsUpdated { + ContentBlocking.shared.contentBlockingManager.scheduleCompilation() + } + DispatchQueue.main.async { + tableView?.reloadData() + } + case .noData: + break + } + } + } + + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func numberOfSections(in tableView: UITableView) -> Int { + Sections.allCases.count + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + switch Sections(rawValue: section) { + case .customURLs: return CustomURLsRows.allCases.count + case nil: return 0 + } + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let row = CustomURLsRows(rawValue: indexPath.row)! + guard let cell = + tableView.dequeueReusableCell(withIdentifier: ConfigurationURLTableViewCell.reuseIdentifier) as? ConfigurationURLTableViewCell else { + fatalError("Failed to dequeue cell") + } + cell.title.text = row.title + cell.subtitle.text = url(for: row) + cell.switchView.isOn = customURL(for: row) != nil + cell.switchView.addAction(makeSwitchViewAction(for: row), for: .valueChanged) + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let row = CustomURLsRows(rawValue: indexPath.row)! + presentCustomURLAlert(for: row) + } + + private func makeSwitchViewAction(for row: CustomURLsRows) -> UIAction { + UIAction { [weak self] act in + guard let switchView = act.sender as? UISwitch else { fatalError("Wrong view") } + if switchView.isOn { + self?.presentCustomURLAlert(for: row) + } else { + self?.setCustomURL(nil, for: row) + self?.tableView.reloadData() + } + } + } + + private func presentCustomURLAlert(for row: CustomURLsRows) { + let alert = UIAlertController(title: row.title, message: "Provide custom URL", preferredStyle: .alert) + alert.addTextField { textField in + textField.tag = row.rawValue + } + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel) { _ in + self.tableView.reloadData() + } + alert.addAction(cancelAction) + + let submitAction = UIAlertAction(title: "Override", style: .default) { _ in + self.setCustomURL(alert.textFields?.first?.text, for: row) + self.tableView.reloadData() + } + alert.addAction(submitAction) + let cell = self.tableView.cellForRow(at: IndexPath(row: row.rawValue, + section: Sections.customURLs.rawValue))! + present(controller: alert, fromView: cell) + } + +} + +struct CustomConfigurationURLProvider: ConfigurationURLProviding { + + var customBloomFilterSpecURL: URL? + var customBloomFilterBinaryURL: URL? + var customBloomFilterExcludedDomainsURL: URL? + var customPrivacyConfigurationURL: URL? + var customTrackerDataSetURL: URL? + var customSurrogatesURL: URL? + var customFBConfigURL: URL? + + let defaultProvider = AppConfigurationURLProvider() + + func url(for configuration: Configuration) -> URL { + let defaultURL = defaultProvider.url(for: configuration) + let customURL: URL? + switch configuration { + case .bloomFilterSpec: customURL = customBloomFilterSpecURL + case .bloomFilterBinary: customURL = customBloomFilterBinaryURL + case .bloomFilterExcludedDomains:customURL = customBloomFilterExcludedDomainsURL + case .privacyConfiguration:customURL = customPrivacyConfigurationURL + case .trackerDataSet:customURL = customTrackerDataSetURL + case .surrogates:customURL = customSurrogatesURL + case .FBConfig: customURL = nil + } + return customURL ?? defaultURL + } + +} + +final class ConfigurationURLTableViewCell: UITableViewCell { + + static let reuseIdentifier = "ConfigurationURLTableViewCell" + + @IBOutlet weak var title: UILabel! + @IBOutlet weak var subtitle: UILabel! + @IBOutlet weak var switchView: UISwitch! + +} diff --git a/DuckDuckGo/Debug.storyboard b/DuckDuckGo/Debug.storyboard index 0b94ca5c81..86e23f022b 100644 --- a/DuckDuckGo/Debug.storyboard +++ b/DuckDuckGo/Debug.storyboard @@ -1,9 +1,9 @@ - + - + @@ -60,9 +60,29 @@ - + + + + + + + + + + + + + + + @@ -81,7 +101,7 @@ - + @@ -101,7 +121,7 @@ - + @@ -121,7 +141,7 @@ - + @@ -141,7 +161,7 @@ - + @@ -161,7 +181,7 @@ - + @@ -181,7 +201,7 @@ - + @@ -190,7 +210,7 @@ - + @@ -199,7 +219,7 @@ - + @@ -208,7 +228,7 @@ - + @@ -217,7 +237,7 @@ - + @@ -226,7 +246,7 @@ - + @@ -514,13 +534,13 @@ - + - - - - + + + + @@ -707,20 +727,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - +