I'm developing a lighting control app for iOS that receives Art-Net (UDP port 6454) and sACN (UDP port 5568) packets from a lighting console and relays commands to BLE wristbands with LEDs. This is used in live event production — the participant locks their phone while in a show and expects lighting control to continue uninterrupted.
The problem
UDP receive stops reliably ~30 seconds after the screen locks. I understand this is by design - iOS suspends apps in the background. However, I'm trying to understand if any supported path exists for this use case.
What I've already tried
UIRequiresPersistentWiFi = true - helps with Wi-Fi association but doesn't prevent app suspension
Silent AVAudioEngine loop with UIBackgroundModes: audio - keeps the app alive, works in testing, but risks App Store rejection and feels like an abuse of the audio background mode
NWListener (Network framework) on the UDP port - same suspension behaviour
Socket rebind on applicationWillEnterForeground - recovers after resume but doesn't prevent dropout
What I'm asking
Is there any supported background mode or entitlement for sustained UDP receive in a professional/enterprise context? (Similar to how VoIP apps get the voip background mode for sustained network activity.)
Is the silent audio workaround considered acceptable for App Store distribution in a professional tools context, or will it be rejected?
Is NEAppProxyProvider or another Network Extension a viable path, and if so does it require a special entitlement?
Test project
I have a minimal Xcode project (~130 lines) demonstrating the issue — NWListener on port 6454, packet counter, staleness timer, and silent audio toggle. I can share the test code.
STEPS TO REPRODUCE
In Xcode (one-time setup):
Select the UDPBackgroundTest target → Signing & Capabilities → set your Team
Plug in your iPhone → select it as the run destination
Build & run — confirm packets appear on screen when you run 'send_test_udp.py'
Lock the phone and observe the dropout
Test:
Open the app and run 'python3 send_test_udp.py 192.168.0.XXX'
The app counts up the packages, they match the python output. 1 packet per second.
lock screen & and wait 10 seconds
unlock phone an see the numbers are 10 packets off
1
0
80