Skip to content

Commit 3647e7d

Browse files
committed
Fix navigator resolution & @PersistentObject
1 parent d659baa commit 3647e7d

File tree

6 files changed

+147
-148
lines changed

6 files changed

+147
-148
lines changed

Sources/Intermodular/Helpers/AppKit or UIKit/AppKitOrUIKitViewControllerResolver.swift

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ private struct _ResolveAppKitOrUIKitViewController: ViewModifier {
226226
PassthroughView {
227227
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
228228
content
229-
.environment(\.navigator, (_appKitOrUIKitViewControllerBox.value?.navigationController).map(_UINavigationControllerNavigatorAdaptorBox.init))
229+
.modifier(ProvideNavigator(_appKitOrUIKitViewControllerBox: _appKitOrUIKitViewControllerBox))
230230
#elseif os(macOS)
231231
content
232232
#endif
@@ -252,6 +252,36 @@ private struct _ResolveAppKitOrUIKitViewController: ViewModifier {
252252
.id(_appKitOrUIKitViewControllerBox.value.map(ObjectIdentifier.init))
253253
}
254254
}
255+
256+
#if os(iOS) || os(tvOS) || targetEnvironment(macCatalyst)
257+
private struct ProvideNavigator: ViewModifier {
258+
struct Navigator: SwiftUIX.Navigator {
259+
var base: AppKitOrUIKitViewController?
260+
261+
private var nearestNavigator: _UINavigationControllerNavigatorAdaptorBox? {
262+
base?.nearestNavigationController.map(_UINavigationControllerNavigatorAdaptorBox.init(navigationController:))
263+
}
264+
265+
func push<V: View>(_ view: V, withAnimation animation: Animation?) {
266+
nearestNavigator?.push(view, withAnimation: animation)
267+
}
268+
269+
func pop(withAnimation animation: Animation?) {
270+
nearestNavigator?.pop(withAnimation: animation)
271+
}
272+
273+
func popToRoot(withAnimation animation: Animation?) {
274+
nearestNavigator?.popToRoot(withAnimation: animation)
275+
}
276+
}
277+
278+
@ObservedObject var _appKitOrUIKitViewControllerBox: ObservableWeakReferenceBox<AppKitOrUIKitViewController>
279+
280+
func body(content: Content) -> some View {
281+
content.environment(\.navigator, Navigator(base: _appKitOrUIKitViewControllerBox.value))
282+
}
283+
}
284+
#endif
255285
}
256286

