Skip to main content
Network extensions provide VPN, content filtering, DNS proxy, and app proxy capabilities to your app.

Network extension types

Expo Apple Targets supports all four network extension types:

Packet Tunnel

VPN and custom network protocols

App Proxy

Per-app VPN and network filtering

DNS Proxy

Custom DNS resolution

Filter Data

Content filtering and parental controls
Network extensions require the Network Extensions entitlement from Apple. You must request this capability from Apple Developer Support before using these extension types.

Packet Tunnel Provider

Creates VPN tunnels and custom network protocols.

Extension point

PropertyValue
Typenetwork-packet-tunnel
Extension Pointcom.apple.networkextension.packet-tunnel
FrameworksNetworkExtension
Embedded SwiftYes

Creating a packet tunnel extension

npx create-target network-packet-tunnel

Implementation

import NetworkExtension
import os

class PacketTunnelProvider: NEPacketTunnelProvider {
    
    override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
        // Configure tunnel settings
        let tunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "10.0.0.1")
        
        // Set IPv4 settings
        let ipv4Settings = NEIPv4Settings(
            addresses: ["10.0.0.2"],
            subnetMasks: ["255.255.255.0"]
        )
        ipv4Settings.includedRoutes = [NEIPv4Route.default()]
        tunnelNetworkSettings.ipv4Settings = ipv4Settings
        
        // Set DNS settings
        let dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "8.8.4.4"])
        tunnelNetworkSettings.dnsSettings = dnsSettings
        
        // Apply settings
        setTunnelNetworkSettings(tunnelNetworkSettings) { error in
            if let error = error {
                completionHandler(error)
                return
            }
            
            // Start reading packets
            self.startReadingPackets()
            completionHandler(nil)
        }
    }
    
    override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        // Clean up tunnel
        completionHandler()
    }
    
    private func startReadingPackets() {
        packetFlow.readPackets { packets, protocols in
            // Process incoming packets
            for (index, packet) in packets.enumerated() {
                let protocolNumber = protocols[index]
                // Handle packet based on protocol (IPv4, IPv6, etc.)
                self.handlePacket(packet, protocol: protocolNumber)
            }
            
            // Continue reading
            self.startReadingPackets()
        }
    }
    
    private func handlePacket(_ packet: Data, protocol protocolNumber: NSNumber) {
        // Process and forward packet
        // Send outgoing packets with:
        // packetFlow.writePackets([packet], withProtocols: [protocolNumber])
    }
}

Starting the VPN

From your React Native app (requires native module):
import NetworkExtension

func startVPN() {
    let manager = NETunnelProviderManager()
    
    manager.loadFromPreferences { error in
        if error != nil {
            // Create new configuration
            let proto = NETunnelProviderProtocol()
            proto.providerBundleIdentifier = "com.myapp.network-packet-tunnel"
            proto.serverAddress = "VPN Server"
            
            manager.protocolConfiguration = proto
            manager.localizedDescription = "My VPN"
            manager.isEnabled = true
            
            manager.saveToPreferences { error in
                if error == nil {
                    try? manager.connection.startVPNTunnel()
                }
            }
        } else {
            // Start existing VPN
            try? manager.connection.startVPNTunnel()
        }
    }
}

App Proxy Provider

App-level VPN that only affects specific apps.

Extension point

PropertyValue
Typenetwork-app-proxy
Extension Pointcom.apple.networkextension.app-proxy
FrameworksNetworkExtension
Embedded SwiftYes
npx create-target network-app-proxy
Implementation is similar to packet tunnel but uses NEAppProxyProvider base class.

DNS Proxy Provider

Custom DNS resolution for all network traffic.

Extension point

PropertyValue
Typenetwork-dns-proxy
Extension Pointcom.apple.networkextension.dns-proxy
FrameworksNetworkExtension
Embedded SwiftYes
npx create-target network-dns-proxy

Implementation

import NetworkExtension

class DNSProxyProvider: NEDNSProxyProvider {
    
