88
99import Foundation
1010import AppKit
11+ import SwiftUI
1112
1213extension NSOpenPanel {
1314 public convenience init (
@@ -22,31 +23,33 @@ extension NSOpenPanel {
2223 }
2324
2425 public func present(
25- completion: ( _ urls: [ URL ] ? ) -> Void
26+ completion: @ MainActor @ escaping ( _ urls: [ URL ] ? ) -> Void
2627 ) {
2728 var selectedURLs : [ URL ] ?
2829
29- if runModal ( ) == . OK {
30- selectedURLs = urls
30+ // runModal() must be called asynchronously or we get a runtime exception on macOS 26
31+ Task { @MainActor in
32+ if runModal ( ) == . OK {
33+ selectedURLs = urls
34+ }
35+
36+ completion ( selectedURLs)
3137 }
32-
33- completion ( selectedURLs)
3438 }
3539}
3640
37- import SwiftUI
38-
41+ @MainActor
3942public struct OpenPanelView < Content: View > : View {
4043 @Binding public var isPresented : Bool
4144 public let setup : ( ( _ panel: NSOpenPanel ) -> Void ) ?
42- public let completion : ( _ urls: [ URL ] ? ) -> Void
45+ public let completion : @ MainActor ( _ urls: [ URL ] ) -> Void
4346 public let content : Content
4447
4548 @State private var panel : NSOpenPanel ?
4649
4750 public var body : some View {
4851 content
49- . onChange ( of: isPresented) { oldValue , newValue in
52+ . onChange ( of: isPresented) { newValue in
5053 newValue ? present ( ) : close ( )
5154 }
5255 }
@@ -56,16 +59,17 @@ public struct OpenPanelView<Content: View>: View {
5659 setup ? ( newPanel)
5760 panel = newPanel
5861
59- var selectedURLs : [ URL ] ?
60-
6162 isPresented = true
62- if newPanel. runModal ( ) == . OK {
63- selectedURLs = newPanel. urls
64- }
6563
66- completion ( selectedURLs)
67- panel = nil
68- isPresented = false
64+ newPanel. present { urls in
65+ isPresented = false
66+ panel = nil
67+ if let urls {
68+ completion ( urls)
69+ } else {
70+ // user cancelled or something else happened
71+ }
72+ }
6973 }
7074
7175 private func close( ) {
@@ -79,7 +83,7 @@ extension View {
7983 public func fileOpenPanel(
8084 isPresented: Binding < Bool > ,
8185 setup: ( ( NSOpenPanel ) -> Void ) ? = nil ,
82- completion: @escaping ( _ urls: [ URL ] ? ) -> Void
86+ completion: @MainActor @ escaping ( _ urls: [ URL ] ? ) -> Void
8387 ) -> some View {
8488 OpenPanelView (
8589 isPresented: isPresented,
0 commit comments