fix: save dialog not appearing after recording on macOS/iOS

macOS:
- Replace deprecated NSSavePanel.begin(completionHandler:) with async begin() API
- The old API has been deprecated since macOS 12 and may silently fail on macOS 26
- Added diagnostic logging throughout recording completion flow

iOS:
- Added diagnostic logging to track onRecordingSaved → pendingExportURL → sheet chain
- Logs file existence, size after recording completes

Diagnostic logging added to help verify the save dialog flow:
- macOS: file size check, showSaveDialog entry, NSSavePanel response
- iOS: recordStream output verification, onRecordingSaved call, sheet binding activation
This commit is contained in:
yumoqing 2026-07-17 20:27:49 +08:00
parent 01bf7d6f4a
commit 04990674cc
4 changed files with 36 additions and 19 deletions

View File

@ -36,10 +36,13 @@ final class HLSRecorder: ObservableObject {
recordTask = Task {
do {
let outputURL = try await recordStream(url: url)
NSLog("[MiniPlayer] recordStream completed: %@", outputURL.path)
NSLog("[MiniPlayer] recordStream completed: %@, exists: %d", outputURL.path, FileManager.default.fileExists(atPath: outputURL.path))
let attrs = try? FileManager.default.attributesOfItem(atPath: outputURL.path)
NSLog("[MiniPlayer] Output file size: %lld", (attrs?[.size] as? Int64) ?? 0)
await MainActor.run {
self.isRecording = false
self.timer?.invalidate()
NSLog("[MiniPlayer] Calling onRecordingSaved...")
self.onRecordingSaved?(outputURL)
}
} catch is CancellationError {

View File

@ -585,10 +585,15 @@ struct BricksAppView: View {
}
}
.sheet(isPresented: Binding<Bool>(
get: { bridge.pendingExportURL != nil },
get: {
let has = bridge.pendingExportURL != nil
if has { NSLog("[MiniPlayer] Sheet binding: pendingExportURL=%@", bridge.pendingExportURL?.lastPathComponent ?? "nil") }
return has
},
set: { if !$0 { bridge.pendingExportURL = nil } }
)) {
if let url = bridge.pendingExportURL {
NSLog("[MiniPlayer] Presenting ActivityShareSheet for: %@", url.lastPathComponent)
ActivityShareSheet(url: url) {
bridge.pendingExportURL = nil
}

View File

@ -178,8 +178,10 @@ final class PlayerBridge: ObservableObject {
#if os(iOS)
hlsRecorder.onRecordingSaved = { [weak self] url in
NSLog("[MiniPlayer] onRecordingSaved called, setting pendingExportURL: %@", url.lastPathComponent)
self?.showToast("Saved: \(url.lastPathComponent)")
self?.pendingExportURL = url
NSLog("[MiniPlayer] pendingExportURL set to: %@", url.lastPathComponent)
}
hlsRecorder.onError = { [weak self] msg in
self?.showToast(msg)

View File

@ -62,10 +62,13 @@ final class PlayerRecorder: NSObject {
try await recorder.start(url: url, outputPath: tempURL.path, duration: 0)
// duration=0 stop()
// start stop()
NSLog("[Recorder] ✓ Recording completed: %@", tempURL.path)
NSLog("[Recorder] ✓ Recording completed: %@, file exists: %d", tempURL.path, FileManager.default.fileExists(atPath: tempURL.path))
let attrs = try? FileManager.default.attributesOfItem(atPath: tempURL.path)
NSLog("[Recorder] Output file size: %lld", (attrs?[.size] as? Int64) ?? 0)
await MainActor.run {
RecorderState.isFinalizing = false
self?.stopDurationTimer()
NSLog("[Recorder] Calling showSaveDialog...")
self?.showSaveDialog(tempURL: tempURL)
}
} catch {
@ -129,6 +132,7 @@ final class PlayerRecorder: NSObject {
// MARK: -
private func showSaveDialog(tempURL: URL) {
NSLog("[Recorder] showSaveDialog called for: %@", tempURL.lastPathComponent)
let panel = NSSavePanel()
panel.allowedContentTypes = [.mpeg4Movie]
// temp_
@ -136,25 +140,28 @@ final class PlayerRecorder: NSObject {
panel.nameFieldStringValue = defaultName
panel.directoryURL = saveDirectory
panel.begin { [weak self] response in
Task { @MainActor in
if response == .OK, let url = panel.url {
do {
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
try FileManager.default.moveItem(at: tempURL, to: url)
NSLog("[Recorder] ✓ Saved: %@", url.path)
self?.onRecordingSaved?(url)
} catch {
NSLog("[Recorder] ✗ Save failed: %@", error.localizedDescription)
self?.onError?("Save failed: \(error.localizedDescription)")
// NSSavePanel.begin(completionHandler:) macOS 12 macOS 26
// 使 async begin() API
Task { @MainActor [weak self] in
NSLog("[Recorder] NSSavePanel.begin() starting...")
let response = await panel.begin()
NSLog("[Recorder] NSSavePanel response: %d", response.rawValue)
if response == .OK, let url = panel.url {
do {
if FileManager.default.fileExists(atPath: url.path) {
try FileManager.default.removeItem(at: url)
}
} else {
try? FileManager.default.removeItem(at: tempURL)
try FileManager.default.moveItem(at: tempURL, to: url)
NSLog("[Recorder] ✓ Saved: %@", url.path)
self?.onRecordingSaved?(url)
} catch {
NSLog("[Recorder] ✗ Save failed: %@", error.localizedDescription)
self?.onError?("Save failed: \(error.localizedDescription)")
}
self?.checkPendingTerminate()
} else {
try? FileManager.default.removeItem(at: tempURL)
}
self?.checkPendingTerminate()
}
}