From ae60a5c3f911f99195b79dd25cccc0dacf2df217 Mon Sep 17 00:00:00 2001 From: beetee17 <81674501+beetee17@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:23:05 +0800 Subject: [PATCH 1/3] Fix OpenURL crash on macOS Sequoia --- Sources/Dependencies/DependencyValues/OpenURL.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Dependencies/DependencyValues/OpenURL.swift b/Sources/Dependencies/DependencyValues/OpenURL.swift index 9cc1fced..f54ee725 100644 --- a/Sources/Dependencies/DependencyValues/OpenURL.swift +++ b/Sources/Dependencies/DependencyValues/OpenURL.swift @@ -12,7 +12,7 @@ @available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) private enum OpenURLKey: DependencyKey { - static let liveValue = OpenURLEffect { url in + static let liveValue = OpenURLEffect { @MainActor url in let stream = AsyncStream { continuation in let task = Task { @MainActor in #if os(watchOS) From 7e6f8098940ffeb4b60e354fed82fe74d2af9b90 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 13 Jan 2025 22:59:09 +0800 Subject: [PATCH 2/3] Amended: Fix OpenURL crash on macOS Sequoia Further testing has revealed that the crash stems from the completion handler for `openURL`. This may be a bummer for use-cases that require it to check whether an app was available to handle a URL scheme. An attempt was made to have the best of both worlds by returning `.systemAction` in the `OpenURLAction` handler, however this causes the completion handler to never return. Therefore, the only real fix is to always return `.handled`, which makes it equivalent to the watchOS implementation that always yields `true`. --- .../DependencyValues/OpenURL.swift | 28 +++++++++++++++++-- Tests/DependenciesTests/OpenURLTests.swift | 19 +++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Tests/DependenciesTests/OpenURLTests.swift diff --git a/Sources/Dependencies/DependencyValues/OpenURL.swift b/Sources/Dependencies/DependencyValues/OpenURL.swift index e2e76930..cb138564 100644 --- a/Sources/Dependencies/DependencyValues/OpenURL.swift +++ b/Sources/Dependencies/DependencyValues/OpenURL.swift @@ -11,14 +11,38 @@ } @available(iOS 14, macOS 11, tvOS 14, watchOS 7, *) - private enum OpenURLKey: DependencyKey { - static let liveValue = OpenURLEffect { @MainActor url in + internal enum OpenURLKey: DependencyKey { + static let liveValue = OpenURLEffect { url in let stream = AsyncStream { continuation in let task = Task { @MainActor in #if os(watchOS) EnvironmentValues().openURL(url) continuation.yield(true) continuation.finish() + #elseif os(macOS) + /// On macOS Sequoia, invokng `EnvironmentValues().openURL(_:completion:)` + /// i.e. with the completion handler, causes an `EXC_BREAKPOINT (SIGTRAP), KERN_INVALID_ADDRESS` + /// runtime crash with `_dispatch_assert_queue_fail` on a non-main thread. + if #available(macOS 15.0, *) { + let openURL = OpenURLAction { url in + EnvironmentValues().openURL(url) + /// However, using `.systemAction` (which may be needed to indicate whether an app + /// is available to handle a URL scheme), seems to cause the completion handler to never return... + /// Therefore, this implementation is actually equivalent to the watchOS one, which always + /// yields `true`. + return .handled + } + + openURL(url) { canOpen in + continuation.yield(canOpen) + continuation.finish() + } + } else { + EnvironmentValues().openURL(url) { canOpen in + continuation.yield(canOpen) + continuation.finish() + } + } #else EnvironmentValues().openURL(url) { canOpen in continuation.yield(canOpen) diff --git a/Tests/DependenciesTests/OpenURLTests.swift b/Tests/DependenciesTests/OpenURLTests.swift new file mode 100644 index 00000000..c8c05ca4 --- /dev/null +++ b/Tests/DependenciesTests/OpenURLTests.swift @@ -0,0 +1,19 @@ +import XCTest + +@testable import Dependencies + +class OpenURLTests: XCTestCase { + @Dependency(\.openURL) var openURL + + /// Please note that running this test may cause side-effects, such as actually opening the + /// given webpage on the Mac that is running the test. + func testOpenURL_liveValue() async { + await withDependencies { + $0.openURL = OpenURLKey.liveValue + } operation: { + let url = URL(string: "https://www.pointfree.co/")! + let result = await self.openURL(url) + XCTAssert(result) + } + } +} From b58d7daf2c2e2975d905d4fb7c1a5ce0605e2ad6 Mon Sep 17 00:00:00 2001 From: Brandon Date: Mon, 13 Jan 2025 23:28:51 +0800 Subject: [PATCH 3/3] Remove unnecessary assertion --- Tests/DependenciesTests/OpenURLTests.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Tests/DependenciesTests/OpenURLTests.swift b/Tests/DependenciesTests/OpenURLTests.swift index c8c05ca4..75a46008 100644 --- a/Tests/DependenciesTests/OpenURLTests.swift +++ b/Tests/DependenciesTests/OpenURLTests.swift @@ -12,8 +12,7 @@ class OpenURLTests: XCTestCase { $0.openURL = OpenURLKey.liveValue } operation: { let url = URL(string: "https://www.pointfree.co/")! - let result = await self.openURL(url) - XCTAssert(result) + await self.openURL(url) } } }