I'm looking at implementing an iOS app that has includes a Content Filter Provider to block access to certain domains when accessed on the device.
This uses NEFilterManager, NEFilterDataProvider and NEFilterControlProvider to handle configuration and manage the network flows and block as necessary.
My question is can you deploy this in an iOS 18+ app on the App Store to devices which are unmanaged, unsupervised and don't use Screen Time APIs?
Although not 100% clear, this technote seems to say it is not possible:
https://developer.apple.com/documentation/Technotes/tn3134-network-extension-provider-deployment
Testing this on a Developer device and build works successfully without any MDM profiles installed.
A similar approach using the same APIs also works on macOS once user permissions have been given.
If it can't work on unsupervised, unmanaged iOS devices, is possible for the user to first manually install a MDM profile which includes the required 'Content Filter' details and then have it work?
If not, how would you filter iOS network traffic on an unmanaged, unsupervised device?
Is it necessary to use a VPN or DNS approach instead (which may be a lot less privacy compliant)?
Networking
RSS for tagExplore the networking protocols and technologies used by the device to connect to Wi-Fi networks, Bluetooth devices, and cellular data services.
Post
Replies
Boosts
Views
Activity
How can we advertise custom data through our iOS app using Bluetooth Low Energy advertisement?
in the local testing, if the binary data length is small like 300 bytes, then it can send binary data success, but if the data length is more that 3000 bytes, then it will failed and print the error like in the screen capture
I am developing a VoIP service.
Usually, when receiving a VoIP Push, Callkit is exposed immediately after receiving the message and the app is designed to be used.
However, there is an extremely intermittent phenomenon (not well reproduced) where the app does not wake up even when receiving a VoIP Push. And after a long time, the app wakes up and Callkit is activated. (A long time after receiving the call…)
Has anyone experienced the above phenomenon? I wonder if there are any reported parts depending on the OS version. (I have identified that it does not occur in the 17.x version, but it is difficult to guarantee because it occurs extremely intermittently)
The app is not running in the background, but... Could this be happening if there are a lot of pending operations in the background?
I need help urgently
I am learning about layer 3 VPN implementations for MacOS, and am slowly making my way through docs and tutorials. I noticed that part of creating an instance of NETunnelProviderManager on the app side of the project requires the specification of protocolConfiguration via an instance of NETunnelProviderProtocol. One of the arguments for this class is serverAdress, which to my understanding, tells the OS where to route traffic towards at the end of the day.
My question: many VPNs these days allow the option to specify the location for which you want your traffic to be routed through. I imagine this would necessitate changing this serverAddress field in the backend. However, setting this option (on a commercially available VPN) doesn't typically prompt the OS notification that you get when initially installing a VPN configuration for the first time. How is this functionality achieved? I could see one possible solution being that most VPN providers route through a main service beforehand (so the first IP in the chain never has to change), though I could see this being problematic for a number of other reasons.
Assuming you have a valid NETunnelProviderManager object called manager, is this valid?
self.manager?.protocolConfiguration?.serverAddress = "somewhereElse"
Even if it compiles, will the traffic be properly re-routed?
My understanding of the flow right now is that in order to "lock in" a new configuration, or modify it, you need to call manager.saveToPreferences, which triggers the OS notification I mentioned earlier.
hello
I am testing the use of network extension. When we use dnsproxy to proxy DNS requests,
we will send you a message that the udp pcbcount of your system continues to increase.
For example
for ((i=1; i<=99999; i++));do
echo "Attempt $i:"
dig google.com
done
when the dig command is used continuously,
the dig command will show the following errors when pcbcount reaches a certain number.
isc_socket_bind: address not available
Can you help us determine what the problem might be? thank you
I have an app that is communicating with a non-HTTP server over TCP/IP. Most everything is working, but I was testing some error conditions and the first one I tried was turning the server off and then trying to send it a message.
I'm using code that uses NWConnection and involves an async method that includes a withCheckedContinuation. Inside this code are checks for errors in the closures, etc. You've seen the example code posted here in the forums. But none of the error code ever gets invoked. I also have a state handler to check the state of the TCP connection.
What I see when I send the request is:
connection goes to .preparing state
nothing happens for about 45 seconds
I then get two errors:
inline-code
nw_endpoint_flow_failed_with_error [C4.1.1 192.168.86.44:3040 in_progress channel-flow (satisfied (Path is satisfied), viable, interface: en0[802.11], ipv4, dns, uses wifi)] already failing, returning
inline-code
nw_endpoint_flow_failed_with_error [C4.1.1 192.168.86.44:3040 cancelled channel-flow ((null))] already failing, returning
then the connection state goes to .waiting
and nothing else happens. I would really like to capture the errors I see in the Xcode console, but I don't know how to catch them. Anyone have any ideas? Is there a better way to send the first message (or a ping or whatever) to a non-HTTP server and see if it is there?
Thanks,
Robert
Hi folks,
I would like to ask for clarification regarding Local Network Policy. I found 2 cases where I think the behaviour differs from the documentation.
1. Use case
In a CI environment, we have multiple services (LaunchAgents) which require Local Network Access. We are fine by manually approving the Local Network Permission once (per service), but we also require these services to be able to self-update. Self update results in downloading the a binary with an (obviously) different UUID, which seems to result in re-triggering the Local Network Consent prompt. Strange thing: If I don't click either buttons (Allow of Reject), just restart macOS, it will result in an enabled entry in Privacy & Security > Local Network.
I read a reply somewhere on this forum by an Apple engineer that the approval process is a mix of Bundle ID + UUID + other components, so I would expect a new binary with the same properties (but different UUID) to be already whitelisted.
Is this behaviour intended?
2. Use Case
Given the first issue, I decided to do this in the "right way".
I was happy to read this sentence in the documentation:
If you ship a launchd agent that’s not installed using SMAppService, make macOS aware of the responsible code by setting the AssociatedBundleIdentifiers property in your launchd property list.
I have a properly setup (and code signed) Application, for which I have enabled Local Network permission in Privacy & Security.
I have a standalone LaunchAgent, which runs a long running binary from a user directory. The agent is managed with launchd, and in this sense it is "independent" from the main Application Bundle. I have setup AssociatedBundleIdentifiers in the Agent plist, to associate it with the Application. The TeamIdentifier of the 2 binaries are the same. Based on the documentation, this should be enough for my agent to signal macOS that the responsible code is the Application Bundle (which is already enabled from Local Network POV).
Instead, once the LaunchAgent starts, the Local Network Consent popup appears for the binary. I would expect the Application to be the responsible code, thus no more Consent popup.
Is this behaviour intended?
I need this service to run as user, so I cannot just circumvent the Consent popup by running as a Daemon or Root. Nor I would like to manage the Agent with ServiceManagement. What do you guys think, have I misunderstood the documentation?
I have an issue that causes multiple instances of the push provider to be initialized. And I'd like to ask you what could trigger the instantiation NEAppPushProvider subclass. It seems like it's being triggered excessively. If there's documentation that I have overlooked then just show it to me and I'll be on my way.
Here's the details. But really all I want to know is why is my subclass for NEAppPushProvider keeps getting initialized. If you can answer me that than maybe all these details don't really matter but here they are.
Here's why I believe there's multiple push provider. I see logs for my push provider initializing but I don't see it de-initializing. I also see redundant logs showing multiple instances trying to log into my server. Each time it initializes, an additional log is added for trying to log into my server.
In the app, the system saves it's configuration shortly after initialization, after saving and loading the push configuration, the app doesn't touch config.
Meanwhile in the extension, after 8 or so hours, the extension starts creating a new instance of the push provider. Then a few hours later it does it again. And again. Until the watch dog kills us for wasting too much CPU.
Normally on a fresh install, I'll observe turning off the wifi to call stop on the push provider and later have the push provider de-initialize.
The extension maintains a socket connection to the server, the server can send it messages to display push notifications. The software runs on hospital networks, which will not have access to the internet. It seems like the connection to the server is stable from the logs. I don't detect any disconnections. I'll check with the server to confirm.
In the app I call removeFromPreferences to clear out any extensions before logging in/saving push configurations. And I call saveToPreferences on the NEAppPushManager. I do this to make sure I don't have more than one push configuration saved at one time. I also have many logs looking out for this. I used the sample code from apple as the basis of the my own Push Manager. I can post code if you deem it necessary.
Hope to here from you soon. Thank you.
I am working on watch os project. I need to check network connectivity when user turn on and off the network.
I am using NWPathMonitor for check network availability. I am connected with wifi but it still showing unsatisfied not real device but it's working perfect on simulator.
How can NEPacketTunnelProvider launch the companion application, or notify user to launch the application?
I have built an iOS VPN that uses credentials stored in the keychain, and it works as expected. Now I'm trying to add OAuth login support.
Everything works fine at first. I login from the companion application, store tokens in the keychain, then launch the VPN from either System Settings or the companion application.
However, when the OAuth refresh tokens expire, or the OAuth IdP otherwise requires login, I can't perform the OAuth login from the NEPacketTunnelProvider. Login must happen from the companion application, which likely isn't running. I need the NEPacketTunnelProvider to either launch the companion application directly or to notify the user to do so.
Searching and reading docs yields:
You can't perform OAuth login from within the NEPacketTunnelProvider because it requires user interaction
There is no way to guarantee that the companion application is running on iOS (otherwise one would use NEVPNStatusDidChange)
You can't launch the companion application from NEPacketTunnelProvider using a custom URL because of security concerns
You might be able to launch the companion application from a system extension...
Some sources say you still can't guarantee that the system extension is loaded whenever the NEPacketTunnelProvider needs it anyway.
Of course, any of these conclusions could be wrong.
At this point I'm not sure where to begin. Is there another approach that could be initiated by the NEPacketTunnelProvider (push notifications, system notifications, smoke signals)?
Any help would be appreciated.
Thanks,
Bill Welch
DESCRIPTION OF PROBLEM
We have developed an app and server based on the WireGuard protocol. While we have successfully implemented device-wide VPN, we are now working on enabling per-app VPN functionality.
The per-app VPN payload is successfully delivered, and the designated app can read the configuration and establish a connection to the VPN server. However, we are experiencing extremely slow download data rates, measuring only in bytes.
Steps Taken:
Created an app-layer payload.
Configured NETestAppMapping in the app’s Info.plist, using the VPNUUID defined in the payload for the Chrome app.
Despite these configurations, data transfer remains significantly slow. We would appreciate any insights into potential causes or recommendations to resolve this performance issue.
Thank you for your assistance.
Context: We are using NWConnection for UDP and TCP Connections, and wanted to know the best way to keep the number of pending send completions in control to limit resource usage
Questions:
Is there a way to control the send rate, such that too many 'send pending completion' does not get queued. Say if I do a ‘extremely dense flurry of 10 million NWConnection.send’ will all go asynchronous without any complications? Or I would be informed once it reaches some threshold.
Or no? And is it the responsibility of the application using NWConnection.send to limit the outstanding completion , as if they were beyond a certain limit, it would have an impact on outstanding and subsequent requests?
If so – how would one know ‘what is supposed to be the limit’ at runtime? Is this a process level or system level limit.
Will errors like EAGAIN and ETIMEOUT ever will be reported. In the test I simulated, where the TCP Server was made to not do receive, causing the 'socket send buffer' to become full on the sender side. On the sender side my send stopped getting complete, and became pending. Millions of sends were pending for long duration, hence wanted to know if we will ever get EAGAIN or ETIMEOUT.
I am working on developing a client to complete 8021.x wireless authentication by python.
According to the CoreWLAN Documentation scanForNetworks(withName:), I'm going to use scanForNetworksWithName_error_ and associateToEnterpriseNetwork_identity_username_password_error_ provided in CoreWLAN. And I wrote a script to have a try.
import os
import pwd
from CoreWLAN import CWWiFiClient
from Foundation import NSString
def get_real_user():
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return sudo_user
return os.environ.get('USER', 'root')
def run_as_user(username):
if os.geteuid() == 0:
uid = pwd.getpwnam(username).pw_uid
gid = pwd.getpwnam(username).pw_gid
os.setuid(uid)
def connect_to_enterprise_network(ssid, username, password):
try:
real_user = get_real_user()
if os.geteuid() == 0:
run_as_user(real_user)
client = CWWiFiClient.sharedWiFiClient()
interface = client.interface()
if not interface:
print("no interface")
return False
print("scaning...")
error = None
scan_result, error = interface.scanForNetworksWithName_error_(ssid, None)
if error:
print(f"scan fialed: {error.localizedDescription()}")
return False
target_network = None
for network in scan_result.allObjects():
if network.ssid() == ssid:
target_network = network
break
if not target_network:
print("no target network")
return False
success, error = interface.associateToEnterpriseNetwork_identity_username_password_error_(
target_network,
None,
NSString.stringWithString_(username),
NSString.stringWithString_(password),
None
)
if not success:
print(f"connect failed: {error.localizedDescription() if error else 'unknown error'}")
return False
print("connect successfully")
return True
except Exception as e:
print(f"exception: {str(e)}")
return False
if __name__ == "__main__":
ssid = "ssid"
username = "username"
password = "password"
success = connect_to_enterprise_network(ssid, username, password)
However, I can only execute this script normally under non-root permissions. When I switch to root and execute it, the variable "scan_result.allObjects()" will be an object without any ssid and bssid. Finally the function prints "no target network" and returned.
<CWNetwork: 0x107104080> [ssid=(null), bssid=(null), security=WPA2 Enterprise, rssi=-52, channel=<CWChannel: 0x11e8a1fd0> [channelNumber=44(5GHz), channelWidth={20MHz}], ibss=0]
Compared with the value without sudo:
[<CWNetwork: 0x144650580> [ssid=ssid, bssid=<redacted>, security=WPA2 Enterprise, rssi=-55, channel=<CWChannel: 0x1247040d0> [channelNumber=149(5GHz), channelWidth={20MHz}], ibss=0]]
My python code will be included in an app that must be executed as a root user, so this issue can't be ignored and waiting for your help. THANKS!
Hi there, I'm having an issue hoping someone could help. We have an iOS app that uses CoreBluetooth to connect to peripherals using the central manager. The app works great - However, when using the same exact central manager for our watchos app, it will attempt to connect, but I never get a callback for either didConnect or didFailToConnect.
The watch can connect successfully to other BLE devices, so the watch itself is capable of BLE connectivity.
Here's a list of thing's I've tried (unsuccessfully):
1) Added every bluetooth-related entitlement to info.plist
Privacy - Bluetooth Always Usage Description
Privacy - Bluetooth Peripheral Usage Description
Background Modes: App communicates using CoreBluetooth, App shares data using CoreBluetooth
2) Checked for Single-Connection Limits
Verified that the iPhone was fully disconnected from the peripheral to ensure the device wasn’t limited to one connection.
Attempted to connect on watchOS alone (with iPhone turned off)
3) Tried various options for CBCentralManager, scanForPeripherals, and connect
I went through all the keys for various options and tried just setting them, they had no effect
CBCentralManagerOptionShowPowerAlertKey, CBConnectPeripheralOptionEnableTransportBridgingKey
Item 2
4) Tried .registerForConnectionEvents()
5) Set peripheral's delegate to the central in the didDiscover, stored it in a variable to ensure a strong reference to it
I get no warnings either. The last time I ran into something like this, I found out the watchOS blocks TCP sockets. If I print out the CBPeripheralState a few seconds after trying to connect, it shows its stuck on CBPeripheralStateConnecting.
Any advice or direction is greatly appreciated
Below is the code and various print outs (day 2 into debugging, so it's not pretty)
class WatchBLEManager:NSObject,CBCentralManagerDelegate, ObservableObject{
var centralManager: CBCentralManager?
@Published var devices : [String:AtomBLEDevice] = [:]
private var scanningDevice:AtomBLEDevice?
var bleStatus:WatchBLEStatus = .blePoweredOff
func startBLE() {
centralManager = CBCentralManager(delegate: self, queue: nil,options: [CBCentralManagerOptionShowPowerAlertKey: true])
self.centralManager?.delegate = self
}
func startScan() {
self.centralManager?.scanForPeripherals(withServices: [],options: [CBCentralManagerScanOptionAllowDuplicatesKey : true])
self.centralManager?.delegate = self
}
func stopScan() {
print("stopping scan")
self.centralManager?.stopScan()
filterName = ""
scanningDevice = nil
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch (central.state) {
//... other states omitted
case .poweredOff:
bleStatus = .blePoweredOff
// bleStateDelegate?.didBlePoweredOff()
for device in devices.values{
device.isConnected = false
}
print("BLE is Powered Off")
case .poweredOn:
bleStatus = .blePoweredOn
// bleStateDelegate?.didBlePoweredOn()
startScan()
centralManager?.registerForConnectionEvents()
print("Central supports extended scan and connect: ", CBCentralManager.supports(.extendedScanAndConnect))
print("powered on")
@unknown default:
print("BLE is Unknown")
}
}
private let connectionQueue = DispatchQueue(label: "com.atom.connectionQueue")
var connectingTo: String? = nil
var peripheral: CBPeripheral? = nil
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String else { return}
if localName.contains("Atom") {
print("\nConnecting to \(localName)")
print("\tAdvertising data: \(advertisementData)")
print("\tANCS Authorized: ",peripheral.ancsAuthorized)
print("\tServices", peripheral.services, "\n")
self.peripheral = peripheral
self.peripheral?.delegate = self
// central.registerForConnectionEvents()
// central.delegate = self
peripheral.delegate = self
DispatchQueue.main.async {
// central.connect(peripheral)
self.centralManager?.connect(peripheral, options: [ CBConnectPeripheralOptionEnableTransportBridgingKey: true])
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
print("\tState", String(describing: peripheral.state))
print("Connected Peripherals: \(self.centralManager?.retrieveConnectedPeripherals(withServices: []))")
}
}
}
// Never gets called for watchos
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected to peripheral: \(peripheral.identifier)")
if let atomDevice = getAtomBLEDevice(peripheral: peripheral) {
//atomDevice.setPeripheral(perpipheral: <#T##CBPeripheral?#>)
atomDevice.isConnected = true
atomDevice.isConnecting = false
//delegate?.didConnected(atomBLE: atomDevice!)
atomDevice.startDiscoveringService()
//atomDevice?.delegate?.didConnected(atomBLE: atomDevice!)
print("Connected: \(peripheral.name)")
} else {
print("no matching atom device found for didConnect")
print("connected peripheral :",peripheral.identifier.uuidString)
}
}
func centralManager(_ central: CBCentralManager, connectionEventDidOccur event: CBConnectionEvent, for peripheral: CBPeripheral) {
print("Connection event: \(event)")
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: (any Error)?) {
print("Failed to connect: \(error?.localizedDescription)")
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
let atomDevice = getAtomBLEDevice(peripheral: peripheral)
atomDevice?.isConnected = false
print("Peripheral disconnected:\(peripheral.name)")
}
func clearData() {
filterName = ""
for device in devices.values{
disconnect(atomBLEDevice: device)
device.perpipheral?.delegate = nil
}
devices = [:]
scanningDevice = nil
// delegate = nil
centralManager = nil
}
}
extension WatchBLEManager: CBPeripheralDelegate {
}```
I’m working with the NEHotspotHelper API in my iOS app, and I noticed the following log message in Console:
"(BUNDLE ID ) is using NEHotspotHelper API and it's unresponsive to API's evaluate command. The API gives 45 seconds to 3rd party apps to respond, and then it launches WebSheet to allow user to interact with the portal."
I have two different apps that both register a NEHotspotHelper handler:
App A checks for .evaluate and calls createResponse(.unsupportedNetwork) if we don’t manage that particular network.
App B registers for hotspot events but does not handle .evaluate at all.
In App A, whenever I see that “unresponsive” or “45 seconds” log, the system eventually launches the standard captive portal WebSheet. In App B, I never see those logs.
I have a few questions:
Are these “unresponsive” logs indeed triggered by the .evaluate command specifically?
In other words, do we only see that 45-second timeout and the subsequent WebSheet message if our app is registered to handle Evaluate but doesn’t respond quickly (or responds with .unsupportedNetwork)?
Is it best practice (or required) to always respond to .evaluate—for example, sending .unsupportedNetwork if we don’t plan on managing the user’s login or captive portal? Does ignoring .evaluate lead to other unexpected behavior or logs?
Should we still explicitly respond to Evaluate with .unsupportedNetwork? Or is it okay to skip Evaluate handling entirely on every app or invocation?
I’d love to confirm whether .evaluate handling is the direct cause of these logs, and how best to avoid the “unresponsive”/“45 seconds” fallback if our app isn’t intended to manage the portal.
Thanks in advance for any insights!
My app helps users connect to Wi-Fi networks, and I have requested the Access Wi-Fi information entitlement. This allows the app to retrieve the current Wi-Fi information to ensure the user’s connection is successful.
Now, we are trying to implement an App Clip that enables users to connect to a specific Wi-Fi network through a QR code scan or NFC in certain scenarios. In the App Clip, I’ve requested the Hotspot entitlement, which allows the app to use the hotspot manager to configure Wi-Fi networks. However, since I cannot access the current Wi-Fi information in the App Clip, I’m unable to confirm whether the connection was successful.
Hi,
I've created a packet tunnel but my packetFlow object isn't get called with any packets. Do I need to do something else to configure the packetFlow? Maybe I have to link it to a NWUDPSession?
Thanks,
Dave
class PacketTunnelProvider: NEPacketTunnelProvider {
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelRemoteAddress)
settings.ipv4Settings = NEIPv4Settings(addresses: [tunnelRemoteAddress], subnetMasks: ["255.255.255.255"])
settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()]
setTunnelNetworkSettings(settings) { error in
completionHandler(error)
self.readPacketObjects()
}
}
private func readPacketObjects() {
self.packetFlow.readPacketObjects() { packets in
// It never gets here.
self.logMessage("Got '\(packets.count)' packet(s)")
self.packetFlow.writePacketObjects(packets)
self.readPacketObjects()
}
}
}
I am looking for a lightweight server that can run inside an app.
The key requirement is that it must support local IP communication over HTTPS.
I have searched Google and found several frameworks, but as far as I know, support for HTTPS in this environment has been discontinued or is no longer available.
If anyone knows a solution that meets these criteria, I would greatly appreciate your guidance.
Thank you in advance!😀
I'm using the NEHotspostConfigurationManager to join the WiFi network of a configured accessory.
While this is all nice and dandy, I wonder why I'm still connected to said WiFi when I (force-)close the app. Wouldn't it be more useful to reconnect to the last network before?