Home > OS >  Swift / SwiftUI - Executing a function before the device locks / is put in the background?
Swift / SwiftUI - Executing a function before the device locks / is put in the background?

Time:10-31

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)
    }
}


  • Related