### Initial Brave Browser Setup for iOS Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Run this command after cloning the brave-browser repository and ensuring all prerequisites are met to initialize the iOS target. ```shell cd brave-browser npm run init -- --target_os=ios ``` -------------------------------- ### TableViewController Example Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Subclass `TableViewController` to automatically set up a `DataSource`. Override `viewDidLoad` to configure the `dataSource.sections` with your desired content. ```swift class SomeViewController: TableViewController { override func viewDidLoad() { super.viewDidLoad() dataSource.sections = [ Section(rows: [ Row(text: "Hi") ]), // ... ] } } ``` -------------------------------- ### Configure a Section with Rows, Headers, Footers, and Selections Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown This example demonstrates a more complex section configuration, including header and footer text, custom accessory views, and selection handlers for rows. The selection closure allows for custom actions, such as presenting a view controller. ```swift Section(header: "Money", rows: [ Row(text: "Balance", detailText: "$12.00", accessory: .disclosureIndicator, selection: { // Show statement }), Row(text: "Transfer to Bank…", cellClass: ButtonCell.self, selection: { [unowned self] in let viewController = ViewController() self.presentViewController(viewController, animated: true, completion: nil) }) ], footer: "Transfers usually arrive within 1-3 business days.") ``` -------------------------------- ### Define a Simple Section with Rows Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Use this to create a basic section with a single row. No special setup is required beyond importing the Static library. ```swift import Static Section(rows: [ Row(text: "Hello") ]) ``` -------------------------------- ### Keeping Brave Core Up-to-Date for iOS Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md After initial setup, use these commands to pull the latest changes in brave-core and sync with the brave-browser project for iOS. ```shell cd /path/to/brave-browser/src/brave git checkout master && git pull npm run sync --target_os=ios ``` -------------------------------- ### Get or Create Domain for URL Source: https://context7.com/brave/brave-ios/llms.txt Fetch or create a Domain object for a given URL. Domain objects are managed by Core Data and require access on the main context. Use this to manage per-site shield overrides. ```swift import Data import BraveShields // Fetch or create a Domain for a URL let url = URL(string: "https://example.com")! // Domain objects are managed by Core Data; access on main context: let domain = Domain.getOrCreate(forUrl: url, persistent: true) ``` -------------------------------- ### Get FingerprintingProtection User Script Source: https://context7.com/brave/brave-ios/llms.txt Retrieve the WKUserScript for FingerprintingProtection. This script is injected at document start to intercept fingerprinting attempts and increment relevant counters. ```swift import WebKit // FingerprintingProtection is registered on a Tab's WKWebView: // (Typically done inside BrowserViewController when a Tab is created) // The script is loaded and secured at class load time: let script: WKUserScript? = FingerprintingProtection.userScript // Injected at .atDocumentStart, in .page content world, for all frames // When JS fires the message handler: // userContentController(_:didReceiveScriptMessage:replyHandler:) is called: // tab?.contentBlocker.stats = stats.adding(fingerprintingCount: 1) // BraveGlobalShieldStats.shared.fpProtection += 1 ``` -------------------------------- ### Initialize and Manage BraveVPN Lifecycle Source: https://context7.com/brave/brave-ios/llms.txt Initialize BraveVPN at app launch. Check VPN state, connect, disconnect, fetch, and set regions. Use this for managing the full VPN connection lifecycle. ```swift import BraveVPN // Initialize at app launch (pass SKUs credential if available) BraveVPN.initialize(customCredential: nil) // Check current VPN state switch BraveVPN.vpnState { case .notPurchased: print("User has not purchased VPN") case .purchased(let connected): print("Purchased, connected:", connected) case .expired: print("Subscription expired") } // Connect to VPN (automatically selects best region) BraveVPN.reconnect() // Disconnect BraveVPN.disconnect() // Fetch available regions BraveVPN.fetchRegions { regions in for region in regions { print(region.displayName) } } // Set a specific region if let usRegion = BraveVPN.regions.first(where: { $0.regionName == "us-east-1" }) { BraveVPN.setRegion(server: usRegion) { success in print("Region set:", success) } } // Check if VPN is currently connected print(BraveVPN.isConnected) // → true / false ``` -------------------------------- ### Build Safari-Compatible User-Agent Strings Source: https://context7.com/brave/brave-ios/llms.txt Construct User-Agent strings that mimic Safari for mobile and desktop. This ensures compatibility with web servers expecting specific UA formats. ```swift import UserAgent // Default construction using current device + iOS version let builder = UserAgentBuilder() // Mobile UA (iPhone) let mobileUA = builder.build(desktopMode: false) // → "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0_1 like Mac OS X) // AppleWebKit/605.1.15 (KHTML, like Gecko) // Version/17.0 Mobile/15E148 Safari/604.1" // Desktop UA (macOS Safari spoofing) let desktopUA = builder.build(desktopMode: true) // → "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) // AppleWebKit/605.1.15 (KHTML, like Gecko) // Version/17.0 Safari/605.1.15" ``` -------------------------------- ### Updating Brave Core in brave-ios via package.json Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md To update BraveCore within the brave-ios project, find the desired version in brave-browser/releases, copy the .tgz asset URL, update package.json, and run npm install. ```shell npm install ``` -------------------------------- ### Initialize and Configure DataSource Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Create a `DataSource` instance and populate its `sections` property with `Section` objects containing `Row`s. Assign the `DataSource` to your table view's `dataSource` property. ```swift let dataSource = DataSource() dataSource.sections = [ Section(rows: [ Row(text: "Hello") ]) ] ``` ```swift dataSource.tableView = tableView ``` -------------------------------- ### Use UserAgentBuilder Convenience and Customization Source: https://context7.com/brave/brave-ios/llms.txt Utilize convenience properties for desktop User-Agent strings and customize the builder with specific device and iOS versions for testing purposes. ```swift // Convenience via UserAgent struct let ua = UserAgent.userAgentForDesktopMode // On iPad with "Request Desktop Site" enabled → desktop UA // On iPhone → mobile UA // Custom iOS version (useful for tests) let customBuilder = UserAgentBuilder( device: .current, iOSVersion: OperatingSystemVersion(majorVersion: 16, minorVersion: 0, patchVersion: 0) ) print(customBuilder.build(desktopMode: false)) // → Version/16.6 Mobile/15E148 Safari/604.1 ``` -------------------------------- ### Preferences.Option - Typed UserDefaults Wrapper Source: https://context7.com/brave/brave-ios/llms.txt Demonstrates how to use the generic Preferences.Option to wrap UserDefaults entries, providing typed access, automatic persistence, and reactive updates via Combine. ```APIDOC ## Preferences.Option — Typed UserDefaults Wrapper `Preferences.Option` is a generic, `ObservableObject`-conforming wrapper around `UserDefaults` entries. Reading or writing `.value` automatically persists to the shared app-group `UserDefaults` container and notifies registered `PreferencesObserver` objects. Each `Option` stores a strongly-typed default and exposes a Combine `@Published` property for reactive UI binding. ```swift import Preferences // Read a preference value let isHTTPSEverywhereOn: Bool = Preferences.Shields.httpsEverywhere.value // → true (default) // Write a preference value — persists immediately to UserDefaults Preferences.Shields.blockScripts.value = true // Observe changes (class must conform to PreferencesObserver) class MyObserver: PreferencesObserver { func preferencesDidChange(for key: String) { print("Changed key:", key) // → "shields.block-scripts" } } let observer = MyObserver() Preferences.Shields.blockScripts.observe(from: observer) // Reset to default Preferences.Shields.blockScripts.reset() // Preferences.Shields.blockScripts.value == false // Combine binding in SwiftUI import Combine var cancellable: AnyCancellable? cancellable = Preferences.Shields.fingerprintingProtection.$value .sink { enabled in print("Fingerprint protection:", enabled) } ``` ``` -------------------------------- ### DataController - Initialization and background writes Source: https://context7.com/brave/brave-ios/llms.txt Manages the Core Data stack and provides methods for performing background and main-thread data operations. ```APIDOC ## DataController - Initialization and background writes ### Description Manages the Core Data stack and provides methods for performing background and main-thread data operations. ### Methods - `shared.initializeOnce()`: Initializes the shared Core Data stack. - `perform(context:save:task:)`: Performs a data operation on a background context. - `performOnMainContext(context:)`: Performs a data operation on the main context. - `sharedInMemory.initializeOnce()`: Initializes an in-memory Core Data stack for testing. ### Parameters - **context** (ContextType) - Specifies the context to use, e.g., `.new`, `.existing(existingContext)`, `.new(inMemory: Bool)`. - **save** (Bool) - Whether to save the context after the operation. - **task** ( (NSManagedObjectContext) -> Void ) - The closure containing the data operations. ### Request Example ```swift // Initialize once at app launch DataController.shared.initializeOnce() // Background write using a new context DataController.perform { context in let favorite = Favorite(context: context) favorite.url = "https://brave.com" favorite.title = "Brave" } // Background write reusing an existing context DataController.perform(context: .existing(existingContext), save: false) { context in let item = PlaylistItem(context: context) item.name = "My Video" } // Main-thread write DataController.performOnMainContext { context in if let domain = try? context.fetch(Domain.fetchRequest()).first { domain.visits += 1 } } // In-memory store for unit tests DataController.sharedInMemory.initializeOnce() DataController.perform(context: .new(inMemory: true)) { context in // writes go to in-memory store only } ``` ### Response N/A (Operations are performed asynchronously and do not return direct values in this context). ``` -------------------------------- ### Building Brave Core for iOS Simulator (Apple Silicon) Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Create an iOS simulator debug build specifically for Apple Silicon simulators (arm64 architecture). ```shell npm run build -- Debug --target_os=ios --target_arch=arm64 --target_environment=simulator ``` -------------------------------- ### Initialize BraveWebView for Regular and Private Tabs Source: https://context7.com/brave/brave-ios/llms.txt Instantiate BraveWebView for standard browsing or private tabs. Private tabs utilize a shared non-persistent WKWebsiteDataStore for enhanced privacy. ```swift import WebKit // Standard (persistent) browsing tab let regularWebView = BraveWebView( frame: .zero, isPrivate: false // uses WKWebsiteDataStore.default() ) // Private browsing tab — uses shared non-persistent store let privateWebView = BraveWebView( frame: .zero, isPrivate: true // uses BraveWebView.sharedNonPersistentStore() ) ``` -------------------------------- ### Running Brave Core Tests with Build Arguments Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Execute Brave Core tests using the same arguments that were provided during the build process. This ensures test consistency. ```shell npm run test brave_rewards_ios_tests -- Debug --target_os=ios ``` -------------------------------- ### Push Strings to Transifex Source: https://github.com/brave/brave-ios/blob/development/App/l10n/tools/README.md Use this script to push newly added or modified strings from the local project to Transifex for translation. Ensure you are in the 'l10n/' directory and provide your Transifex username and password. ```bash cd l10n/ USERNAME= PASSWORD= ./push-strings-to-transifex.sh ``` -------------------------------- ### Building Brave Core for iOS (arm64) Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Compile a release build of BraveCore for iOS targeting the arm64 architecture. ```shell npm run build -- Release --target_os=ios --target_arch=arm64 ``` -------------------------------- ### Building Brave Core for iOS (Release) Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Compile a release build of BraveCore for iOS. This is typically used for production or pre-release testing. ```shell npm run build -- Release --target_os=ios ``` -------------------------------- ### Running Brave Rewards iOS Unit Tests Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Execute Brave Rewards iOS unit tests using this npm command. Ensure you are in the correct directory within the brave-browser project. ```shell npm run test brave_rewards_ios_tests -- --target_os=ios ``` -------------------------------- ### Handle IPFS and Web3 Domain Interstitials Source: https://context7.com/brave/brave-ios/llms.txt Register IPFSSchemeHandler and Web3DomainHandler with WKWebViewConfiguration to serve interstitial pages for IPFS and Web3 domain URLs. These handlers prompt user consent before resolving decentralized web content. ```swift import WebKit // IPFSSchemeHandler is registered on the WKWebViewConfiguration: let ipfsHandler = IPFSSchemeHandler() // Registered at path "internal://local/web3/ipfs" // When the user navigates to ipfs://Qm... the handler returns an HTML page // offering "Use Public Gateway" or "Disable IPFS" options. // Web3DomainHandler services ENS/SNS/Unstoppable domain interstitials. // Example: navigating to "vitalik.eth" triggers an ENS interstitial. // The handler reads localized strings for each Web3Service case: let service = Web3Service.ethereum print(service.errorTitle) // → "Ethereum Name Service" print(service.errorDescription) // → "ENS domain resolution requires ... terms of use ... privacy policy ..." // The interstitials are loaded internally via InternalSchemeHandler: // URL pattern: "internal://local/?url=" // Check supported Unstoppable Domain extensions import BraveWallet print(WalletConstants.supportedUDExtensions) // → [".crypto", ".nft", ".x", ".wallet", ...] ``` -------------------------------- ### Event Listener for Proceed Anyway Link Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Attaches a click event listener to the 'proceedAnyway' link to send a message via webkit.messageHandlers. ```javascript var proceedAnywayLink = document.getElementById("proceedAnyway"); if (proceedAnywayLink != null) { proceedAnywayLink.addEventListener('click', function(e) { e.preventDefault(); webkit.messageHandlers["%message_handler%"].postMessage({ "securityToken": "%security_token%", "type": "certVisitOnce" }); }); } ``` -------------------------------- ### Running Brave Core Dependency Check Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Perform a dependency check for Brave Core using the arguments specified during the build. This is crucial for CI jobs. ```shell npm run gn_check -- Debug --target_os=ios --target_environment=simulator ``` -------------------------------- ### NetworkManager - Async/await data request Source: https://context7.com/brave/brave-ios/llms.txt Fetches data from a URL using async/await. This method does not perform ETag caching. ```APIDOC ## NetworkManager - Async/await data request ### Description Fetches data from a URL using async/await. This method does not perform ETag caching. ### Method `dataRequest(with: URL) async throws -> (Data, URLResponse)` ### Parameters #### Path Parameters None #### Query Parameters None ### Request Example ```swift let manager = NetworkManager() let url = URL(string: "https://cdn.brave.com/filters/brave-lists.txt")! let (data, response) = try await manager.dataRequest(with: url) print(data.count, response) ``` ### Response #### Success Response (200) - **data** (Data) - The downloaded data. - **response** (URLResponse) - The URL response. #### Response Example ``` (12345, { URL: https://cdn.brave.com/filters/brave-lists.txt, status code: 200, headers: {...}}) ``` ``` -------------------------------- ### NetworkManager - Combine publisher with retry Source: https://context7.com/brave/brave-ios/llms.txt Provides a Combine publisher for downloading resources, including automatic retry logic. ```APIDOC ## NetworkManager - Combine publisher with retry ### Description Provides a Combine publisher for downloading resources, including automatic retry logic. ### Method `downloadResource(with: URL, resourceType: ResourceType, retryTimeout: TimeInterval) -> AnyPublisher` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **url** (URL) - The URL of the resource to download. - **resourceType** (ResourceType) - Specifies the resource type, e.g., `.regular`. - **retryTimeout** (TimeInterval) - The timeout for retries in seconds. ### Request Example ```swift import Combine var cancellable: AnyCancellable? cancellable = manager.downloadResource(with: url, resourceType: .regular, retryTimeout: 60) .sink( receiveCompletion: { if case .failure(let error) = $0 { print("Download failed:", error) // NetworkManagerError.fileNotModified if 304 } }, receiveValue: { resource in print("Downloaded \(resource.data.count) bytes") } ) ``` ### Response #### Success Response (200) - **resource** (CachedNetworkResource) - The downloaded resource containing data, etag, and timestamp. #### Response Example ``` // Prints: // Downloaded 2048 bytes ``` ``` -------------------------------- ### Dispatch NavigationPath to BrowserViewController Source: https://context7.com/brave/brave-ios/llms.txt Illustrates how to dispatch a parsed NavigationPath to the BrowserViewController for handling. This is the final step after parsing a deep link. ```swift // Dispatch to BrowserViewController // NavigationPath.handle(nav: path, with: browserViewController) ``` -------------------------------- ### Pull Translations from Transifex Source: https://github.com/brave/brave-ios/blob/development/App/l10n/tools/README.md Import the latest translations from Transifex into the Xcode project. Navigate to the 'l10n/' directory and provide your Transifex credentials. Issues during this process are logged to 'output.log'. ```bash cd l10n/ USERNAME= PASSWORD= ./pull-translations-into-project.sh ``` -------------------------------- ### Formatting Brave Core Code Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Apply code formatting rules to the Brave Core project. This command should be run before submitting a Pull Request. ```shell npm run format ``` -------------------------------- ### Implement Custom Cell Configuration Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Conform to the `Cell` protocol and implement the `configure(row:)` method to customize how a `Row`'s data is applied to a cell. This method is called by the `DataSource` to update the cell's appearance based on the row's properties. ```swift func configure(row row: Row) { // Custom cell configuration logic here } ``` -------------------------------- ### Creating a New Feature Branch in Brave Core Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Before making changes to BraveCore.xcframework, create a new branch in the brave-core repository located within the brave-browser src directory. ```shell cd src/brave git checkout -b my-feature-branch ``` -------------------------------- ### NetworkManager - Async/await ETag cached download Source: https://context7.com/brave/brave-ios/llms.txt Downloads a resource from a URL with ETag caching. It supports retries and checking for server-side modifications. ```APIDOC ## NetworkManager - Async/await ETag cached download ### Description Downloads a resource from a URL with ETag caching. It supports retries and checking for server-side modifications. ### Method `downloadResource(with: URL, resourceType: ResourceType, retryTimeout: TimeInterval, checkLastServerSideModification: Bool) async throws -> CachedNetworkResource` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **url** (URL) - The URL of the resource to download. - **resourceType** (ResourceType) - Specifies the resource type, e.g., `.cached(etag: String)`. - **retryTimeout** (TimeInterval) - The timeout for retries in seconds. - **checkLastServerSideModification** (Bool) - Whether to check for last server-side modification. ### Request Example ```swift let resource = try await manager.downloadResource( with: url, resourceType: .cached(etag: "\"abc123\""), retryTimeout: 30, checkLastServerSideModification: true ) print(resource.data.count) print(resource.etag ?? "no etag") print(resource.lastModifiedTimestamp ?? 0) ``` ### Response #### Success Response (200) - **data** (Data) - The downloaded data. - **etag** (String?) - The ETag of the resource, if available. - **lastModifiedTimestamp** (TimeInterval?) - The last modified timestamp, if available. #### Response Example ``` // Prints: // 1024 // "def456" // 1678886400.0 ``` ``` -------------------------------- ### Handle User Actions on Blocked Domain Page Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/BlockedDomain.html This JavaScript code attaches event listeners to the 'proceed' and 'go back' buttons. It sends messages to the Brave application via `webkit.messageHandlers` to indicate the user's choice. ```javascript (() => { const sendAction = (action) => { webkit.messageHandlers["%message_handler%"].postMessage({ "securityToken": "%security_token%", "action": action }) } document.getElementById('proceed-action').onclick = () => { sendAction('didProceed') } document.getElementById('go-back-action').onclick = () => { sendAction('didGoBack') } })() ``` -------------------------------- ### Preferences.Shields — Global Shield Settings Source: https://context7.com/brave/brave-ios/llms.txt Provides access to global Brave Shields settings through the Preferences.Shields namespace. Each shield option is a typed UserDefaults entry that can be read, written, or reset. ```APIDOC ## Preferences.Shields — Global Shield Settings `Preferences.Shields` is a namespace of `Option` values controlling Brave Shields behavior globally across all tabs. Each option persists to a keyed `UserDefaults` entry and can be read by any module that imports `Preferences`. ```swift import Preferences // Available shield options and their defaults Preferences.Shields.httpsEverywhere.value // Bool, default: true Preferences.Shields.googleSafeBrowsing.value // Bool, default: true Preferences.Shields.blockScripts.value // Bool, default: false Preferences.Shields.fingerprintingProtection.value // Bool, default: true Preferences.Shields.autoRedirectAMPPages.value // Bool, default: true Preferences.Shields.autoRedirectTrackingURLs.value // Bool, default: true Preferences.Shields.blockImages.value // Bool, default: false Preferences.Shields.useRegionAdBlock.value // Bool, default: true // Enable aggressive mode for all shields Preferences.Shields.httpsEverywhere.value = true Preferences.Shields.fingerprintingProtection.value = true Preferences.Shields.blockScripts.value = true Preferences.Shields.blockImages.value = false // Iterate all shields to reset them for shield in Preferences.Shields.allShields { shield.reset() } ``` ``` -------------------------------- ### Implement Reader Mode for Article Rendering Source: https://context7.com/brave/brave-ios/llms.txt Instantiate and register ReaderModeHandler with WKWebViewConfiguration to serve cached Readability-processed articles. Use the 'page-exists' sub-path to check availability without rendering. ```swift // ReaderModeHandler is instantiated with a Profile and registered // on the WKWebViewConfiguration at app startup: let handler = ReaderModeHandler(profile: profile) // Registered at InternalURL.Path.readermode (e.g. "reader-mode") // The browser checks availability via the "page-exists" sub-path: // GET internal://local/reader-mode/page-exists?url=https%3A%2F%2Farticle.com // → HTTP 200 if cached, HTTP 400 if not // When the user taps the reader-mode button the browser navigates to: // internal://local/reader-mode?url=https%3A%2F%2Farticle.com // The handler retrieves the ReadabilityResult from DiskReaderModeCache // and renders it as a styled HTML page. // Inject into a WKWebViewConfiguration (as done in BrowserViewController): let config = WKWebViewConfiguration() InternalSchemeHandler.register(handler, forPath: ReaderModeHandler.path) ``` -------------------------------- ### Copying Brave Core xcframeworks to brave-ios Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md After building BraveCore, use this script to copy the generated xcframeworks into the brave-ios project's node_modules directory. ```shell build_in_core.sh ~/path/to/brave-browser ``` -------------------------------- ### Event Listener for More Details Button Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Attaches a click event listener to the 'moreDetailsButton' to toggle details visibility. ```javascript var moreDetailsButton = document.getElementById("moreDetailsButton"); if (moreDetailsButton != null) { moreDetailsButton.addEventListener('click', function(e) { e.preventDefault(); toggleDetails(moreDetailsButton); }); } ``` -------------------------------- ### NetworkManager: Fetch HTTP Resources with ETag Caching Source: https://context7.com/brave/brave-ios/llms.txt Use NetworkManager to fetch HTTP resources with ETag caching, automatic retries, and Last-Modified timestamp extraction. Supports closure, Combine, and async/await interfaces. ```swift import Foundation let manager = NetworkManager() let url = URL(string: "https://cdn.brave.com/filters/brave-lists.txt")! // Async/await — no ETag caching let (data, response) = try await manager.dataRequest(with: url) print(data.count, response) // Async/await — ETag cached download (returns CachedNetworkResource) let resource = try await manager.downloadResource( with: url, resourceType: .cached(etag: "\"abc123\""), retryTimeout: 30, checkLastServerSideModification: true ) print(resource.data.count) print(resource.etag ?? "no etag") print(resource.lastModifiedTimestamp ?? 0) ``` ```swift // Combine publisher with retry import Combine var cancellable: AnyCancellable? cancellable = manager.downloadResource(with: url, resourceType: .regular, retryTimeout: 60) .sink( receiveCompletion: { result in if case .failure(let error) = result { print("Download failed:", error) // NetworkManagerError.fileNotModified if 304 } }, receiveValue: { resource in print("Downloaded \(resource.data.count) bytes") } ) ``` -------------------------------- ### Check Effective Shield Levels for a Domain Source: https://context7.com/brave/brave-ios/llms.txt Check the effective ad-block and fingerprint protection levels for a specific domain. Also checks if all shields are disabled for the domain. Requires main actor context. ```swift // Check effective ad-block level (main actor required) Task { @MainActor in let level: ShieldLevel = domain.blockAdsAndTrackingLevel // → .aggressive for youtube.com, .standard for others (when global is .standard) print(level) // Check fingerprint protection level let fpLevel = domain.finterprintProtectionLevel // → .standard or .disabled // Check if all shields are disabled for this domain let allOff: Bool = domain.areAllShieldsOff print(allOff) // → false by default } ``` -------------------------------- ### Favorite: CRUD Operations for Bookmarks Source: https://context7.com/brave/brave-ios/llms.txt Perform Create, Read, Update, and Delete operations on the Favorite Core Data entity. All operations are dispatched through DataController.perform. ```swift import Data // Add a single favorite Favorite.add(url: URL(string: "https://brave.com")!, title: "Brave") // Add multiple favorites at once (batch insert) let list: [(url: URL, title: String)] = [ (URL(string: "https://search.brave.com")!, "Brave Search"), (URL(string: "https://talk.brave.com")!, "Brave Talk"), ] Favorite.add(from: list) // Fetch all favorites (on main context) let allFavorites = Favorite.allFavorites(context: DataController.shared.viewContext) for fav in allFavorites { print(fav.title ?? "", fav.url ?? "") } // Delete a favorite if let toDelete = allFavorites.first { Favorite.remove(toDelete) } // Reorder favorites Favorite.reorder( sourceIndexPath: IndexPath(item: 0, section: 0), destinationIndexPath: IndexPath(item: 2, section: 0), isInteractiveDragReorder: true ) ``` -------------------------------- ### Linting Brave Core Code Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Run the linter on the Brave Core project to check for code style and potential errors. This is a required step before PR submission. ```shell npm run lint ``` -------------------------------- ### Building Brave Core for iOS (Debug) Source: https://github.com/brave/brave-ios/blob/development/BraveCore/Working with BraveCore.md Compile a debug build of BraveCore for iOS. This is a common build type for development and testing. ```shell npm run build -- Debug --target_os=ios ``` -------------------------------- ### Row with Selectable Detail Button Accessory Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Implement row selection handling by providing a closure to the `.detailButton` accessory case. This is useful for accessories like the info button that trigger an action. ```swift Row(text: "Sam Soffes", accessory: .detailButton({ // Show info about this contact })) ``` -------------------------------- ### JavaScript Session Restore Implementation Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/SessionRestore.html This JavaScript code implements the session restore logic. It reconstructs the URL list from the current page's query parameters and uses `history.replaceState` and `history.pushState` to update the browser's history stack. A `setTimeout` is used to ensure the reload occurs after the history state is updated, and another `setTimeout` with a delay of 0 triggers a message to the native layer. ```javascript (function () { const uuidKeyValue = "%INSERT_UUID_VALUE%" const uuidKeyName = "uuidkey" const sessionRestoreUrl = "internal://local/sessionrestore" const errorPageUrl = "internal://local/errorpage" function getRestoreURL(urlString) { const url = new URL(urlString); if (urlString.startsWith(sessionRestoreUrl) || urlString.startsWith(errorPageUrl)) { url.searchParams.set(uuidKeyName, uuidKeyValue); if (urlString.startsWith(errorPageUrl)) { url.searchParams.delete("timestamp"); } return url.toString(); } const wrappedUrl = new URL(sessionRestoreUrl); wrappedUrl.searchParams.set("url", urlString); wrappedUrl.searchParams.set(uuidKeyName, uuidKeyValue); return wrappedUrl.toString(); } const uuidSuffix = "&" + uuidKeyName + "=" + uuidKeyValue const pageUrl = location.href.substr(0, location.href.lastIndexOf(uuidSuffix)); var index = pageUrl.search("history"); var sessionRestoreComponents = JSON.parse(unescape(pageUrl.substring(index + "history=".length))); var urlList = sessionRestoreComponents['history']; var currentPage = sessionRestoreComponents['currentPage']; history.replaceState({}, "", getRestoreURL(urlList[0])); for (var i = 1; i < urlList.length; i++) { history.pushState({}, '', getRestoreURL(urlList[i])); } history.go(currentPage); setTimeout(() => { if (currentPage !== 0) { location.reload(); } }); setTimeout(function () { webkit.messageHandlers.sessionRestoreHelper.postMessage({ "securityToken": "%security_token%", name: "didRestoreSession" }); }, 0); }) (); ``` -------------------------------- ### Reader View Loading JavaScript Logic Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Frontend/Reader/ReaderViewLoading.html Manages the loading process for the reader view by repeatedly checking for content availability. It includes a timeout mechanism to prevent infinite loops and displays a failure message if content cannot be loaded. ```javascript var numberOfChecks = 20; // 20 * 500ms = 10 seconds total function triggerCheck() { if (numberOfChecks--) { setTimeout(function() { checkIfContentIsAvailable(); }, 500); } else { var message = document.getElementById("message") if (message != null) { message.innerText = "%LOADING-FAILED-TEXT%"; } var link = document.getElementById("link") if (link != null) { link.style.visibility = "visible"; } } } function checkIfContentIsAvailable() { var request = new XMLHttpRequest(); request.open("GET", "/reader-mode/page-exists" + document.location.search, true); request.onload = function() { if (request.status == 200) { window.location.replace("%READER-URL%"); } else { triggerCheck(); } }; request.onerror = function() { triggerCheck(); }; request.send(); } triggerCheck(); ``` -------------------------------- ### Typed UserDefaults Wrapper with Preferences.Option Source: https://context7.com/brave/brave-ios/llms.txt Use Preferences.Option to create strongly-typed, observable wrappers around UserDefaults entries. Reading or writing `.value` persists changes and notifies observers. Useful for reactive UI binding with Combine. ```swift import Preferences // Read a preference value let isHTTPSEverywhereOn: Bool = Preferences.Shields.httpsEverywhere.value // → true (default) // Write a preference value — persists immediately to UserDefaults Preferences.Shields.blockScripts.value = true // Observe changes (class must conform to PreferencesObserver) class MyObserver: PreferencesObserver { func preferencesDidChange(for key: String) { print("Changed key:", key) // → "shields.block-scripts" } } let observer = MyObserver() Preferences.Shields.blockScripts.observe(from: observer) // Reset to default Preferences.Shields.blockScripts.reset() // Preferences.Shields.blockScripts.value == false // Combine binding in SwiftUI import Combine var cancellable: AnyCancellable? cancellable = Preferences.Shields.fingerprintingProtection.$value .sink { enabled in print("Fingerprint protection:", enabled) } ``` -------------------------------- ### Handle IPFS Button Clicks Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/IPFSPreference.html Attaches event listeners to the 'disable' and 'proceed' buttons to send messages to the webkit handler. Use this to manage user interactions with IPFS settings. ```javascript var disableButton = document.getElementById("disableIPFSButton") disableButton.addEventListener('click', function(e) { e.preventDefault(); webkit.messageHandlers["%message_handler%"].postMessage({ "type": "IPFSDisable" }); }); var proceedButton = document.getElementById("proceedIPFSButton") proceedButton.addEventListener('click', function(e) { e.preventDefault(); webkit.messageHandlers["%message_handler%"].postMessage({ "type": "IPFSProceed" }); }); ``` -------------------------------- ### Global Brave Shields Settings with Preferences.Shields Source: https://context7.com/brave/brave-ios/llms.txt Access and modify global Brave Shields settings using the Preferences.Shields namespace. Each option is a typed UserDefaults entry that can be read by any module importing Preferences. ```swift import Preferences // Available shield options and their defaults Preferences.Shields.httpsEverywhere.value // Bool, default: true Preferences.Shields.googleSafeBrowsing.value // Bool, default: true Preferences.Shields.blockScripts.value // Bool, default: false Preferences.Shields.fingerprintingProtection.value // Bool, default: true Preferences.Shields.autoRedirectAMPPages.value // Bool, default: true Preferences.Shields.autoRedirectTrackingURLs.value // Bool, default: true Preferences.Shields.blockImages.value // Bool, default: false Preferences.Shields.useRegionAdBlock.value // Bool, default: true // Enable aggressive mode for all shields Preferences.Shields.httpsEverywhere.value = true Preferences.Shields.fingerprintingProtection.value = true Preferences.Shields.blockScripts.value = true Preferences.Shields.blockImages.value = false // Iterate all shields to reset them for shield in Preferences.Shields.allShields { shield.reset() } ``` -------------------------------- ### Load URL and Manage BraveWebView Data Source: https://context7.com/brave/brave-ios/llms.txt Load a URL in a BraveWebView and clear the shared non-persistent store when necessary. The non-persistent store is used for private browsing tabs. ```swift // Load a URL privateWebView.load(URLRequest(url: URL(string: "https://brave.com")!)) // Clear the shared non-persistent store (e.g. on all private tabs closed) BraveWebView.removeNonPersistentStore() ``` -------------------------------- ### Favorite - CRUD Operations Source: https://context7.com/brave/brave-ios/llms.txt Provides class-level methods for managing browser favorites (bookmarks), including adding, fetching, deleting, and reordering. ```APIDOC ## Favorite - CRUD Operations ### Description Provides class-level methods for managing browser favorites (bookmarks), including adding, fetching, deleting, and reordering. ### Methods - `add(url: URL, title: String)`: Adds a single favorite. - `add(from: [(url: URL, title: String)])`: Adds multiple favorites in a batch. - `allFavorites(context:)`: Fetches all favorites from a given context. - `remove(Favorite)`: Deletes a specific favorite. - `reorder(sourceIndexPath:destinationIndexPath:isInteractiveDragReorder:)`: Reorders favorites. ### Parameters - **url** (URL) - The URL of the favorite. - **title** (String) - The title of the favorite. - **list** ([(url: URL, title: String)]) - An array of tuples for batch adding. - **context** (NSManagedObjectContext) - The Core Data context to use for fetching. - **favorite** (Favorite) - The favorite object to remove. - **sourceIndexPath** (IndexPath) - The starting index path for reordering. - **destinationIndexPath** (IndexPath) - The destination index path for reordering. - **isInteractiveDragReorder** (Bool) - Flag indicating if the reorder is interactive. ### Request Example ```swift // Add a single favorite Favorite.add(url: URL(string: "https://brave.com")!, title: "Brave") // Add multiple favorites at once let list: [(url: URL, title: String)] = [ (URL(string: "https://search.brave.com")!, "Brave Search"), (URL(string: "https://talk.brave.com")!, "Brave Talk"), ] Favorite.add(from: list) // Fetch all favorites let allFavorites = Favorite.allFavorites(context: DataController.shared.viewContext) for fav in allFavorites { print(fav.title ?? "", fav.url ?? "") } // Delete a favorite if let toDelete = allFavorites.first { Favorite.remove(toDelete) } // Reorder favorites Favorite.reorder( sourceIndexPath: IndexPath(item: 0, section: 0), destinationIndexPath: IndexPath(item: 2, section: 0), isInteractiveDragReorder: true ) ``` ### Response N/A (Operations modify the data store and do not return specific values in this context, except for `allFavorites` which returns an array of `Favorite` objects). ``` -------------------------------- ### Conditionally Register FingerprintingProtection Script Source: https://context7.com/brave/brave-ios/llms.txt Conditionally register the FingerprintingProtection user script with a WKWebViewConfiguration if the global preference for FpProtection is enabled. This ensures the script is only added when the feature is active. ```swift // Check whether the shield is active before registering the script: if BraveShield.FpProtection.globalPreference { let config = WKWebViewConfiguration() if let script = FingerprintingProtection.userScript { config.userContentController.addUserScript(script) } } ``` -------------------------------- ### Add New Localized String in Swift Source: https://github.com/brave/brave-ios/blob/development/App/l10n/tools/README.md Define a new localized string within the Strings.swift file for the en_US locale. Comments are crucial for providing context to translators. ```swift extension Strings { public static let someString = NSLocalizedString("SomeString", value: "Some String", comment: "Comments are used to bring context to the string to aide others") } ``` -------------------------------- ### Check BraveShield Global Preferences Source: https://context7.com/brave/brave-ios/llms.txt Access the global preference for each shield type using the BraveShield enum. Note that AllOff.globalPreference is always false. ```swift import BraveShields // Check global preference for each shield type BraveShield.AdblockAndTp.globalPreference // ShieldPreferences.blockAdsAndTrackingLevel.isEnabled BraveShield.FpProtection.globalPreference // Preferences.Shields.fingerprintingProtection.value BraveShield.NoScript.globalPreference // Preferences.Shields.blockScripts.value BraveShield.AllOff.globalPreference // always false (disables all shields) // Typical usage — check before injecting content scripts if BraveShield.FpProtection.globalPreference { // inject FingerprintingProtection WKUserScript } if BraveShield.NoScript.globalPreference { // block script execution for this tab } ``` -------------------------------- ### Row with Custom View Accessory Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Assign a custom view as a row accessory using the `.view` case. Ensure the custom view is properly sized before assignment, as its bounds will determine the row's height. ```swift Row(text: "My Profile", accessory: .view(someEditButton)) ``` -------------------------------- ### Parse Brave Deep Links with NavigationPath Source: https://context7.com/brave/brave-ios/llms.txt Parse various Brave deep link URL schemes using the NavigationPath enum. This includes handling open-url, search, and widget shortcut requests. ```swift import Shared // Parse an external open-URL request let url = URL(string: "brave://open-url?url=https%3A%2F%2Fbrave.com&private=true")! if let path = NavigationPath(url: url, isPrivateBrowsing: false) { switch path { case .url(let webURL, let isPrivate): print(webURL!) // → https://brave.com print(isPrivate) // → true default: break } } // Parse a search query let searchURL = URL(string: "brave://search?q=privacy+browser")! if let path = NavigationPath(url: searchURL, isPrivateBrowsing: false) { switch path { case .text(let query): print(query) // → "privacy browser" default: break } } // Parse a widget shortcut let shortcutURL = URL(string: "brave://shortcut?path=0")! if let path = NavigationPath(url: shortcutURL, isPrivateBrowsing: false) { switch path { case .widgetShortcutURL(let shortcut): print(shortcut) // → WidgetShortcut(rawValue: 0) default: break } } ``` -------------------------------- ### Access BraveGlobalShieldStats Counters Source: https://context7.com/brave/brave-ios/llms.txt Read cumulative counters for ads blocked, trackers blocked, scripts, and fingerprinting attempts from BraveGlobalShieldStats.shared. These counters are persisted via Preferences.BlockStats. ```swift import BraveShields let stats = BraveGlobalShieldStats.shared // Read counters print(stats.adblock) // e.g. 1024 print(stats.trackingProtection) // e.g. 512 print(stats.fpProtection) // e.g. 87 // Increment (done internally by content scripts) stats.adblock += 1 stats.fpProtection += 1 // Human-readable summaries print(stats.timeSaved) // → "1 min", "2 hr", "3 days", etc. print(stats.dataSaved) // → "45 MB", "1.2 GB", etc. ``` -------------------------------- ### Toggle Details Visibility Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Toggles the display of detailed information and updates the button text and arrow direction. ```javascript function toggleDetails(button) { var element = document.getElementById("details"); var arrow = document.getElementById("arrow"); if (element.style.display === "none") { element.style.display = "block"; arrow.classList.replace("down", "up"); button.firstChild.data = "%hide_details%"; } else { element.style.display = "none"; arrow.classList.replace("up", "down"); button.firstChild.data = "%more_details%"; } } ``` -------------------------------- ### Conditional Removal of Proceed Anyway Link Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Removes the 'proceedAnyway' link if bypass is not allowed. ```javascript if (!%allow_bypass% && proceedAnywayLink != null) { proceedAnywayLink.remove(); } ``` -------------------------------- ### Section with Custom View Header Source: https://github.com/brave/brave-ios/blob/development/ThirdParty/Static/Readme.markdown Provide a custom view for a section header using the `.view` case of the `Section.Extremity` enum. The height of the header will be determined by the view's bounds. ```swift Section(header: .view(yourView)) ``` -------------------------------- ### Event Listener for Back to Safety or Reload Button Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Attaches a click event listener to 'backToSafetyOrReloadButton' to either navigate back or reload the page based on bypass allowance. ```javascript var backToSafetyOrReloadButton = document.getElementById("backToSafetyOrReloadButton"); if (backToSafetyOrReloadButton != null) { backToSafetyOrReloadButton.addEventListener('click', function(e) { if (%allow_bypass%) { history.back(); } else { location.reload(); } }); } ``` -------------------------------- ### Conditional UI Rendering based on Certificate Status Source: https://github.com/brave/brave-ios/blob/development/Sources/Brave/Assets/Interstitial Pages/Pages/CertificateError.html Adjusts the UI by showing or hiding warning/error messages and navigation buttons based on whether a certificate is present and if bypass is allowed. ```javascript var has_a_certificate = "%has_certificate%".toLowerCase() === "true"; if (has_a_certificate) { // If we have a certificate, show the warning var element = document.getElementById("descriptionWarning"); if (element != null) { element.style.display = "block"; } // Remove the error element = document.getElementById("descriptionError"); if (element != null) { element.remove(); } } else { // We don't have a certificate, so remove the warning var element = document.getElementById("descriptionWarning"); if (element != null) { element.remove(); } // Show the error element = document.getElementById("descriptionError"); if (element != null) { element.style.display = "block"; } // Remove navigation buttons element = document.getElementById("navigationButtons"); if (element) { element.remove(); } // Remove details element = document.getElementById("details"); if (element) { element.remove(); } } ``` -------------------------------- ### Enable and Use Find-in-Page in BraveWebView Source: https://context7.com/brave/brave-ios/llms.txt Initiate the Find-in-Page interaction on BraveWebView for iOS 16 and later. The `isFindInteractionEnabled` property is set to true by default. ```swift // Find in page (iOS 16+) // isFindInteractionEnabled = true is set automatically in init if #available(iOS 16, *) { privateWebView.findInteraction?.presentFindNavigator(showingReplace: false) } ``` -------------------------------- ### ShieldPreferences — Ad-Block & Tracking Level Source: https://context7.com/brave/brave-ios/llms.txt Manages the global ad/tracker blocking protection level and the Global Privacy Control (GPC) setting. Allows setting the level to standard, aggressive, or disabled, and toggling GPC. ```APIDOC ## ShieldPreferences — Ad-Block & Tracking Level `ShieldPreferences` manages the global ad/tracker blocking protection level (`.standard`, `.aggressive`, or `.disabled`) and the Global Privacy Control (GPC) setting. The raw string value is persisted in `UserDefaults` and decoded to the `ShieldLevel` enum. ```swift import BraveShields // Read current level let level: ShieldLevel = ShieldPreferences.blockAdsAndTrackingLevel // → .standard (default) // Set to aggressive blocking ShieldPreferences.blockAdsAndTrackingLevel = .aggressive // Set to disabled ShieldPreferences.blockAdsAndTrackingLevel = .disabled // Enable Global Privacy Control header ShieldPreferences.enableGPC.value = true // Check if ads/tracking are blocked print(ShieldPreferences.blockAdsAndTrackingLevel.isEnabled) // → true for .standard or .aggressive, false for .disabled ``` ```