Skip to content

Commit f1faf90

Browse files
committed
Added SwiftUI environment helpers
1 parent 8e47148 commit f1faf90

3 files changed

Lines changed: 118 additions & 1 deletion

File tree

README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Helpful tools for Core Data
1717
- [ModelManager and ModelFileManager Protocols](#modelmanager-and-modelfilemanager-protocols)
1818
- [ModelVersion Protocol](#modelversion-protocol)
1919
- [PersistentHistoryTracker actor](#persistenthistorytracker-actor)
20+
- [SwiftUI Integration](#swiftui-integration)
2021
- [CRUD Helpers](#crud-helpers)
2122
- [Updatable Protocol](#updatable-protocol)
2223
- [Persistable Protocol](#persistable-protocol)
@@ -228,6 +229,37 @@ let stack = try BasicDataStack(
228229
stack.historyTracker?.startMonitoring()
229230
```
230231

232+
### SwiftUI Integration
233+
234+
You can add your `DataStack` and its `viewContext` to the SwiftUI environment in a single call:
235+
236+
```swift
237+
struct MyView: View {
238+
var myStack: MyDataStack
239+
240+
var body: some View {
241+
SubView()
242+
.dataStackEnvironment(myStack)
243+
}
244+
}
245+
```
246+
247+
The `.dataStackEnvironment(_:)` call wraps `.environment(_:,_:)` calls for both the DataStack and ManagedObjectContext, so you can access either environment value with the following property wrappers:
248+
249+
```swift
250+
struct SubView: View {
251+
/// This is a NSManagedObjectContext accessed by `myStack.viewContext`
252+
@Environment(\.managedObjectContext) var context
253+
254+
/// This is a (any DataStack)? equal to `myStack` from MyView.
255+
@EnvironmentDataStack var dataStack
256+
257+
var body: some View { ... }
258+
}
259+
```
260+
261+
You must set the DataStack into a view's environment using the `dataStackEnvironment(_:)` call in order to use the `@EnvironmentDataStack` property wrapper, or it will cause a fatal error.
262+
231263
# CRUD Helpers
232264

233265
### Updatable Protocol

Sources/RCDataKit/Data Stack/DataStackProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import CoreData
66
import Foundation
77

88
/// A type that provides some standard helper tools for managing a CoreData persistent store.
9-
public protocol DataStack {
9+
public protocol DataStack: Sendable {
1010
/// The `NSPersistentContainer` that the `DataStack` wraps.
1111
var container: NSPersistentContainer { get }
1212

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//
2+
// SwiftUI+DataStack.swift
3+
// RCDataKit
4+
//
5+
// Created by Ryan Linn on 11/10/24.
6+
//
7+
8+
import SwiftUI
9+
10+
extension View {
11+
/// Adds the `DataStack` to the View's environment.
12+
///
13+
/// This call includes adding the `DataStack`'s `viewContext` to the View environment, so child views
14+
/// can access both or either the `DataStack` and/or `managedObjectContext` through the following
15+
/// variable declarations:
16+
///
17+
/// ```swift
18+
/// struct MyView: View {
19+
/// // Get the DataStack's viewContext:
20+
/// @Environment(\.managedObjectContext) var context
21+
///
22+
/// // Get the DataStack
23+
/// @EnvironmentDataStack var dataStack
24+
/// }
25+
/// ```
26+
public func dataStackEnvironment(_ stack: any DataStack) -> some View {
27+
self
28+
.environment(\.dataStack, stack)
29+
.environment(\.managedObjectContext, stack.viewContext)
30+
}
31+
}
32+
33+
/// A property wrapper that accesses a `DataStack` from the view environment.
34+
///
35+
/// You must set the `DataStack` into the environment in a parent view to be able to access this environment
36+
/// variable this way. Otherwise, attempting to access `EnvironmentDataStack` will cause a fatal error.
37+
///
38+
/// ```swift
39+
/// struct ParentView: View {
40+
/// var dataStack: MyDataStack
41+
///
42+
/// var body: some View {
43+
/// ChildView()
44+
/// .dataStackEnvironment(dataStack)
45+
/// }
46+
/// }
47+
///
48+
/// struct ChildView: View {
49+
/// // accesses ParentView.dataStack as (any DataStack)
50+
/// @EnvironmentDataStack var dataStack
51+
///
52+
/// // accesses ParentView.dataStack.viewContext
53+
/// @Environment(\.managedObjectContext) var viewContext
54+
///
55+
/// var body: some View {
56+
/// Text("Hello World")
57+
/// }
58+
/// }
59+
/// ```
60+
@propertyWrapper public struct EnvironmentDataStack: DynamicProperty {
61+
@Environment(\.dataStack) var env: (any DataStack)?
62+
63+
public init() {
64+
_env = Environment(\.dataStack)
65+
}
66+
67+
public var wrappedValue: any DataStack {
68+
if let env {
69+
return env
70+
} else {
71+
fatalError("Attempted to access environment DataStack, but none was found")
72+
}
73+
}
74+
}
75+
76+
fileprivate extension EnvironmentValues {
77+
var dataStack: (any DataStack)? {
78+
get { self[DataStackKey.self] }
79+
set { self[DataStackKey.self] = newValue }
80+
}
81+
}
82+
83+
fileprivate struct DataStackKey: EnvironmentKey {
84+
static let defaultValue: (any DataStack)? = nil
85+
}

0 commit comments

Comments
 (0)