Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.
Merged
17 changes: 15 additions & 2 deletions Core/AppConfigurationURLProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,32 @@
import Foundation
import Configuration
import Core
import BrowserServicesKit

struct AppConfigurationURLProvider: ConfigurationURLProviding {

private let trackerDataUrlProvider: TrackerDataURLProviding

/// Default initializer using shared dependencies.
init (privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager,
featureFlagger: FeatureFlagger = AppDependencyProvider.shared.featureFlagger) {
self.trackerDataUrlProvider = TrackerDataURLOverrider(privacyConfigurationManager: privacyConfigurationManager, featureFlagger: featureFlagger)
}

/// Initializer for injecting a custom TrackerDataURLProvider.
internal init (trackerDataUrlProvider: TrackerDataURLProviding) {
self.trackerDataUrlProvider = trackerDataUrlProvider
}

func url(for configuration: Configuration) -> URL {
switch configuration {
case .bloomFilterSpec: return URL.bloomFilterSpec
case .bloomFilterBinary: return URL.bloomFilter
case .bloomFilterExcludedDomains: return URL.bloomFilterExcludedDomains
case .privacyConfiguration: return URL.privacyConfig
case .trackerDataSet: return URL.trackerDataSet
case .trackerDataSet: return trackerDataUrlProvider.trackerDataURL ?? URL.trackerDataSet
case .surrogates: return URL.surrogates
case .remoteMessagingConfig: return RemoteMessagingClient.Constants.endpoint
}
}

}
30 changes: 29 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,9 @@
4BE67B072B96B9B0007335F7 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 4BE67B062B96B9B0007335F7 /* Common */; };
4BF3E4AF2C06A85200ED5D57 /* VPNRedditSessionWorkaround.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BF3E4AE2C06A85200ED5D57 /* VPNRedditSessionWorkaround.swift */; };
560E990F2BEE2CB800507CE0 /* SyncErrorMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 560E990E2BEE2CB800507CE0 /* SyncErrorMessage.swift */; };
563A3CFE2D37B8FA001966FD /* ConfigurationManagerIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563A3CFD2D37B8FA001966FD /* ConfigurationManagerIntegrationTests.swift */; };
563A3D012D37BF83001966FD /* ConfigurationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563A3D002D37BF83001966FD /* ConfigurationManagerTests.swift */; };
563A3D032D37C363001966FD /* AppConfigurationURLProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 563A3D022D37C363001966FD /* AppConfigurationURLProviderTests.swift */; };
564DE4532C3ED1B700D23241 /* NewTabDaxDialogFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564DE4522C3ED1B700D23241 /* NewTabDaxDialogFactory.swift */; };
564DE4552C3EDEF200D23241 /* ContextualOnboardingNewTabDialogFactoryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564DE4542C3EDEF200D23241 /* ContextualOnboardingNewTabDialogFactoryTests.swift */; };
564DE4572C4150E600D23241 /* NewTabPageControllerDaxDialogTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 564DE4562C4150E600D23241 /* NewTabPageControllerDaxDialogTests.swift */; };
Expand Down Expand Up @@ -1678,6 +1681,9 @@
4BE27566272F878F006B20B0 /* URLRequestExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = URLRequestExtension.swift; path = ../DuckDuckGo/URLRequestExtension.swift; sourceTree = "<group>"; };
4BF3E4AE2C06A85200ED5D57 /* VPNRedditSessionWorkaround.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNRedditSessionWorkaround.swift; sourceTree = "<group>"; };
560E990E2BEE2CB800507CE0 /* SyncErrorMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncErrorMessage.swift; sourceTree = "<group>"; };
563A3CFD2D37B8FA001966FD /* ConfigurationManagerIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationManagerIntegrationTests.swift; sourceTree = "<group>"; };
563A3D002D37BF83001966FD /* ConfigurationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationManagerTests.swift; sourceTree = "<group>"; };
563A3D022D37C363001966FD /* AppConfigurationURLProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppConfigurationURLProviderTests.swift; sourceTree = "<group>"; };
564DE4522C3ED1B700D23241 /* NewTabDaxDialogFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabDaxDialogFactory.swift; sourceTree = "<group>"; };
564DE4542C3EDEF200D23241 /* ContextualOnboardingNewTabDialogFactoryTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextualOnboardingNewTabDialogFactoryTests.swift; sourceTree = "<group>"; };
564DE4562C4150E600D23241 /* NewTabPageControllerDaxDialogTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageControllerDaxDialogTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4066,6 +4072,23 @@
name = LiveActivity;
sourceTree = "<group>";
};
563A3CFC2D37B8E3001966FD /* Configuration */ = {
isa = PBXGroup;
children = (
563A3CFD2D37B8FA001966FD /* ConfigurationManagerIntegrationTests.swift */,
);
path = Configuration;
sourceTree = "<group>";
};
563A3CFF2D37BF60001966FD /* Configuration */ = {
isa = PBXGroup;
children = (
563A3D002D37BF83001966FD /* ConfigurationManagerTests.swift */,
563A3D022D37C363001966FD /* AppConfigurationURLProviderTests.swift */,
);
path = Configuration;
sourceTree = "<group>";
};
566B736E2BECD3DC00FF1959 /* Utilities */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -4921,6 +4944,7 @@
85D33FCC25C97B6E002B91A6 /* IntegrationTests */ = {
isa = PBXGroup;
children = (
563A3CFC2D37B8E3001966FD /* Configuration */,
1E1D8B5F29950FB300C96994 /* Autoconsent */,
85F21DBD21121147002631A6 /* AtbServerTests.swift */,
85519124247468580010FDD0 /* TrackerRadarIntegrationTests.swift */,
Expand Down Expand Up @@ -6210,6 +6234,7 @@
F12D98401F266B30003C2EE3 /* DuckDuckGo */ = {
isa = PBXGroup;
children = (
563A3CFF2D37BF60001966FD /* Configuration */,
6FF9157F2B88E04F0042AC87 /* AdAttribution */,
F17669A21E411D63003D3222 /* Application */,
981FED7222045FFA008488D7 /* AutoClear */,
Expand Down Expand Up @@ -8500,6 +8525,7 @@
files = (
8528AE84212FF9A100D0BD74 /* AppRatingPromptStorageTests.swift in Sources */,
569437312BE3F64400C0881B /* SyncErrorHandlerSyncPausedAlertsTests.swift in Sources */,
563A3D012D37BF83001966FD /* ConfigurationManagerTests.swift in Sources */,
9F16230B2CA0F0190093C4FC /* DebouncerTests.swift in Sources */,
1CB7B82323CEA28300AA24EA /* DateExtensionTests.swift in Sources */,
31C138A427A3352600FFD4B2 /* DownloadTests.swift in Sources */,
Expand Down Expand Up @@ -8546,6 +8572,7 @@
C158AC7B297AB5DC0008723A /* MockSecureVault.swift in Sources */,
569437342BE4E41500C0881B /* SyncErrorHandlerSyncErrorsAlertsTests.swift in Sources */,
85C11E4120904BBE00BFFEB4 /* VariantManagerTests.swift in Sources */,
563A3D032D37C363001966FD /* AppConfigurationURLProviderTests.swift in Sources */,
F1134ECE1F40EA9C00B73467 /* AtbParserTests.swift in Sources */,
F189AEE41F18FDAF001EBAE1 /* LinkTests.swift in Sources */,
6F7FB8E12C660B3E00867DA7 /* NewTabPageFavoritesModelTests.swift in Sources */,
Expand Down Expand Up @@ -8755,6 +8782,7 @@
1E1D8B6629953B9800C96994 /* WebViewTestHelper.swift in Sources */,
EE3B226C29DE0FD30082298A /* MockInternalUserStoring.swift in Sources */,
CB5516D0286500290079B175 /* TrackerRadarIntegrationTests.swift in Sources */,
563A3CFE2D37B8FA001966FD /* ConfigurationManagerIntegrationTests.swift in Sources */,
1E1D8B6129950FD200C96994 /* AutoconsentBackgroundTests.swift in Sources */,
CB5516D2286500290079B175 /* AtbServerTests.swift in Sources */,
);
Expand Down Expand Up @@ -11933,7 +11961,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 224.7.2;
version = 225.0.0;
};
};
9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 35 additions & 11 deletions DuckDuckGo/Configuration/ConfigurationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import os.log

final class ConfigurationManager: DefaultConfigurationManager {

private let trackerDataManager: TrackerDataManager
private let privacyConfigurationManager: PrivacyConfigurationManaging

private enum Constants {
static let lastConfigurationInstallDateKey = "config.last.installed"
}
Expand Down Expand Up @@ -71,9 +74,13 @@ final class ConfigurationManager: DefaultConfigurationManager {
}
}

override init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents),
store: ConfigurationStoring = AppDependencyProvider.shared.configurationStore,
defaults: KeyValueStoring = UserDefaults(suiteName: "\(Global.groupIdPrefix).app-configuration") ?? UserDefaults()) {
init(fetcher: ConfigurationFetching = ConfigurationFetcher(store: ConfigurationStore(), eventMapping: configurationDebugEvents),
store: ConfigurationStoring = AppDependencyProvider.shared.configurationStore,
defaults: KeyValueStoring = UserDefaults(suiteName: "\(Global.groupIdPrefix).app-configuration") ?? UserDefaults(),
trackerDataManager: TrackerDataManager = ContentBlocking.shared.trackerDataManager,
privacyConfigurationManager: PrivacyConfigurationManaging = ContentBlocking.shared.privacyConfigurationManager) {
self.trackerDataManager = trackerDataManager
self.privacyConfigurationManager = privacyConfigurationManager
super.init(fetcher: fetcher, store: store, defaults: defaults)
subscribeToLifecycleNotifications()
}
Expand Down Expand Up @@ -117,10 +124,27 @@ final class ConfigurationManager: DefaultConfigurationManager {
private func fetchTrackerBlockingDependencies(isDebug: Bool = false) async -> Bool {
var didFetchAnyTrackerBlockingDependencies = false

var tasks = [Configuration: Task<(), Swift.Error>]()
tasks[.trackerDataSet] = Task { try await fetcher.fetch(.trackerDataSet, isDebug: isDebug) }
tasks[.surrogates] = Task { try await fetcher.fetch(.surrogates, isDebug: isDebug) }
tasks[.privacyConfiguration] = Task { try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug) }
// Start surrogates fetch task
let surrogatesTask = Task { try await fetcher.fetch(.surrogates, isDebug: isDebug) }

// Perform privacyConfiguration fetch and update
do {
try await fetcher.fetch(.privacyConfiguration, isDebug: isDebug)
didFetchAnyTrackerBlockingDependencies = true
privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
data: store.loadData(for: .privacyConfiguration))
} catch {
Logger.general.error("Did not apply update to \(Configuration.privacyConfiguration.rawValue, privacy: .public): \(error.localizedDescription, privacy: .public)")
}

// Start trackerDataSet fetch task after privacyConfiguration completes
let trackerDataSetTask = Task { try await fetcher.fetch(.trackerDataSet, isDebug: isDebug) }

// Wait for surrogates and trackerDataSet tasks
let tasks: [(Configuration, Task<(), Swift.Error>)] = [
(.surrogates, surrogatesTask),
(.trackerDataSet, trackerDataSetTask)
]

for (configuration, task) in tasks {
do {
Expand All @@ -135,10 +159,10 @@ final class ConfigurationManager: DefaultConfigurationManager {
}

private func updateTrackerBlockingDependencies() {
ContentBlocking.shared.privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
data: store.loadData(for: .privacyConfiguration))
ContentBlocking.shared.trackerDataManager.reload(etag: store.loadEtag(for: .trackerDataSet),
data: store.loadData(for: .trackerDataSet))
privacyConfigurationManager.reload(etag: store.loadEtag(for: .privacyConfiguration),
data: store.loadData(for: .privacyConfiguration))
trackerDataManager.reload(etag: store.loadEtag(for: .trackerDataSet),
data: store.loadData(for: .trackerDataSet))
NotificationCenter.default.post(name: ConfigurationManager.didUpdateTrackerDependencies, object: self)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// AppConfigurationURLProviderTests.swift
// DuckDuckGo
//
// Copyright © 2025 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 XCTest
import BrowserServicesKit
import Configuration
@testable import DuckDuckGo

final class AppConfigurationURLProviderTests: XCTestCase {
private var urlProvider: AppConfigurationURLProvider!
private var mockTdsURLProvider: MockTrackerDataURLProvider!
let controlURL = "control/url.json"
let treatmentURL = "treatment/url.json"

override func setUp() {
super.setUp()
mockTdsURLProvider = MockTrackerDataURLProvider()
urlProvider = AppConfigurationURLProvider(trackerDataUrlProvider: mockTdsURLProvider)
}

override func tearDown() {
urlProvider = nil
mockTdsURLProvider = nil
super.tearDown()
}

func testUrlForTrackerDataIsDefaultWhenTdsUrlProviderUrlIsNil() {
// GIVEN
mockTdsURLProvider.trackerDataURL = nil

// WHEN
let url = urlProvider.url(for: .trackerDataSet)

// THEN
XCTAssertEqual(url, URL.trackerDataSet)
}

func testUrlForTrackerDataIsTheOneProvidedByTdsUrlProvider() {
// GIVEN
let expectedURL = URL(string: "https://someurl.com")!
mockTdsURLProvider.trackerDataURL = expectedURL

// WHEN
let url = urlProvider.url(for: .trackerDataSet)

// THEN
XCTAssertEqual(url, expectedURL)
}

}

class MockTrackerDataURLProvider: TrackerDataURLProviding {
var trackerDataURL: URL?
}
Loading
Loading