    override func startProxy(options: [String : Any]?, completionHandler: @escaping (Error?) -> Void) {
        // Configure DNS proxy
        let settings = NEDNSProxyProviderSettings()
        settings.dnsServers = ["8.8.8.8", "1.1.1.1"]
        
        setProxyConfiguration(settings) { error in
            completionHandler(error)
        }
    }
    
    override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        completionHandler()
    }
    
    override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        if let udpFlow = flow as? NEAppProxyUDPFlow {
            // Handle DNS query
            handleDNSQuery(udpFlow)
            return true
        }
        return false
    }
    
    private func handleDNSQuery(_ flow: NEAppProxyUDPFlow) {
        flow.readDatagrams { datagrams, endpoints, error in
            guard let query = datagrams?.first else { return }
            
            // Parse DNS query
            // Resolve query (custom logic or forward to upstream DNS)
            // Write response
            let response = self.resolveDNS(query)
            flow.writeDatagrams([response], sentBy: endpoints ?? []) { error in
                // Continue reading
                self.handleDNSQuery(flow)
            }
        }
    }
    
    private func resolveDNS(_ query: Data) -> Data {
        // Implement DNS resolution logic
        return query // Placeholder
    }
}

Filter Data Provider

Content filtering and parental controls.

Extension point

PropertyValue
Typenetwork-filter-data
Extension Pointcom.apple.networkextension.filter-data
FrameworksNetworkExtension
Embedded SwiftYes
npx create-target network-filter-data

Implementation

import NetworkExtension

class FilterDataProvider: NEFilterDataProvider {
    
    override func startFilter(completionHandler: @escaping (Error?) -> Void) {
        // Initialize filter
        completionHandler(nil)
    }
    
    override func stopFilter(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        completionHandler()
    }
    
    override func handleNewFlow(_ flow: NEFilterFlow) -> NEFilterNewFlowVerdict {
        guard let socketFlow = flow as? NEFilterSocketFlow else {
            return .allow()
        }
        
        // Check if URL should be blocked
        if let url = socketFlow.url?.absoluteString {
            if shouldBlock(url) {
                return .drop()
            }
        }
        
        // Allow and monitor data
        return .filterDataVerdict(
            withFilterInbound: true,
            peekInboundBytes: 1024,
            filterOutbound: true,
            peekOutboundBytes: 1024
        )
    }
    
    override func handleInboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
        // Inspect inbound data
        if containsBlockedContent(readBytes) {
            return .drop()
        }
        return .allow()
    }
    
    override func handleOutboundData(from flow: NEFilterFlow, readBytesStartOffset offset: Int, readBytes: Data) -> NEFilterDataVerdict {
        // Inspect outbound data
        return .allow()
    }
    
    private func shouldBlock(_ url: String) -> Bool {
        // Implement blocking logic
        let blocklist = ["example.com", "blocked.com"]
        return blocklist.contains(where: { url.contains($0) })
    }
    
    private func containsBlockedContent(_ data: Data) -> Bool {
        // Check for blocked keywords or patterns
        return false
    }
}

Required entitlements

All network extensions require special entitlements:
<key>com.apple.developer.networking.networkextension</key>
<array>
  <string>packet-tunnel-provider</string>
  <!-- or app-proxy, dns-proxy, content-filter-provider -->
</array>
You must request the Network Extensions capability from Apple Developer Support. This is not automatically granted.

Testing network extensions

1

Build the extension

Build your network extension target in Xcode.
2

Install on device

Network extensions must be tested on a real device - they don’t work in the simulator.
3

Trust the VPN configuration

When starting the VPN for the first time, you’ll need to allow the VPN configuration in Settings.
4

Monitor system logs

Use Console.app to view network extension logs.

Best practices

  • Always call completion handlers
  • Log errors for debugging
  • Implement proper error recovery
  • Test network failure scenarios
  • Minimize packet processing overhead
  • Use efficient data structures
  • Avoid blocking operations
  • Monitor memory usage
  • Validate all external data
  • Use secure connections for VPN servers
  • Implement proper authentication
  • Don’t log sensitive information

Learn more

Entitlements

Configure network entitlements

Target config

Configure network extensions

Complete target list

All extension types

Apple documentation

Official NetworkExtension documentation

Build docs developers (and LLMs) love