Skip to content

Commit

Permalink
Gate SwiftUI code with #if canImport (#75)
Browse files Browse the repository at this point in the history
* Gate SwiftUI code with `#if canImport`

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip

* wip
  • Loading branch information
stephencelis authored May 29, 2024
1 parent a459ec9 commit 9b77fbd
Show file tree
Hide file tree
Showing 10 changed files with 278 additions and 288 deletions.
128 changes: 65 additions & 63 deletions Sources/Perception/Environment.swift
Original file line number Diff line number Diff line change
@@ -1,74 +1,76 @@
import SwiftUI
#if canImport(SwiftUI)
import SwiftUI

@available(iOS, introduced: 13, obsoleted: 17)
@available(macOS, introduced: 10.15, obsoleted: 14)
@available(tvOS, introduced: 13, obsoleted: 17)
@available(watchOS, introduced: 6, obsoleted: 10)
@available(visionOS, unavailable)
extension Environment {
/// Creates an environment property to read a perceptible object from the environment.
///
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
///
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
@_disfavoredOverload
public init(_ objectType: Value.Type) where Value: AnyObject & Perceptible {
self.init(\.[unwrap: \Value.self])
}
@available(iOS, introduced: 13, obsoleted: 17)
@available(macOS, introduced: 10.15, obsoleted: 14)
@available(tvOS, introduced: 13, obsoleted: 17)
@available(watchOS, introduced: 6, obsoleted: 10)
@available(visionOS, unavailable)
extension Environment {
/// Creates an environment property to read a perceptible object from the environment.
///
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
///
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
@_disfavoredOverload
public init(_ objectType: Value.Type) where Value: AnyObject & Perceptible {
self.init(\.[unwrap: \Value.self])
}

/// Creates an environment property to read a perceptible object from the environment, returning
/// `nil` if no corresponding object has been set in the current view's environment.
///
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
///
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
@_disfavoredOverload
public init<T: AnyObject & Perceptible>(_ objectType: T.Type) where Value == T? {
self.init(\.[\T.self])
/// Creates an environment property to read a perceptible object from the environment, returning
/// `nil` if no corresponding object has been set in the current view's environment.
///
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
///
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
@_disfavoredOverload
public init<T: AnyObject & Perceptible>(_ objectType: T.Type) where Value == T? {
self.init(\.[\T.self])
}
}
}

@available(iOS, introduced: 13, obsoleted: 17)
@available(macOS, introduced: 10.15, obsoleted: 14)
@available(tvOS, introduced: 13, obsoleted: 17)
@available(watchOS, introduced: 6, obsoleted: 10)
@available(visionOS, unavailable)
extension View {
/// Places a perceptible object in the view’s environment.
///
/// A backport of SwiftUI's `View.environment` that takes an observable object.
///
/// - Parameter object: The object to set for this object's type in the environment, or `nil` to
/// clear an object of this type from the environment.
/// - Returns: A view that has the specified object in its environment.
@_disfavoredOverload
public func environment<T: AnyObject & Perceptible>(_ object: T?) -> some View {
self.environment(\.[\T.self], object)
@available(iOS, introduced: 13, obsoleted: 17)
@available(macOS, introduced: 10.15, obsoleted: 14)
@available(tvOS, introduced: 13, obsoleted: 17)
@available(watchOS, introduced: 6, obsoleted: 10)
@available(visionOS, unavailable)
extension View {
/// Places a perceptible object in the view’s environment.
///
/// A backport of SwiftUI's `View.environment` that takes an observable object.
///
/// - Parameter object: The object to set for this object's type in the environment, or `nil` to
/// clear an object of this type from the environment.
/// - Returns: A view that has the specified object in its environment.
@_disfavoredOverload
public func environment<T: AnyObject & Perceptible>(_ object: T?) -> some View {
self.environment(\.[\T.self], object)
}
}
}

private struct PerceptibleKey<T: Perceptible>: EnvironmentKey {
static var defaultValue: T? { nil }
}

extension EnvironmentValues {
fileprivate subscript<T: Perceptible>(_: KeyPath<T, T>) -> T? {
get { self[PerceptibleKey<T>.self] }
set { self[PerceptibleKey<T>.self] = newValue }
private struct PerceptibleKey<T: Perceptible>: EnvironmentKey {
static var defaultValue: T? { nil }
}

fileprivate subscript<T: Perceptible>(unwrap _: KeyPath<T, T>) -> T {
get {
guard let object = self[\T.self] else {
fatalError(
"""
No perceptible object of type \(T.self) found. A View.environment(_:) for \(T.self) may \
be missing as an ancestor of this view.
"""
)
extension EnvironmentValues {
fileprivate subscript<T: Perceptible>(_: KeyPath<T, T>) -> T? {
get { self[PerceptibleKey<T>.self] }
set { self[PerceptibleKey<T>.self] = newValue }
}

fileprivate subscript<T: Perceptible>(unwrap _: KeyPath<T, T>) -> T {
get {
guard let object = self[\T.self] else {
fatalError(
"""
No perceptible object of type \(T.self) found. A View.environment(_:) for \(T.self) may \
be missing as an ancestor of this view.
"""
)
}
return object
}
return object
set { self[\T.self] = newValue }
}
set { self[\T.self] = newValue }
}
}
#endif
9 changes: 8 additions & 1 deletion Sources/Perception/Internal/Exports.swift
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
@_exported import Observation
#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
9 changes: 9 additions & 0 deletions Sources/Perception/Internal/Locals.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@available(iOS, deprecated: 17)
@available(macOS, deprecated: 14)
@available(tvOS, deprecated: 17)
@available(visionOS, deprecated: 9999)
@available(watchOS, deprecated: 10)
public enum _PerceptionLocals {
@TaskLocal public static var isInPerceptionTracking = false
@TaskLocal public static var skipPerceptionChecking = false
}
17 changes: 8 additions & 9 deletions Sources/Perception/Internal/ThreadLocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@
import Foundation

struct _ThreadLocal {
static var value: UnsafeMutableRawPointer? {
get {
Thread.current.threadDictionary[Key()] as! UnsafeMutableRawPointer?
#if os(WASI)
static var value: UnsafeMutableRawPointer?
#else
static var value: UnsafeMutableRawPointer? {
get { Thread.current.threadDictionary[Key()] as! UnsafeMutableRawPointer? }
set { Thread.current.threadDictionary[Key()] = newValue }
}
set {
Thread.current.threadDictionary[Key()] = newValue
}
}
private struct Key: Hashable {}
#endif
}

private struct Key: Hashable {}
54 changes: 25 additions & 29 deletions Sources/Perception/Macros.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,30 @@
//
//===----------------------------------------------------------------------===//

#if canImport(Observation)
import Observation
@available(iOS, deprecated: 17, renamed: "Observable")
@available(macOS, deprecated: 14, renamed: "Observable")
@available(tvOS, deprecated: 17, renamed: "Observable")
@available(watchOS, deprecated: 10, renamed: "Observable")
@attached(
member, names: named(_$id), named(_$perceptionRegistrar), named(access), named(withMutation))
@attached(memberAttribute)
@attached(extension, conformances: Perceptible, _Observable)
public macro Perceptible() =
#externalMacro(module: "PerceptionMacros", type: "PerceptibleMacro")

@available(iOS, deprecated: 17, renamed: "Observable")
@available(macOS, deprecated: 14, renamed: "Observable")
@available(tvOS, deprecated: 17, renamed: "Observable")
@available(watchOS, deprecated: 10, renamed: "Observable")
@attached(
member, names: named(_$id), named(_$perceptionRegistrar), named(access), named(withMutation))
@attached(memberAttribute)
@attached(extension, conformances: Observable, Perceptible)
public macro Perceptible() =
#externalMacro(module: "PerceptionMacros", type: "PerceptibleMacro")
@available(iOS, deprecated: 17, renamed: "ObservationTracked")
@available(macOS, deprecated: 14, renamed: "ObservationTracked")
@available(tvOS, deprecated: 17, renamed: "ObservationTracked")
@available(watchOS, deprecated: 10, renamed: "ObservationTracked")
@attached(accessor, names: named(init), named(get), named(set))
@attached(peer, names: prefixed(_))
public macro PerceptionTracked() =
#externalMacro(module: "PerceptionMacros", type: "PerceptionTrackedMacro")

@available(iOS, deprecated: 17, renamed: "ObservationTracked")
@available(macOS, deprecated: 14, renamed: "ObservationTracked")
@available(tvOS, deprecated: 17, renamed: "ObservationTracked")
@available(watchOS, deprecated: 10, renamed: "ObservationTracked")
@attached(accessor, names: named(init), named(get), named(set))
@attached(peer, names: prefixed(_))
public macro PerceptionTracked() =
#externalMacro(module: "PerceptionMacros", type: "PerceptionTrackedMacro")

@available(iOS, deprecated: 17, renamed: "ObservationIgnored")
@available(macOS, deprecated: 14, renamed: "ObservationIgnored")
@available(tvOS, deprecated: 17, renamed: "ObservationIgnored")
@available(watchOS, deprecated: 10, renamed: "ObservationIgnored")
@attached(accessor, names: named(willSet))
public macro PerceptionIgnored() =
#externalMacro(module: "PerceptionMacros", type: "PerceptionIgnoredMacro")
#endif
@available(iOS, deprecated: 17, renamed: "ObservationIgnored")
@available(macOS, deprecated: 14, renamed: "ObservationIgnored")
@available(tvOS, deprecated: 17, renamed: "ObservationIgnored")
@available(watchOS, deprecated: 10, renamed: "ObservationIgnored")
@attached(accessor, names: named(willSet))
public macro PerceptionIgnored() =
#externalMacro(module: "PerceptionMacros", type: "PerceptionIgnoredMacro")
51 changes: 20 additions & 31 deletions Sources/Perception/PerceptionRegistrar.swift
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
import Foundation
import SwiftUI

#if canImport(Observation)
import Observation
#endif

/// Provides storage for tracking and access to data changes.
///
Expand Down Expand Up @@ -91,7 +86,7 @@ extension PerceptionRegistrar {
file: StaticString = #file,
line: UInt = #line
) {
#if DEBUG
#if DEBUG && canImport(SwiftUI)
self.perceptionCheck(file: file, line: line)
#endif
#if canImport(Observation)
Expand All @@ -103,12 +98,11 @@ extension PerceptionRegistrar {
)
}
if let subject = subject as? any Observable {
open(subject)
return open(subject)
}
} else {
self.perceptionRegistrar.access(subject, keyPath: keyPath)
}
#endif
self.perceptionRegistrar.access(subject, keyPath: keyPath)
}

@_disfavoredOverload
Expand All @@ -129,12 +123,9 @@ extension PerceptionRegistrar {
)
}
return try open(subject)
} else {
return try self.perceptionRegistrar.withMutation(of: subject, keyPath: keyPath, mutation)
}
#else
return try mutation()
#endif
return try self.perceptionRegistrar.withMutation(of: subject, keyPath: keyPath, mutation)
}

@_disfavoredOverload
Expand All @@ -153,10 +144,9 @@ extension PerceptionRegistrar {
)
}
return open(subject)
} else {
return self.perceptionRegistrar.willSet(subject, keyPath: keyPath)
}
#endif
return self.perceptionRegistrar.willSet(subject, keyPath: keyPath)
}