257287
#endif
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// Copyright (c) Vatsal Manot
3+
//
4+
5+
import Combine
6+
import Swift
7+
8+
final class _AnyObservableObject: ObservableObject {
9+
private class _EmptyObservableObject: ObservableObject {
10+
init() {
11+
12+
}
13+
}
14+
15+
static let empty = _AnyObservableObject(_EmptyObservableObject())
16+
17+
let base: AnyObject
18+
19+
private let objectWillChangeImpl: () -> AnyPublisher<Void, Never>
20+
21+
var objectWillChange: AnyPublisher<Void, Never> {
22+
objectWillChangeImpl()
23+
}
24+
25+
init<T: ObservableObject>(_ base: T) {
26+
self.base = base
27+
self.objectWillChangeImpl = { base.objectWillChange.map({ _ in () }).eraseToAnyPublisher() }
28+
}
29+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
//
2+
// Copyright (c) Vatsal Manot
3+
//
4+
5+
import Combine
6+
import Swift
7+
8+
final class _OptionalObservedObjectContainer<ObjectType: ObservableObject>: ObservableObject {
9+
private var baseSubscription: AnyCancellable?
10+
11+
var onObjectWillChange: () -> Void = { }
12+
13+
var base: ObjectType? {
14+
didSet {
15+
if let oldValue = oldValue, let base = base {
16+
if oldValue === base, baseSubscription != nil {
17+
return
18+
}
19+
}
20+
21+
subscribe()
22+
}
23+
}
24+
25+
init(base: ObjectType? = nil) {
26+
self.base = base
27+
28+
subscribe()
29+
}
30+
31+
private func subscribe() {
32+
guard let base = base else {
33+
return
34+
}
35+
36+
baseSubscription = base
37+
.objectWillChange
38+
.receive(on: DispatchQueue.main)
39+
.sink(receiveValue: { [weak self] _ in
40+
guard let `self` = self else {
41+
return
42+
}
43+
44+
DispatchQueue.asyncOnMainIfNecessary {
45+
`self`.objectWillChange.send()
46+
`self`.onObjectWillChange()
47+
}
48+
})
49+
}
50+
}

Sources/Intramodular/Dynamic Properties/EnvironmentObjectOrObservedObject.swift

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,12 @@ public struct EnvironmentObjectOrObservedObject<Value: ObservableObject>: Dynami
1414
@OptionalObservedObject private var _wrappedValue1: Value?
1515

1616
public var wrappedValue: Value {
17-
get {
18-
if let result = _wrappedValue1 ?? _wrappedValue0 {
19-
return result
20-
} else {
21-
assertionFailure()
22-
23-
return defaultValue()
24-
}
25-
} set {
26-
_wrappedValue1 = newValue
17+
if let result = _wrappedValue1 ?? _wrappedValue0 {
18+
return result
19+
} else {
20+
assertionFailure()
21+
22+
return defaultValue()
2723
}
2824
}
2925

@@ -34,7 +30,7 @@ public struct EnvironmentObjectOrObservedObject<Value: ObservableObject>: Dynami
3430

3531
public mutating func update() {
3632
if _wrappedValue0 == nil {
37-
_wrappedValue1 = defaultValue()
33+
__wrappedValue1 = .init(wrappedValue: defaultValue())
3834
}
3935

4036
self.__wrappedValue0.update()

Sources/Intramodular/Dynamic Properties/OptionalObservedObject.swift

Lines changed: 9 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -11,139 +11,29 @@ import SwiftUI
1111
public struct OptionalObservedObject<ObjectType: ObservableObject>: DynamicProperty {
1212
private typealias Container = _OptionalObservedObjectContainer<ObjectType>
1313

14-
@State
15-
private var dummyStateVariable: Bool = false
16-
@ObservedObject
17-
private var dummyObservedObject = _DummyObservableObject()
18-
@State
19-
private var base: ObjectType?
20-
@State
21-
private var container: Container
22-
@ObservedObject
23-
private var observedContainer: Container
24-
@ObservedObject
25-
private var observedObject: _AnyObservableObject
14+
private let base: ObjectType?
15+
16+
@ObservedObject private var observedContainer = Container(base: nil)
2617

2718
/// The current state value.
2819
public var wrappedValue: ObjectType? {
29-
get {
30-
container.base
31-
} nonmutating set {
32-
base = newValue
33-
container.base = newValue
34-
observedContainer.base = newValue
35-
36-
container.onObjectWillChange = {
37-
dummyObservedObject.objectWillChange.send()
38-
}
39-
40-
dummyStateVariable.toggle()
20+
if observedContainer.base !== base {
21+
observedContainer.base = base
4122
}
23+
24+
return base
4225
}
4326

4427
/// Initialize with the provided initial value.
4528
public init(wrappedValue value: ObjectType?) {
46-
let container = Container(base: value)
47-
48-
self.container = container
49-
self.observedContainer = container
50-
self.observedObject = value.map(_AnyObservableObject.init) ?? .empty
29+
self.base = value
5130
}
5231

5332
public init() {
5433
self.init(wrappedValue: nil)
5534
}
5635

5736
public mutating func update() {
58-
let container = self.container
59-
60-
if self.observedContainer !== container {
61-
self.observedContainer = container
62-
}
63-
64-
if let base = container.base, container.isDirty {
65-
self.observedContainer = container
66-
self.observedObject = _AnyObservableObject(base)
67-
68-
container.isDirty = false
69-
}
70-
}
71-
}
72-
73-
// MARK: - Auxiliary Implementation -
74-
75-
private class _DummyObservableObject: ObservableObject {
76-
init() {
77-
78-
}
79-
}
80-
81-
private final class _OptionalObservedObjectContainer<ObjectType: ObservableObject>: ObservableObject {
82-
private var baseSubscription: AnyCancellable?
83-
84-
var onObjectWillChange: () -> Void = { }
85-
var isDirty: Bool = false
86-
87-
var base: ObjectType? {
88-
didSet {
89-
if let oldValue = oldValue, let base = base {
90-
if oldValue === base, baseSubscription != nil {
91-
return
92-
}
93-
}
94-
95-
subscribe()
96-
97-
isDirty = true
98-
}
99-
}
100-
101-
init(base: ObjectType?) {
102-
self.base = base
103-
104-
subscribe()
105-
}
106-
107-
private func subscribe() {
108-
guard let base = base else {
109-
return
110-
}
111-
112-
baseSubscription = base
113-
.objectWillChange
114-
.receive(on: DispatchQueue.main)
115-
.sink(receiveValue: { [weak self] _ in
116-
guard let `self` = self else {
117-
return
118-
}
119-
120-
DispatchQueue.asyncOnMainIfNecessary {
121-
`self`.objectWillChange.send()
122-
`self`.onObjectWillChange()
123-
}
124-
})
125-
}
126-
}
127-
128-
private final class _AnyObservableObject: ObservableObject {
129-
private class _EmptyObservableObject: ObservableObject {
130-
init() {
131-
132-
}
133-
}
134-
135-
static let empty = _AnyObservableObject(_EmptyObservableObject())
136-
137-
let base: AnyObject
138-
139-
private let objectWillChangeImpl: () -> AnyPublisher<Void, Never>
140-
141-
var objectWillChange: AnyPublisher<Void, Never> {
142-
objectWillChangeImpl()
143-
}
144-
145-
init<T: ObservableObject>(_ base: T) {
146-
self.base = base
147-
self.objectWillChangeImpl = { base.objectWillChange.map({ _ in () }).eraseToAnyPublisher() }
37+
_observedContainer.update()
14838
}
14939
}

Sources/Intramodular/Dynamic Properties/PersistentObject.swift

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,42 @@ import SwiftUI
1111
public struct PersistentObject<ObjectType: ObservableObject>: DynamicProperty {
1212
private let thunk: () -> ObjectType
1313

14-
@OptionalObservedObject
15-
private var observedObject: ObjectType?
16-
@State
17-
private var state = ReferenceBox<ObjectType?>(nil)
14+
@State private var objectContainer = _OptionalObservedObjectContainer<ObjectType>()
15+
16+
@ObservedObject private var observedObjectContainer = _OptionalObservedObjectContainer<ObjectType>()
1817

1918
public var wrappedValue: ObjectType {
2019
get {
21-
if state.value == nil {
22-
state.value = thunk()
20+
if let object = objectContainer.base {
21+
if observedObjectContainer.base !== object {
22+
observedObjectContainer.base = object
23+
}
24+
25+
return object
26+
} else {
27+
let object = thunk()
28+
29+
objectContainer.base = object
30+
observedObjectContainer.base = object
31+
32+
return object
2333
}
24-
25-
return state.value!
2634
} nonmutating set {
27-
state.value = newValue
28-
observedObject = newValue
35+
objectContainer.base = newValue
36+
observedObjectContainer.base = newValue
2937
}
3038
}
3139

3240
public var projectedValue: ObservedObject<ObjectType>.Wrapper {
33-
ObservedObject(wrappedValue: observedObject!).projectedValue
41+
ObservedObject(wrappedValue: wrappedValue).projectedValue
3442
}
3543

3644
public init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType) {
3745
self.thunk = thunk
3846
}
3947

4048
public mutating func update() {
41-
if state.value == nil {
42-
let object = thunk()
43-
44-
state.value = object
45-
observedObject = object
46-
}
49+
_objectContainer.update()
50+
_observedObjectContainer.update()
4751
}
4852
}

0 commit comments

Comments
 (0)