2025 System Services
WWDC25 · 28 min · System Services
Filter and tunnel network traffic with NetworkExtension
Learn about the APIs in the NetworkExtension framework that give your app the power and flexibility to extend the system’s core networking features — like implementing network content filters, creating and managing VPN configurations, and more. In iOS, iPadOS and macOS 26, you can now build robust content filters that make traffic decisions using the entire URL — not just the hostname — all without compromising privacy and security. We’ll start by briefly covering many of the key use cases for the NetworkExtension framework, including network relays and VPN. Then, we’ll dive into the new URL filter API and its key components, including Private Information Retrieval, Privacy Pass, and more.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 3 snippets
Use participation API to check URLs before sending requests
// Use participation API to check URLs before sending requests
import NetworkExtension
func checkURL(url: URL) async throws -> Bool {
var passRequest : Bool = true
let verdict = await NEURLFilter.verdict(for: url)
if verdict == .deny {
passRequest = false
}
return passRequest
} Configure and manage URL Filter
// Configure and manage URL Filter
import NetworkExtension
let manager = NEURLFilterManager.shared
try await manager.loadFromPreferences()
try manager.setConfiguration(
pirServerURL: URL(string:"https://pir.example.com")!,
pirPrivacyPassIssuerURL: URL(string:"https://privacypass.example.com")!,
pirAuthenticationToken: "1234",
controlProviderBundleIdentifier: "com.example.myURLFilter.extension")
manager.prefilterFetchInterval = 86400 // fetch every 1 day
manager.shouldFailClosed = false
manager.localizedDescription = "Alice's URL Filter"
manager.isEnabled = true
try await manager.saveToPreferences() Implement NEURLFilterControlProvider protocol
// Implement NEURLFilterControlProvider protocol
import NetworkExtension
class URLFilterControlProvider: NEURLFilterControlProvider {
func fetchPrefilter() async throws -> NEURLFilterPrefilter? {
// Fetch your Bloom filters data from your app bundle or from your server
let data = NEURLFilterPrefilter.PrefilterData.temporaryFilepath(fileURL)
let result = NEURLFilterPrefilter(data: data,
bitCount: numberOfBits,
hashCount: numberOfHashes,
murmurSeed: murmurSeed)
return result
}
} Resources
Related sessions
-
39 min -
26 min