@_disfavoredOverload
Expand All @@ -175,10 +165,9 @@ extension PerceptionRegistrar {
)
}
return open(subject)
} else {
return self.perceptionRegistrar.didSet(subject, keyPath: keyPath)
}
#endif
return self.perceptionRegistrar.didSet(subject, keyPath: keyPath)
}
}

Expand All @@ -205,7 +194,7 @@ extension PerceptionRegistrar: Hashable {
}
}

#if DEBUG
#if DEBUG && canImport(SwiftUI)
extension PerceptionRegistrar {
fileprivate func perceptionCheck(file: StaticString, line: UInt) {
if self.isPerceptionCheckingEnabled,
Expand Down Expand Up @@ -254,7 +243,6 @@ extension PerceptionRegistrar: Hashable {
}
}


extension String {
var isGeometryTrailingClosure: Bool {
self.contains("(SwiftUI.GeometryProxy) -> ")
Expand All @@ -265,18 +253,19 @@ extension PerceptionRegistrar: Hashable {
return fragment.starts(
with: " suspend resume partial function for closure".utf8
)
|| fragment.starts(
with: " suspend resume partial function for implicit closure".utf8
)
|| fragment.starts(
with: " await resume partial function for partial apply forwarder for closure".utf8
)
|| fragment.starts(
with: " await resume partial function for partial apply forwarder for implicit closure".utf8
)
|| fragment.starts(
with: " await resume partial function for implicit closure".utf8
)
|| fragment.starts(
with: " suspend resume partial function for implicit closure".utf8
)
|| fragment.starts(
with: " await resume partial function for partial apply forwarder for closure".utf8
)
|| fragment.starts(
with: " await resume partial function for partial apply forwarder for implicit closure"
.utf8
)
|| fragment.starts(
with: " await resume partial function for implicit closure".utf8
)
}
fileprivate var isActionClosure: Bool {
var view = self[...].utf8
Expand Down
4 changes: 0 additions & 4 deletions Sources/Perception/PerceptionTracking.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
#if canImport(Observation)
import Observation
#endif

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
Expand Down
Loading

0 comments on commit 9b77fbd

Please sign in to comment.