Skip to content

Commit 30fcbff

Browse files
committed
wip
wip wip wip
1 parent e7f0876 commit 30fcbff

File tree

3 files changed

+81
-74
lines changed

3 files changed

+81
-74
lines changed

Sources/Introspect.swift

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@ extension View {
2020
customize: @escaping (PlatformSpecificEntity) -> Void
2121
) -> some View {
2222
if let platform = platforms.first(where: \.isCurrent) {
23-
let anchorID = IntrospectionAnchorID()
23+
let introspectionViewID = IntrospectionViewID()
2424
self.background(
2525
IntrospectionAnchorView(
26-
id: anchorID
26+
id: introspectionViewID
2727
)
2828
.frame(width: 0, height: 0)
2929
)
3030
.overlay(
3131
IntrospectionView(
32-
selector: { entity in
33-
(platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID)
32+
id: introspectionViewID,
33+
selector: { controller in
34+
(platform.selector ?? .default)(controller, scope ?? viewType.scope)
3435
},
3536
customize: customize
3637
)
@@ -53,9 +54,6 @@ public protocol PlatformEntity: AnyObject {
5354

5455
@_spi(Internals)
5556
func isDescendant(of other: Base) -> Bool
56-
57-
@_spi(Internals)
58-
func entityWithTag(_ tag: Int) -> Base?
5957
}
6058

6159
extension PlatformEntity {
@@ -87,12 +85,11 @@ extension PlatformEntity {
8785
}
8886

8987
func receiver<PlatformSpecificEntity: PlatformEntity>(
90-
ofType type: PlatformSpecificEntity.Type,
91-
anchorID: IntrospectionAnchorID
88+
ofType type: PlatformSpecificEntity.Type
9289
) -> PlatformSpecificEntity? {
9390
let frontEntity = self
9491
guard
95-
let backEntity = ContiguousArray(frontEntity.ancestors).last?.entityWithTag(anchorID.hashValue), // optimize this... maybe there's a way to hold a ref somewhere in memory without having to use tags?
92+
let backEntity = frontEntity.introspectionAnchorEntity,
9693
let commonAncestor = backEntity.nearestCommonAncestor(with: frontEntity~)
9794
else {
9895
return nil
@@ -124,11 +121,6 @@ extension PlatformView: PlatformEntity {
124121
public var descendants: [PlatformView] {
125122
subviews
126123
}
127-
128-
@_spi(Internals)
129-
public func entityWithTag(_ tag: Int) -> PlatformView? {
130-
viewWithTag(tag)
131-
}
132124
}
133125

134126
extension PlatformViewController: PlatformEntity {
@@ -146,17 +138,4 @@ extension PlatformViewController: PlatformEntity {
146138
public func isDescendant(of other: PlatformViewController) -> Bool {
147139
self.ancestors.contains(other)
148140
}
149-
150-
@_spi(Internals)
151-
public func entityWithTag(_ tag: Int) -> PlatformViewController? {
152-
if self.view.tag == tag {
153-
return self
154-
}
155-
for child in children {
156-
if let childWithTag = child.entityWithTag(tag) {
157-
return childWithTag
158-
}
159-
}
160-
return nil
161-
}
162141
}

Sources/IntrospectionSelector.swift

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,28 @@ public struct IntrospectionSelector<Target: PlatformEntity> {
66
@_spi(Internals)
77
public static func from<Entry: PlatformEntity>(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self {
88
.init(
9-
receiverSelector: { controller, anchorID in
10-
controller.as(Entry.self)?.receiver(ofType: Entry.self, anchorID: anchorID).flatMap(selector)
9+
receiverSelector: { controller in
10+
controller.as(Entry.Base.self)?.receiver(ofType: Entry.self).flatMap(selector)
1111
},
1212
ancestorSelector: { controller in
13-
controller.as(Entry.self)?.ancestor(ofType: Entry.self).flatMap(selector)
13+
controller.as(Entry.Base.self)?.ancestor(ofType: Entry.self).flatMap(selector)
1414
}
1515
)
1616
}
1717

18-
private var receiverSelector: (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?
18+
private var receiverSelector: (IntrospectionPlatformViewController) -> Target?
1919
private var ancestorSelector: (IntrospectionPlatformViewController) -> Target?
2020

2121
private init(
22-
receiverSelector: @escaping (IntrospectionPlatformViewController, IntrospectionAnchorID) -> Target?,
22+
receiverSelector: @escaping (IntrospectionPlatformViewController) -> Target?,
2323
ancestorSelector: @escaping (IntrospectionPlatformViewController) -> Target?
2424
) {
2525
self.receiverSelector = receiverSelector
2626
self.ancestorSelector = ancestorSelector
2727
}
2828

2929
@_spi(Internals)
30-
public func withReceiverSelector(_ selector: @escaping (PlatformViewController, IntrospectionAnchorID) -> Target?) -> Self {
30+
public func withReceiverSelector(_ selector: @escaping (PlatformViewController) -> Target?) -> Self {
3131
var copy = self
3232
copy.receiverSelector = selector
3333
return copy
@@ -40,14 +40,10 @@ public struct IntrospectionSelector<Target: PlatformEntity> {
4040
return copy
4141
}
4242

43-
func callAsFunction(
44-
_ controller: IntrospectionPlatformViewController,
45-
_ scope: IntrospectionScope,
46-
_ anchorID: IntrospectionAnchorID
47-
) -> Target? {
43+
func callAsFunction(_ controller: IntrospectionPlatformViewController, _ scope: IntrospectionScope) -> Target? {
4844
if
4945
scope.contains(.receiver),
50-
let target = receiverSelector(controller, anchorID)
46+
let target = receiverSelector(controller)
5147
{
5248
return target
5349
}
@@ -62,14 +58,14 @@ public struct IntrospectionSelector<Target: PlatformEntity> {
6258
}
6359

6460
extension PlatformViewController {
65-
func `as`<Entity: PlatformEntity>(_ entityType: Entity.Type) -> (any PlatformEntity)? {
66-
if Entity.Base.self == PlatformView.self {
61+
func `as`<Base: PlatformEntity>(_ baseType: Base.Type) -> (any PlatformEntity)? {
62+
if Base.self == PlatformView.self {
6763
#if canImport(UIKit)
6864
return viewIfLoaded
6965
#elseif canImport(AppKit)
7066
return isViewLoaded ? view : nil
7167
#endif
72-
} else if Entity.Base.self == PlatformViewController.self {
68+
} else if Base.self == PlatformViewController.self {
7369
return self
7470
}
7571
return nil

Sources/IntrospectionView.swift

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
11
import SwiftUI
22

3-
@_spi(Internals)
4-
public typealias IntrospectionAnchorID = UUID
3+
typealias IntrospectionViewID = UUID
4+
5+
fileprivate enum IntrospectionStore {
6+
static var shared: [IntrospectionViewID: Pair] = [:]
7+
8+
struct Pair {
9+
weak var controller: IntrospectionPlatformViewController?
10+
weak var anchor: IntrospectionAnchorPlatformViewController?
11+
}
12+
}
13+
14+
extension PlatformEntity {
15+
var introspectionAnchorEntity: Base? {
16+
if let introspectionController = self as? IntrospectionPlatformViewController {
17+
return IntrospectionStore.shared[introspectionController.id]?.anchor~
18+
}
19+
if
20+
let view = self as? PlatformView,
21+
let introspectionController = view.introspectionController
22+
{
23+
return IntrospectionStore.shared[introspectionController.id]?.anchor?.view~
24+
}
25+
return nil
26+
}
27+
}
528

629
/// ⚓️
730
struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
@@ -14,9 +37,9 @@ struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
1437
@Binding
1538
private var observed: Void // workaround for state changes not triggering view updates
1639

17-
let id: IntrospectionAnchorID
40+
let id: IntrospectionViewID
1841

19-
init(id: IntrospectionAnchorID) {
42+
init(id: IntrospectionViewID) {
2043
self._observed = .constant(())
2144
self.id = id
2245
}
@@ -31,36 +54,19 @@ struct IntrospectionAnchorView: PlatformViewControllerRepresentable {
3154
}
3255

3356
final class IntrospectionAnchorPlatformViewController: PlatformViewController {
34-
let id: IntrospectionAnchorID
35-
36-
init(id: IntrospectionAnchorID) {
37-
self.id = id
57+
init(id: IntrospectionViewID) {
3858
super.init(nibName: nil, bundle: nil)
59+
IntrospectionStore.shared[id, default: .init()].anchor = self
3960
}
4061

4162
@available(*, unavailable)
4263
required init?(coder: NSCoder) {
4364
fatalError("init(coder:) has not been implemented")
4465
}
4566

46-
#if canImport(UIKit)
47-
override func viewDidLoad() {
48-
super.viewDidLoad()
49-
view.tag = id.hashValue
50-
}
51-
#elseif canImport(AppKit)
52-
final class TaggableView: NSView {
53-
private var _tag: Int?
54-
override var tag: Int {
55-
get { _tag ?? super.tag }
56-
set { _tag = newValue }
57-
}
58-
}
59-
67+
#if canImport(AppKit) && !targetEnvironment(macCatalyst)
6068
override func loadView() {
61-
let view = TaggableView()
62-
view.tag = id.hashValue
63-
self.view = view
69+
view = NSView()
6470
}
6571
#endif
6672
}
@@ -78,14 +84,17 @@ struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresen
7884

7985
@Binding
8086
private var observed: Void // workaround for state changes not triggering view updates
87+
private let id: IntrospectionViewID
8188
private let selector: (IntrospectionPlatformViewController) -> Target?
8289
private let customize: (Target) -> Void
8390

8491
init(
92+
id: IntrospectionViewID,
8593
selector: @escaping (IntrospectionPlatformViewController) -> Target?,
8694
customize: @escaping (Target) -> Void
8795
) {
8896
self._observed = .constant(())
97+
self.id = id
8998
self.selector = selector
9099
self.customize = customize
91100
}
@@ -95,7 +104,7 @@ struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresen
95104
}
96105

97106
func makePlatformViewController(context: Context) -> IntrospectionPlatformViewController {
98-
let controller = IntrospectionPlatformViewController { controller in
107+
let controller = IntrospectionPlatformViewController(id: id) { controller in
99108
guard let target = selector(controller) else {
100109
return
101110
}
@@ -128,16 +137,22 @@ struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresen
128137
}
129138

130139
final class IntrospectionPlatformViewController: PlatformViewController {
140+
let id: IntrospectionViewID
131141
var handler: (() -> Void)? = nil
132142

133-
fileprivate init(handler: ((IntrospectionPlatformViewController) -> Void)?) {
143+
fileprivate init(
144+
id: IntrospectionViewID,
145+
handler: ((IntrospectionPlatformViewController) -> Void)?
146+
) {
147+
self.id = id
134148
super.init(nibName: nil, bundle: nil)
135149
self.handler = { [weak self] in
136150
guard let self = self else {
137151
return
138152
}
139153
handler?(self)
140154
}
155+
IntrospectionStore.shared[id, default: .init()].controller = self
141156
}
142157

143158
@available(*, unavailable)
@@ -146,13 +161,14 @@ final class IntrospectionPlatformViewController: PlatformViewController {
146161
}
147162

148163
#if canImport(UIKit)
149-
override func didMove(toParent parent: UIViewController?) {
150-
super.didMove(toParent: parent)
164+
override func viewDidLoad() {
165+
super.viewDidLoad()
166+
view.introspectionController = self
151167
handler?()
152168
}
153169

154-
override func viewDidLoad() {
155-
super.viewDidLoad()
170+
override func didMove(toParent parent: UIViewController?) {
171+
super.didMove(toParent: parent)
156172
handler?()
157173
}
158174

@@ -168,6 +184,7 @@ final class IntrospectionPlatformViewController: PlatformViewController {
168184
#elseif canImport(AppKit)
169185
override func loadView() {
170186
view = NSView()
187+
view.introspectionController = self
171188
}
172189

173190
override func viewDidLoad() {
@@ -181,3 +198,18 @@ final class IntrospectionPlatformViewController: PlatformViewController {
181198
}
182199
#endif
183200
}
201+
202+
import ObjectiveC
203+
204+
extension PlatformView {
205+
fileprivate var introspectionController: IntrospectionPlatformViewController? {
206+
get {
207+
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
208+
return objc_getAssociatedObject(self, key) as? IntrospectionPlatformViewController
209+
}
210+
set {
211+
let key = unsafeBitCast(Selector(#function), to: UnsafeRawPointer.self)
212+
objc_setAssociatedObject(self, key, newValue, .OBJC_ASSOCIATION_ASSIGN)
213+
}
214+
}
215+
}

0 commit comments

Comments
 (0)