12 KiB
12 KiB
source, confidence, namespace, last_synced, alwaysApply
| source | confidence | namespace | last_synced | alwaysApply |
|---|---|---|---|---|
| ~/DuckDuckGo/apple-browsers.git/main/.cursor/rules/browserserviceskit-integration.mdc | 0.9 | work | 2026-04-28 | false |
BrowserServicesKit Integration Guide
Overview
BrowserServicesKit is the core shared library providing essential browser functionality to both iOS and macOS DuckDuckGo applications. It ensures consistent behavior and code reuse across platforms while maintaining privacy as the primary focus.
Core Modules and Usage
Privacy & Protection
Always use BrowserServicesKit for privacy-related functionality:
// ✅ CORRECT - Content blocking integration
import BrowserServicesKit
import ContentBlocking
final class PrivacyManager {
private let contentBlockingManager = ContentBlockingManager.shared
func enableContentBlocking(for webView: WKWebView) {
contentBlockingManager.enable(for: webView)
}
func updateBlockingRules() async {
await contentBlockingManager.updateRules()
}
}
// ✅ CORRECT - Privacy configuration
import PrivacyConfig
final class PrivacyFeatureManager {
private let privacyConfig = PrivacyConfiguration.shared
func isFeatureEnabled(_ feature: PrivacyFeature) -> Bool {
return privacyConfig.isEnabled(feature)
}
}
Data Management
Use BrowserServicesKit for all data persistence:
// ✅ CORRECT - Bookmarks management
import Bookmarks
final class BookmarkService {
private let bookmarkManager = BookmarkManager.shared
func saveBookmark(_ bookmark: Bookmark) async {
await bookmarkManager.save(bookmark)
}
func fetchBookmarks() async -> [Bookmark] {
return await bookmarkManager.fetchAll()
}
}
// ✅ CORRECT - Secure credential storage
import SecureVault
final class CredentialManager {
private let secureVault = SecureVault.shared
func storeCredential(_ credential: WebsiteCredential) async throws {
try await secureVault.store(credential)
}
func retrieveCredentials(for domain: String) async throws -> [WebsiteCredential] {
return try await secureVault.credentials(for: domain)
}
}
Navigation and URL Handling
Use BrowserServicesKit for navigation logic:
// ✅ CORRECT - Navigation handling
import Navigation
final class NavigationManager {
private let navigationController = NavigationController()
func navigate(to url: URL) {
let request = NavigationRequest(url: url)
navigationController.navigate(request)
}
func canGoBack() -> Bool {
return navigationController.canGoBack
}
}
User Scripts and Content Injection
Use BrowserServicesKit for JavaScript injection:
// ✅ CORRECT - User script management
import UserScript
final class ContentScriptManager {
private let userScriptManager = UserScriptManager()
func injectPrivacyScripts(into webView: WKWebView) {
let scripts = userScriptManager.privacyScripts
scripts.forEach { script in
webView.configuration.userContentController.addUserScript(script)
}
}
}
Platform-Specific Integration
iOS Integration Pattern
// ✅ CORRECT - iOS-specific BrowserServicesKit usage
import BrowserServicesKit
import UIKit
final class iOSBrowserViewController: UIViewController {
private let contentBlockingManager = ContentBlockingManager.shared
private let privacyDashboard = PrivacyDashboard()
override func viewDidLoad() {
super.viewDidLoad()
setupBrowserServices()
}
private func setupBrowserServices() {
// Configure content blocking for iOS
contentBlockingManager.configure(for: .iOS)
// Setup privacy dashboard
privacyDashboard.delegate = self
}
}
macOS Integration Pattern
// ✅ CORRECT - macOS-specific BrowserServicesKit usage
import BrowserServicesKit
import AppKit
final class macOSBrowserViewController: NSViewController {
private let contentBlockingManager = ContentBlockingManager.shared
private let downloadManager = DownloadManager.shared
override func viewDidLoad() {
super.viewDidLoad()
setupBrowserServices()
}
private func setupBrowserServices() {
// Configure content blocking for macOS
contentBlockingManager.configure(for: .macOS)
// Setup download handling
downloadManager.delegate = self
}
}
Feature-Specific Integration
Autofill Integration
// ✅ CORRECT - Autofill implementation
import Autofill
import BrowserServicesKit
final class AutofillCoordinator {
private let autofillManager = AutofillManager.shared
func setupAutofill(for webView: WKWebView) {
// Configure autofill user scripts
let autofillScripts = autofillManager.userScripts
autofillScripts.forEach { script in
webView.configuration.userContentController.addUserScript(script)
}
// Setup message handlers
autofillManager.setupMessageHandlers(for: webView)
}
func handleAutofillRequest(_ request: AutofillRequest) async {
await autofillManager.handleRequest(request)
}
}
Sync Integration
// ✅ CORRECT - Sync functionality
import DDGSync
import BrowserServicesKit
final class SyncManager {
private let syncService = SyncService.shared
func enableSync() async {
await syncService.enable()
}
func syncBookmarks() async {
await syncService.sync(.bookmarks)
}
func syncCredentials() async {
await syncService.sync(.credentials)
}
}
Testing with BrowserServicesKit
Mock BrowserServicesKit Components
// ✅ CORRECT - Testing with mocks
import BrowserServicesKit
import XCTest
final class MockContentBlockingManager: ContentBlockingManagerProtocol {
var enabledRules: [ContentBlockingRule] = []
var isBlocked: Bool = false
func enable(rules: [ContentBlockingRule]) {
enabledRules = rules
}
func isBlocked(url: URL) -> Bool {
return isBlocked
}
}
final class BrowserFeatureTests: XCTestCase {
private var mockContentBlocking: MockContentBlockingManager!
private var browserManager: BrowserManager!
override func setUp() {
super.setUp()
mockContentBlocking = MockContentBlockingManager()
browserManager = BrowserManager(contentBlocking: mockContentBlocking)
}
func testContentBlockingEnabled() {
// Given
let rules = [ContentBlockingRule.trackerRule]
// When
browserManager.enableContentBlocking(rules: rules)
// Then
XCTAssertEqual(mockContentBlocking.enabledRules, rules)
}
}
Configuration and Environment
Environment-Based Configuration
// ✅ CORRECT - Environment configuration
import Configuration
import BrowserServicesKit
final class AppConfiguration {
static func configure() {
// Configure BrowserServicesKit for current environment
let config = Configuration.current
BrowserServicesKit.configure(
environment: config.environment,
privacyConfig: config.privacyConfig,
contentBlockingConfig: config.contentBlockingConfig
)
}
}
Feature Flag Integration
// ✅ CORRECT - Feature flag integration
import FeatureFlags
import BrowserServicesKit
extension FeatureFlags {
var isAdvancedPrivacyEnabled: Bool {
return isEnabled(.advancedPrivacy)
}
var isEnhancedAutofillEnabled: Bool {
return isEnabled(.enhancedAutofill)
}
}
final class FeatureFlagBrowserManager {
func configureFeatures() {
if FeatureFlags.shared.isAdvancedPrivacyEnabled {
PrivacyConfiguration.shared.enableAdvancedFeatures()
}
if FeatureFlags.shared.isEnhancedAutofillEnabled {
AutofillManager.shared.enableEnhancedFeatures()
}
}
}
Performance Optimization
Efficient BrowserServicesKit Usage
// ✅ CORRECT - Performance-optimized usage
import BrowserServicesKit
final class OptimizedBrowserManager {
private let contentBlockingManager = ContentBlockingManager.shared
private var cachedRules: [ContentBlockingRule] = []
func loadContentBlockingRules() async {
// Cache rules to avoid repeated API calls
if cachedRules.isEmpty {
cachedRules = await contentBlockingManager.loadRules()
}
// Apply cached rules
await contentBlockingManager.apply(cachedRules)
}
func updateRulesIfNeeded() async {
let lastUpdate = await contentBlockingManager.lastUpdateTime
let shouldUpdate = Date().timeIntervalSince(lastUpdate) > 3600 // 1 hour
if shouldUpdate {
await loadContentBlockingRules()
}
}
}
Error Handling
BrowserServicesKit Error Handling
// ✅ CORRECT - Error handling patterns
import BrowserServicesKit
enum BrowserServiceError: LocalizedError {
case contentBlockingFailed
case bookmarkSaveFailed
case credentialStoreFailed
var errorDescription: String? {
switch self {
case .contentBlockingFailed:
return "Failed to enable content blocking"
case .bookmarkSaveFailed:
return "Failed to save bookmark"
case .credentialStoreFailed:
return "Failed to store credential"
}
}
}
final class ErrorHandlingBrowserManager {
func enableContentBlocking() async {
do {
try await ContentBlockingManager.shared.enable()
} catch {
// Log error and provide fallback
Logger.error("Content blocking failed: \(error)")
await showErrorToUser(BrowserServiceError.contentBlockingFailed)
}
}
}
API Usage Guidelines
Consistent API Patterns
// ✅ CORRECT - Follow BrowserServicesKit API patterns
import BrowserServicesKit
final class BrowserAPIManager {
// Use async/await for async operations
func loadData() async throws -> BrowserData {
return try await BrowserDataManager.shared.load()
}
// Use Combine for reactive streams
func observePrivacyEvents() -> AnyPublisher<PrivacyEvent, Never> {
return PrivacyManager.shared.privacyEventPublisher
}
// Use completion handlers only when required by platform APIs
func legacyOperation(completion: @escaping (Result<Data, Error>) -> Void) {
Task {
do {
let data = try await modernAsyncOperation()
completion(.success(data))
} catch {
completion(.failure(error))
}
}
}
}
Common Integration Patterns
Dependency Injection with BrowserServicesKit
// ✅ CORRECT - Dependency injection pattern
protocol BrowserServiceProvider {
var contentBlockingManager: ContentBlockingManagerProtocol { get }
var bookmarkManager: BookmarkManagerProtocol { get }
var autofillManager: AutofillManagerProtocol { get }
}
final class DefaultBrowserServiceProvider: BrowserServiceProvider {
let contentBlockingManager: ContentBlockingManagerProtocol = ContentBlockingManager.shared
let bookmarkManager: BookmarkManagerProtocol = BookmarkManager.shared
let autofillManager: AutofillManagerProtocol = AutofillManager.shared
}
final class BrowserViewModel: ObservableObject {
private let serviceProvider: BrowserServiceProvider
init(serviceProvider: BrowserServiceProvider = DefaultBrowserServiceProvider()) {
self.serviceProvider = serviceProvider
}
}
This guide ensures proper integration with BrowserServicesKit while maintaining privacy-first principles and cross-platform compatibility.