I am working on an app that records video segments (see Instagram's reels for reference), and I'm currently in the process of handling states when the application moves to the background / screen is locked.
The current issue I have is that if I move the app to the background / lock the screen DURING an on-going video recording the AVCaptureFileOutputRecordingDelegate's fileOutput method fails to save the video. I have tried adding 'required background modes' in the .plist, and I've also stumbled upon the following thread and now I am unsure of wether or not it is possible to actually save a on-going video recording when the app moves to the background, as well as if it's a good idea to do so at all if you want to adhere to privacy guidelines.
I'm wondering if there is a way to delay an app being moved to the background so that I can execute the methods that I would normally use to stop and save a video recording before the device ends up in the background (which ultimately fails the process of saving the video for me).
Note: Putting my method for saving / stopping the on-going video recording in the following observation does NOT work, it will fail as described above:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
print("Moving to the background!")
toggleRecording() <--- this normally stops and saves the recording but it fails here.
}
Thankful for any input that can be provided!
CodePudding user response:
Research the Scene protocol.
I quote...
The Scene protocol provides scene modifiers, defined as protocol methods with default implementations, that you use to configure a scene. For example, you can use the onChange(of:perform:) modifier to trigger an action when a value changes. The following code empties a cache when all of the scenes in the window group have moved to the background:
and to be clear this is the example provided by Apple...
struct MyScene: Scene {
@Environment(\.scenePhase) private var scenePhase
@StateObject private var cache = DataCache()
var body: some Scene {
WindowGroup {
MyRootView()
}
.onChange(of: scenePhase) { newScenePhase in
if newScenePhase == .background {
cache.empty()
}
}
}
}
and for your situation you might replace the cache.empty
command with toggleRecording()
.
CodePudding user response:
The following code solved the issue in my particular case:
When the app is put in to the background the AVCaptureFileOutputRecordingDelegate
is called and the error inevitably occurs. If that happens I tell the app to look for any recorded data from the url (var activeRecordingUrl: URL?
) that I generate/update when starting a recording. If there was data found (which always seem to be the case for me) I store it like I would as if the recording had been "manually" stopped by the user from the UI.
extension CameraViewController: AVCaptureFileOutputRecordingDelegate {
func fileOutput(
_ output: AVCaptureFileOutput,
didFinishRecordingTo outputFileURL: URL,
from connections: [AVCaptureConnection],
error: Error?
) {
guard error == nil else {
// --------- Start of fix ---------
NSLog("> ERROR: Recording was interrupted: \(String(describing: error?.localizedDescription)), attempting to salvage record data from activeRecordingUrl.")
guard let outputUrl = activeRecordingUrl else {
NSLog("> ERROR: found nil when unwrapping activeRecordingUrl.")
onRecordingFinished(false)
return
}
recordings.append(VideoSegment(avAssetUrl: outputUrl, cameraPosition: sessionInput.device.position, avAsset: AVAsset(url: outputUrl)))
NSLog("Found record data from the session that was interrupted.")
onRecordingFinished(true)
// --------- End of fix ---------
return
}
recordings.append(VideoSegment(
avAssetUrl: outputFileURL,
cameraPosition: sessionInput.device.position,
avAsset: AVAsset(url: outputFileURL
)))
onRecordingFinished(true)
}
}