Swift / SwiftUI - 在设备锁定/置于后台之前执行功能?

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

我正在开发一个记录视频片段的应用程序(请参阅 Instagram 的 reels 以供参考),我目前正在处理应用程序移至后台/屏幕被锁定时的状态。

我目前遇到的问题是,如果我在录制正在进行的视频时将应用程序移至后台/锁定屏幕,AVCaptureFileOutputRecordingDelegate 的 fileOutput 方法将无法保存视频。我尝试在 .plist 中添加 'required background modes',我也偶然发现了 following thread,现在我不确定是否可以在应用程序移至后台,如果您想遵守隐私准则,这样做是否是个好主意。

我想知道是否有一种方法可以延迟将应用移至后台,以便我可以执行通常用于在设备进入后台之前停止和保存视频录制的方法(最终无法为我保存视频)。

注意:将我保存/停止正在进行的视频录制的方法放在下面的观察中是行不通的,它会失败,如上所述:

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

感谢您提供的任何意见!

研究 Scene 协议。

我引用...

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:

需要说明的是,这是 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()
            }
        }
    }
}

根据您的情况,您可以将 cache.empty 命令替换为 toggleRecording()

以下代码解决了我的特定情况下的问题:

当应用程序被置于后台时,AVCaptureFileOutputRecordingDelegate 被调用并且错误不可避免地发生。如果发生这种情况,我会告诉应用程序在开始录制时从 url (var activeRecordingUrl: URL?) 中查找任何已录制的数据。如果找到数据(对我来说似乎总是如此),我会像我希望的那样存储它,就好像用户从 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)
    }
}