Skip to content

Commit ee1d361

Browse files
authored
Add support for SwiftUI environment (pointfreeco#13)
* Add support for SwiftUI environment * Update ExampleApp.swift * Update ExampleApp.swift * Update Environment.swift * Update Environment.swift * Update Environment.swift * wip * README
1 parent 6409a03 commit ee1d361

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,35 @@ set up correctly:
6969
> 🟣 Runtime Warning: Perceptible state was accessed but is not being tracked. Track changes to
7070
> state by wrapping your view in a 'WithPerceptionTracking' view.
7171
72+
### Bindable
73+
74+
SwiftUI's `@Bindable` property wrapper has also been backported to support perceptible objects. You
75+
can simply qualify the property wrapper with the `Perception` module:
76+
77+
```swift
78+
struct FeatureView: View {
79+
@Perception.Bindable var model: FeatureModel
80+
81+
// ...
82+
}
83+
```
84+
85+
### Environment
86+
87+
SwiftUI's `@Environment` property wrapper and `environment` view modifier's support for observation
88+
has also been backported to support perceptible objects using the exact same APIs:
89+
90+
```swift
91+
struct FeatureView: View {
92+
@Environment(Settings.self) var settings
93+
94+
// ...
95+
}
96+
97+
// In some parent view:
98+
.environment(settings)
99+
```
100+
72101
## Community
73102

74103
If you want to discuss this library or have a question about how to use it to solve

Sources/Perception/Environment.swift

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import SwiftUI
2+
3+
@available(iOS, introduced: 13, obsoleted: 17)
4+
@available(macOS, introduced: 10.15, obsoleted: 14)
5+
@available(tvOS, introduced: 13, obsoleted: 17)
6+
@available(watchOS, introduced: 6, obsoleted: 10)
7+
@available(visionOS, unavailable)
8+
extension Environment {
9+
/// Creates an environment property to read a perceptible object from the environment.
10+
///
11+
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
12+
///
13+
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
14+
@_disfavoredOverload
15+
public init(_ objectType: Value.Type) where Value: AnyObject & Perceptible {
16+
self.init(\.[unwrap: \Value.self])
17+
}
18+
19+
/// Creates an environment property to read a perceptible object from the environment, returning
20+
/// `nil` if no corresponding object has been set in the current view's environment.
21+
///
22+
/// A backport of SwiftUI's `Environment.init` that takes an observable object.
23+
///
24+
/// - Parameter objectType: The type of the `Perceptible` object to read from the environment.
25+
@_disfavoredOverload
26+
public init<T: AnyObject & Perceptible>(_ objectType: T.Type) where Value == T? {
27+
self.init(\.[\T.self])
28+
}
29+
}
30+
31+
@available(iOS, introduced: 13, obsoleted: 17)
32+
@available(macOS, introduced: 10.15, obsoleted: 14)
33+
@available(tvOS, introduced: 13, obsoleted: 17)
34+
@available(watchOS, introduced: 6, obsoleted: 10)
35+
@available(visionOS, unavailable)
36+
extension View {
37+
/// Places a perceptible object in the view’s environment.
38+
///
39+
/// A backport of SwiftUI's `View.environment` that takes an observable object.
40+
///
41+
/// - Parameter object: The object to set for this object's type in the environment, or `nil` to
42+
/// clear an object of this type from the environment.
43+
/// - Returns: A view that has the specified object in its environment.
44+
@_disfavoredOverload
45+
public func environment<T: AnyObject & Perceptible>(_ object: T?) -> some View {
46+
self.environment(\.[\T.self], object)
47+
}
48+
}
49+
50+
private struct PerceptibleKey<T: Perceptible>: EnvironmentKey {
51+
static var defaultValue: T? { nil }
52+
}
53+
54+
extension EnvironmentValues {
55+
fileprivate subscript<T: Perceptible>(_: KeyPath<T, T>) -> T? {
56+
get { self[PerceptibleKey<T>.self] }
57+
set { self[PerceptibleKey<T>.self] = newValue }
58+
}
59+
60+
fileprivate subscript<T: Perceptible>(unwrap _: KeyPath<T, T>) -> T {
61+
get {
62+
guard let object = self[\T.self] else {
63+
fatalError(
64+
"""
65+
No perceptible object of type \(T.self) found. A View.environment(_:) for \(T.self) may \
66+
be missing as an ancestor of this view.
67+
"""
68+
)
69+
}
70+
return object
71+
}
72+
set { self[\T.self] = newValue }
73+
}
74+
}

0 commit comments

Comments
 (0)