From dccdf5aed1db969afa11d6fbd36b96a4932ebe8c Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Thu, 14 Nov 2024 11:42:24 -0800 Subject: [PATCH] Reintroduce conditional import (#108) * Reintroduce conditional import Not all platforms have Observation enabled in their official releases at this time, for example SwiftWasm. * wip * wip * wip * Fix * wip * wip * wip * wip * wip --- .github/workflows/ci.yml | 31 ++++ Sources/PerceptionCore/Internal/Exports.swift | 7 +- .../PerceptionCore/Internal/ThreadLocal.swift | 2 +- .../PerceptionCore/PerceptionRegistrar.swift | 162 ++++++++++-------- .../PerceptionCore/PerceptionTracking.swift | 8 +- .../PerceptionMacros/PerceptibleMacro.swift | 2 +- 6 files changed, 133 insertions(+), 79 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3bdf759..e68e36a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,37 @@ jobs: run: make test-compatibility if: ${{ matrix.config == 'debug' }} + linux: + name: Linux + strategy: + matrix: + swift: + - '6.0' + runs-on: ubuntu-latest + container: swift:${{ matrix.swift }} + steps: + - uses: actions/checkout@v4 + - name: Build + run: swift build + + wasm: + name: Wasm + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: bytecodealliance/actions/wasmtime/setup@v1 + - name: Install Swift and Swift SDK for WebAssembly + run: | + PREFIX=/opt/swift + set -ex + curl -f -o /tmp/swift.tar.gz "https://download.swift.org/swift-6.0.2-release/ubuntu2204/swift-6.0.2-RELEASE/swift-6.0.2-RELEASE-ubuntu22.04.tar.gz" + sudo mkdir -p $PREFIX; sudo tar -xzf /tmp/swift.tar.gz -C $PREFIX --strip-component 1 + $PREFIX/usr/bin/swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-6.0.2-RELEASE/swift-wasm-6.0.2-RELEASE-wasm32-unknown-wasi.artifactbundle.zip --checksum 6ffedb055cb9956395d9f435d03d53ebe9f6a8d45106b979d1b7f53358e1dcb4 + echo "$PREFIX/usr/bin" >> $GITHUB_PATH + + - name: Build + run: swift build --swift-sdk wasm32-unknown-wasi -Xlinker -z -Xlinker stack-size=$((1024 * 1024)) + check-macro-compatibility: name: Check Macro Compatibility runs-on: macos-latest diff --git a/Sources/PerceptionCore/Internal/Exports.swift b/Sources/PerceptionCore/Internal/Exports.swift index 66f9aa8..ab02410 100644 --- a/Sources/PerceptionCore/Internal/Exports.swift +++ b/Sources/PerceptionCore/Internal/Exports.swift @@ -1 +1,6 @@ -@_exported import Observation +#if canImport(Observation) + @_exported import Observation +#else + @available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) + public protocol Observable {} +#endif diff --git a/Sources/PerceptionCore/Internal/ThreadLocal.swift b/Sources/PerceptionCore/Internal/ThreadLocal.swift index 059184b..02a3845 100644 --- a/Sources/PerceptionCore/Internal/ThreadLocal.swift +++ b/Sources/PerceptionCore/Internal/ThreadLocal.swift @@ -13,7 +13,7 @@ import Foundation struct _ThreadLocal { #if os(WASI) - static var value: UnsafeMutableRawPointer? + static nonisolated(unsafe) var value: UnsafeMutableRawPointer? #else static var value: UnsafeMutableRawPointer? { get { Thread.current.threadDictionary[Key()] as! UnsafeMutableRawPointer? } diff --git a/Sources/PerceptionCore/PerceptionRegistrar.swift b/Sources/PerceptionCore/PerceptionRegistrar.swift index d9924b4..bbff69e 100644 --- a/Sources/PerceptionCore/PerceptionRegistrar.swift +++ b/Sources/PerceptionCore/PerceptionRegistrar.swift @@ -24,7 +24,11 @@ public struct PerceptionRegistrar: Sendable { /// of a type. public init(isPerceptionCheckingEnabled: Bool = PerceptionCore.isPerceptionCheckingEnabled) { if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { - self._rawValue = AnySendable(ObservationRegistrar()) + #if canImport(Observation) + self._rawValue = AnySendable(ObservationRegistrar()) + #else + self._rawValue = AnySendable(_PerceptionRegistrar()) + #endif } else { self._rawValue = AnySendable(_PerceptionRegistrar()) } @@ -33,47 +37,51 @@ public struct PerceptionRegistrar: Sendable { #endif } - @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - private var registrar: ObservationRegistrar { - self._rawValue.base as! ObservationRegistrar - } + #if canImport(Observation) + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + private var registrar: ObservationRegistrar { + self._rawValue.base as! ObservationRegistrar + } + #endif private var perceptionRegistrar: _PerceptionRegistrar { self._rawValue.base as! _PerceptionRegistrar } } -@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) -extension PerceptionRegistrar { - public func access( - _ subject: Subject, - keyPath: KeyPath, - fileID: StaticString = #fileID, - filePath: StaticString = #filePath, - line: UInt = #line, - column: UInt = #column - ) { - self.registrar.access(subject, keyPath: keyPath) - } +#if canImport(Observation) + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + extension PerceptionRegistrar { + public func access( + _ subject: Subject, + keyPath: KeyPath, + fileID: StaticString = #fileID, + filePath: StaticString = #filePath, + line: UInt = #line, + column: UInt = #column + ) { + self.registrar.access(subject, keyPath: keyPath) + } - public func withMutation( - of subject: Subject, keyPath: KeyPath, _ mutation: () throws -> T - ) rethrows -> T { - try self.registrar.withMutation(of: subject, keyPath: keyPath, mutation) - } + public func withMutation( + of subject: Subject, keyPath: KeyPath, _ mutation: () throws -> T + ) rethrows -> T { + try self.registrar.withMutation(of: subject, keyPath: keyPath, mutation) + } - public func willSet( - _ subject: Subject, keyPath: KeyPath - ) { - self.registrar.willSet(subject, keyPath: keyPath) - } + public func willSet( + _ subject: Subject, keyPath: KeyPath + ) { + self.registrar.willSet(subject, keyPath: keyPath) + } - public func didSet( - _ subject: Subject, keyPath: KeyPath - ) { - self.registrar.didSet(subject, keyPath: keyPath) + public func didSet( + _ subject: Subject, keyPath: KeyPath + ) { + self.registrar.didSet(subject, keyPath: keyPath) + } } -} +#endif extension PerceptionRegistrar { @_disfavoredOverload @@ -93,17 +101,19 @@ extension PerceptionRegistrar { column: column ) #endif - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { - func `open`(_ subject: T) { - self.registrar.access( - subject, - keyPath: unsafeDowncast(keyPath, to: KeyPath.self) - ) - } - if let subject = subject as? any Observable { - return open(subject) + #if canImport(Observation) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { + func `open`(_ subject: T) { + self.registrar.access( + subject, + keyPath: unsafeDowncast(keyPath, to: KeyPath.self) + ) + } + if let subject = subject as? any Observable { + return open(subject) + } } - } + #endif self.perceptionRegistrar.access(subject, keyPath: keyPath) } @@ -113,18 +123,20 @@ extension PerceptionRegistrar { keyPath: KeyPath, _ mutation: () throws -> T ) rethrows -> T { - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, - let subject = subject as? any Observable - { - func `open`(_ subject: S) throws -> T { - return try self.registrar.withMutation( - of: subject, - keyPath: unsafeDowncast(keyPath, to: KeyPath.self), - mutation - ) + #if canImport(Observation) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, + let subject = subject as? any Observable + { + func `open`(_ subject: S) throws -> T { + return try self.registrar.withMutation( + of: subject, + keyPath: unsafeDowncast(keyPath, to: KeyPath.self), + mutation + ) + } + return try open(subject) } - return try open(subject) - } + #endif return try self.perceptionRegistrar.withMutation(of: subject, keyPath: keyPath, mutation) } @@ -133,17 +145,19 @@ extension PerceptionRegistrar { _ subject: Subject, keyPath: KeyPath ) { - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, - let subject = subject as? any Observable - { - func `open`(_ subject: S) { - return self.registrar.willSet( - subject, - keyPath: unsafeDowncast(keyPath, to: KeyPath.self) - ) + #if canImport(Observation) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, + let subject = subject as? any Observable + { + func `open`(_ subject: S) { + return self.registrar.willSet( + subject, + keyPath: unsafeDowncast(keyPath, to: KeyPath.self) + ) + } + return open(subject) } - return open(subject) - } + #endif return self.perceptionRegistrar.willSet(subject, keyPath: keyPath) } @@ -152,17 +166,19 @@ extension PerceptionRegistrar { _ subject: Subject, keyPath: KeyPath ) { - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, - let subject = subject as? any Observable - { - func `open`(_ subject: S) { - return self.registrar.didSet( - subject, - keyPath: unsafeDowncast(keyPath, to: KeyPath.self) - ) + #if canImport(Observation) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta, + let subject = subject as? any Observable + { + func `open`(_ subject: S) { + return self.registrar.didSet( + subject, + keyPath: unsafeDowncast(keyPath, to: KeyPath.self) + ) + } + return open(subject) } - return open(subject) - } + #endif return self.perceptionRegistrar.didSet(subject, keyPath: keyPath) } } diff --git a/Sources/PerceptionCore/PerceptionTracking.swift b/Sources/PerceptionCore/PerceptionTracking.swift index 53b36e7..1a31f19 100644 --- a/Sources/PerceptionCore/PerceptionTracking.swift +++ b/Sources/PerceptionCore/PerceptionTracking.swift @@ -209,9 +209,11 @@ public func withPerceptionTracking( _ apply: () -> T, onChange: @autoclosure () -> @Sendable () -> Void ) -> T { - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { - return withObservationTracking(apply, onChange: onChange()) - } + #if canImport(Observation) + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { + return withObservationTracking(apply, onChange: onChange()) + } + #endif let (result, accessList) = generateAccessList(apply) if let accessList { PerceptionTracking._installTracking(accessList, onChange: onChange()) diff --git a/Sources/PerceptionMacros/PerceptibleMacro.swift b/Sources/PerceptionMacros/PerceptibleMacro.swift index b4b8feb..f7e1cef 100644 --- a/Sources/PerceptionMacros/PerceptibleMacro.swift +++ b/Sources/PerceptionMacros/PerceptibleMacro.swift @@ -308,7 +308,7 @@ extension PerceptibleMacro: ExtensionMacro { } let decl: DeclSyntax = """ - extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName), Observation.Observable {} + extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName), Observable {} """ let ext = decl.cast(ExtensionDeclSyntax.self)