From 7a76798aa41cc3c6251b0cc1001c949a409b9e94 Mon Sep 17 00:00:00 2001 From: Brandon Williams <135203+mbrandonw@users.noreply.github.com> Date: Tue, 5 Nov 2024 13:43:27 -0500 Subject: [PATCH] Fix signature of `access` (#103) * Fix signature of 'access' method. wip * Fix registrar overload resolution (#104) * Fix registrar overload resolution It looks like the `@availability` check we apply to the extension that adds an `Observable` conformance prevents iOS 17+ deployment targets from calling the correct version of `access`, which leads to errant perception checks. * Update Sources/Perception/Perceptible.swift Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com> --------- Co-authored-by: Brandon Williams <135203+mbrandonw@users.noreply.github.com> --------- Co-authored-by: Stephen Celis --- Sources/Perception/Internal/Exports.swift | 9 +- Sources/Perception/Macros.swift | 2 +- Sources/Perception/PerceptionRegistrar.swift | 157 ++++++++---------- Sources/Perception/PerceptionTracking.swift | 9 +- .../PerceptionMacros/PerceptibleMacro.swift | 9 +- 5 files changed, 79 insertions(+), 107 deletions(-) diff --git a/Sources/Perception/Internal/Exports.swift b/Sources/Perception/Internal/Exports.swift index 3814f9c..66f9aa8 100644 --- a/Sources/Perception/Internal/Exports.swift +++ b/Sources/Perception/Internal/Exports.swift @@ -1,8 +1 @@ -#if canImport(Observation) - @_exported import Observation - @available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) - public typealias _Observable = Observation.Observable -#else - @available(macOS 14, iOS 17, watchOS 10, tvOS 17, *) - public protocol _Observable {} -#endif +@_exported import Observation diff --git a/Sources/Perception/Macros.swift b/Sources/Perception/Macros.swift index 7eba273..471992c 100644 --- a/Sources/Perception/Macros.swift +++ b/Sources/Perception/Macros.swift @@ -16,7 +16,7 @@ @attached( member, names: named(_$id), named(_$perceptionRegistrar), named(access), named(withMutation)) @attached(memberAttribute) -@attached(extension, conformances: Perceptible, _Observable) +@attached(extension, conformances: Perceptible, Observable) public macro Perceptible() = #externalMacro(module: "PerceptionMacros", type: "PerceptibleMacro") diff --git a/Sources/Perception/PerceptionRegistrar.swift b/Sources/Perception/PerceptionRegistrar.swift index cdfc56d..118edb3 100644 --- a/Sources/Perception/PerceptionRegistrar.swift +++ b/Sources/Perception/PerceptionRegistrar.swift @@ -24,11 +24,7 @@ public struct PerceptionRegistrar: Sendable { /// of a type. public init(isPerceptionCheckingEnabled: Bool = Perception.isPerceptionCheckingEnabled) { if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { - #if canImport(Observation) - self._rawValue = AnySendable(ObservationRegistrar()) - #else - self._rawValue = AnySendable(_PerceptionRegistrar()) - #endif + self._rawValue = AnySendable(ObservationRegistrar()) } else { self._rawValue = AnySendable(_PerceptionRegistrar()) } @@ -37,46 +33,47 @@ public struct PerceptionRegistrar: Sendable { #endif } - #if canImport(Observation) - @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - private var registrar: ObservationRegistrar { - self._rawValue.base as! ObservationRegistrar - } - #endif + @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) + private var registrar: ObservationRegistrar { + self._rawValue.base as! ObservationRegistrar + } private var perceptionRegistrar: _PerceptionRegistrar { self._rawValue.base as! _PerceptionRegistrar } } -#if canImport(Observation) - @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - extension PerceptionRegistrar { - public func access( - _ subject: Subject, keyPath: KeyPath - ) { - self.registrar.access(subject, keyPath: keyPath) - } +@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 @@ -96,19 +93,17 @@ extension PerceptionRegistrar { column: column ) #endif - #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) - } + 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) + ) } - #endif + if let subject = subject as? any Observable { + return open(subject) + } + } self.perceptionRegistrar.access(subject, keyPath: keyPath) } @@ -118,20 +113,18 @@ extension PerceptionRegistrar { keyPath: KeyPath, _ mutation: () throws -> T ) rethrows -> T { - #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) + 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 + ) } - #endif + return try open(subject) + } return try self.perceptionRegistrar.withMutation(of: subject, keyPath: keyPath, mutation) } @@ -140,19 +133,17 @@ extension PerceptionRegistrar { _ subject: Subject, keyPath: KeyPath ) { - #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) + 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) + ) } - #endif + return open(subject) + } return self.perceptionRegistrar.willSet(subject, keyPath: keyPath) } @@ -161,19 +152,17 @@ extension PerceptionRegistrar { _ subject: Subject, keyPath: KeyPath ) { - #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) + 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) + ) } - #endif + return open(subject) + } return self.perceptionRegistrar.didSet(subject, keyPath: keyPath) } } diff --git a/Sources/Perception/PerceptionTracking.swift b/Sources/Perception/PerceptionTracking.swift index c28932b..53b36e7 100644 --- a/Sources/Perception/PerceptionTracking.swift +++ b/Sources/Perception/PerceptionTracking.swift @@ -209,12 +209,9 @@ public func withPerceptionTracking( _ apply: () -> T, onChange: @autoclosure () -> @Sendable () -> Void ) -> T { - #if canImport(Observation) - if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { - return withObservationTracking(apply, onChange: onChange()) - } - #endif - + if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta { + return withObservationTracking(apply, onChange: onChange()) + } 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 9691ec1..b4b8feb 100644 --- a/Sources/PerceptionMacros/PerceptibleMacro.swift +++ b/Sources/PerceptionMacros/PerceptibleMacro.swift @@ -308,24 +308,17 @@ extension PerceptibleMacro: ExtensionMacro { } let decl: DeclSyntax = """ - extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName) {} - """ - let obsDecl: DeclSyntax = """ - @available(iOS 17, macOS 14, tvOS 17, watchOS 10, *) - extension \(raw: type.trimmedDescription): _Observable {} + extension \(raw: type.trimmedDescription): \(raw: qualifiedConformanceName), Observation.Observable {} """ let ext = decl.cast(ExtensionDeclSyntax.self) - let obsExt = obsDecl.cast(ExtensionDeclSyntax.self) if let availability = declaration.attributes.availability { return [ ext.with(\.attributes, availability), - obsExt.with(\.attributes, availability), ] } else { return [ ext, - obsExt, ] } }