Skip to content

Commit

Permalink
Fix registrar overload resolution (#104)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
stephencelis and mbrandonw authored Nov 4, 2024
1 parent a25a34f commit 7a71cb8
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 112 deletions.
9 changes: 1 addition & 8 deletions Sources/Perception/Internal/Exports.swift
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion Sources/Perception/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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")

Expand Down
162 changes: 73 additions & 89 deletions Sources/Perception/PerceptionRegistrar.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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())
}
Expand All @@ -37,51 +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: Observable, Member>(
_ subject: Subject,
keyPath: KeyPath<Subject, Member>,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column
) {
self.registrar.access(subject, keyPath: keyPath)
}
@available(iOS 17, macOS 14, tvOS 17, watchOS 10, *)
extension PerceptionRegistrar {
public func access<Subject: Observable, Member>(
_ subject: Subject,
keyPath: KeyPath<Subject, Member>,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column
) {
self.registrar.access(subject, keyPath: keyPath)
}

public func withMutation<Subject: Observable, Member, T>(
of subject: Subject, keyPath: KeyPath<Subject, Member>, _ mutation: () throws -> T
) rethrows -> T {
try self.registrar.withMutation(of: subject, keyPath: keyPath, mutation)
}
public func withMutation<Subject: Observable, Member, T>(
of subject: Subject, keyPath: KeyPath<Subject, Member>, _ mutation: () throws -> T
) rethrows -> T {
try self.registrar.withMutation(of: subject, keyPath: keyPath, mutation)
}

public func willSet<Subject: Observable, Member>(
_ subject: Subject, keyPath: KeyPath<Subject, Member>
) {
self.registrar.willSet(subject, keyPath: keyPath)
}
public func willSet<Subject: Observable, Member>(
_ subject: Subject, keyPath: KeyPath<Subject, Member>
) {
self.registrar.willSet(subject, keyPath: keyPath)
}

public func didSet<Subject: Observable, Member>(
_ subject: Subject, keyPath: KeyPath<Subject, Member>
) {
self.registrar.didSet(subject, keyPath: keyPath)
}
public func didSet<Subject: Observable, Member>(
_ subject: Subject, keyPath: KeyPath<Subject, Member>
) {
self.registrar.didSet(subject, keyPath: keyPath)
}
#endif
}

extension PerceptionRegistrar {
@_disfavoredOverload
Expand All @@ -101,19 +93,17 @@ extension PerceptionRegistrar {
column: column
)
#endif
#if canImport(Observation)
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta {
func `open`<T: Observable>(_ subject: T) {
self.registrar.access(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<T, Member>.self)
)
}
if let subject = subject as? any Observable {
return open(subject)
}
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta {
func `open`<T: Observable>(_ subject: T) {
self.registrar.access(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<T, Member>.self)
)
}
#endif
if let subject = subject as? any Observable {
return open(subject)
}
}
self.perceptionRegistrar.access(subject, keyPath: keyPath)
}

Expand All @@ -123,20 +113,18 @@ extension PerceptionRegistrar {
keyPath: KeyPath<Subject, Member>,
_ 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`<S: Observable>(_ subject: S) throws -> T {
return try self.registrar.withMutation(
of: subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.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`<S: Observable>(_ subject: S) throws -> T {
return try self.registrar.withMutation(
of: subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.self),
mutation
)
}
#endif
return try open(subject)
}
return try self.perceptionRegistrar.withMutation(of: subject, keyPath: keyPath, mutation)
}

Expand All @@ -145,19 +133,17 @@ extension PerceptionRegistrar {
_ subject: Subject,
keyPath: KeyPath<Subject, Member>
) {
#if canImport(Observation)
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta,
let subject = subject as? any Observable
{
func `open`<S: Observable>(_ subject: S) {
return self.registrar.willSet(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.self)
)
}
return open(subject)
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta,
let subject = subject as? any Observable
{
func `open`<S: Observable>(_ subject: S) {
return self.registrar.willSet(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.self)
)
}
#endif
return open(subject)
}
return self.perceptionRegistrar.willSet(subject, keyPath: keyPath)
}

Expand All @@ -166,19 +152,17 @@ extension PerceptionRegistrar {
_ subject: Subject,
keyPath: KeyPath<Subject, Member>
) {
#if canImport(Observation)
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta,
let subject = subject as? any Observable
{
func `open`<S: Observable>(_ subject: S) {
return self.registrar.didSet(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.self)
)
}
return open(subject)
if #available(iOS 17, macOS 14, tvOS 17, watchOS 10, *), !isObservationBeta,
let subject = subject as? any Observable
{
func `open`<S: Observable>(_ subject: S) {
return self.registrar.didSet(
subject,
keyPath: unsafeDowncast(keyPath, to: KeyPath<S, Member>.self)
)
}
#endif
return open(subject)
}
return self.perceptionRegistrar.didSet(subject, keyPath: keyPath)
}
}
Expand Down
9 changes: 3 additions & 6 deletions Sources/Perception/PerceptionTracking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,12 +209,9 @@ public func withPerceptionTracking<T>(
_ 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())
Expand Down
9 changes: 1 addition & 8 deletions Sources/PerceptionMacros/PerceptibleMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
]
}
}
Expand Down

0 comments on commit 7a71cb8

Please sign in to comment.