When calling FilePickerService.browseFile() from ContentView, since the function is now annotated with @MainActor, Swift ensures that the call is made on the main thread, which is compatible with SwiftUI's expectations for UI operations.
struct FilePickerService {
@MainActor // ← necessary for new Swift concurrency
static func browseForShellFile() async -> String? {
return await withCheckedContinuation { continuation in
let panel = NSOpenPanel()
panel.allowsMultipleSelection = false
panel.canChooseDirectories = false
if let shellScriptType = UTType(filenameExtension: "sh") {
panel.allowedContentTypes = [shellScriptType]
} else {
print("Failed to find UTType for .sh files")
continuation.resume(returning: nil)
return
}
panel.begin { response in
if response == .OK, let url = panel.urls.first, url.pathExtension == "sh" {
continuation.resume(returning: url.path)
} else {
print("Error: Selected file is not a .sh script.")
continuation.resume(returning: nil)
}
}
}
}
}
GPT4 PR feedback:
Starting with Swift concurrency, certain operations, especially those involving the user interface or other main-thread-bound actions, need to be explicitly marked to run on the main thread. This is enforced more strictly in newer versions of Swift (like Swift 6) with the introduction of stricter concurrency checks.
To resolve these errors, you need to ensure that all interactions with NSOpenPanel occur on the main actor. This can be done by marking the asynchronous function or specific code blocks to run on the main actor. Here's how to adjust your FilePickerService:
When calling
FilePickerService.browseFile()
from ContentView, since the function is now annotated with@MainActor
, Swift ensures that the call is made on the main thread, which is compatible with SwiftUI's expectations for UI operations.GPT4 PR feedback: