ARTICLE AD BOX
I need help with ReplayKit screen recording using a Broadcast Upload Extension in my React Native Expo (bare) iOS project.
Current situation:
My broadcast extension appears correctly in Control Center under Screen Recording.
When I select it:
The 3-2-1 countdown starts
Recording begins
But it stops instantly
So the broadcast is not actually running properly.
Because of this:
When I return to the app and call stopRecording()
The App Group UserDefaults key (lastRecordingPath) is empty
No video path is returned
It looks like:
broadcastStarted is triggered
But the extension stops immediately
So broadcastFinished might be called right away
Or the extension is crashing on the first frame
Based on debugging, I suspect the crash happens when processSampleBuffer receives the first video frame and calls setupWriter(), which initializes AVAssetWriter with:
AVAssetWriter(outputURL: url, fileType: .mp4)
Project structure:
Main App
Broadcast Upload Extension (SampleHandler.swift)
App Group configured and shared
Extension embedded correctly (Embed & Sign)
Automatic signing enabled
Testing on physical device (iPhone 11, iOS 18.2)
Bundle IDs:
Main App: com.athelia.app
Broadcast Extension: com.athelia.app.AtheliaBroadcast
The extension is visible in Control Center, so configuration seems correct.
Main issue:
Recording starts but stops immediately. Video path is never saved to App Group
Crash log shows (from Analytics Data on device):
Exception: EXC_CRASH (SIGABRT) -[AVAssetWriterInput markAsFinished] closure #1 in SampleHandler.broadcastFinished() consecutiveCrashCount: 3
Logs from main app: zero [EXT] logs ever appear, confirming extension crashes before broadcastStarted prints anything:
Triggering broadcast picker button
Broadcast detection poll 1: isActive=false ... 🔍 Broadcast detection poll 20: isActive=false ⚠️ Broadcast detection timeout
SampleHandler.swift:
override func broadcastStarted(withSetupInfo setupInfo: [String: NSObject]?) { let container = FileManager.default.containerURL( forSecurityApplicationGroupIdentifier: "group.com.athelia.app" ) let baseURL = container ?? FileManager.default .urls(for: .documentDirectory, in: .userDomainMask)[0] let url = baseURL.appendingPathComponent("broadcast_\(Int(Date().timeIntervalSince1970)).mp4") outputURL = url UserDefaults(suiteName: "group.com.athelia.app")?.set(true, forKey: "broadcastIsActive") } override func broadcastFinished() { guard sessionStarted, let writer = assetWriter, writer.status == .writing else { saveResultAndExit(path: nil) return } videoInput?.markAsFinished() // guarded — should never crash audioInput?.markAsFinished() writer.finishWriting { ... } }Question: Why does the broadcast extension stop immediately after the countdown? What causes broadcastFinished to be called instantly after broadcastStarted with no frames received?
