Skip to content

Commit f327069

Browse files
authored
Custom selectors (#233)
1 parent 43a8b9c commit f327069

File tree

4 files changed

+114
-75
lines changed

4 files changed

+114
-75
lines changed

Sources/Introspect.swift

Lines changed: 24 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,23 @@ extension View {
1919
scope: IntrospectionScope? = nil,
2020
customize: @escaping (PlatformSpecificEntity) -> Void
2121
) -> some View {
22-
if platforms.contains(where: \.isCurrent) {
23-
let id = IntrospectionAnchorID()
22+
if let platform = platforms.first(where: \.isCurrent) {
23+
let anchorID = IntrospectionAnchorID()
2424
self.background(
25-
IntrospectionAnchorView(
26-
id: id
27-
)
28-
.frame(width: 0, height: 0)
25+
IntrospectionAnchorView(
26+
id: anchorID
2927
)
30-
.overlay(
31-
IntrospectionView(
32-
selector: { entity in
33-
let scope = scope ?? viewType.scope
34-
if
35-
scope.contains(.receiver),
36-
let target = entity.receiver(ofType: PlatformSpecificEntity.self, anchorID: id)
37-
{
38-
return target
39-
}
40-
if
41-
scope.contains(.ancestor),
42-
let target = entity.ancestor(ofType: PlatformSpecificEntity.self)
43-
{
44-
return target
45-
}
46-
return nil
47-
},
48-
customize: customize
49-
)
50-
.frame(width: 0, height: 0)
28+
.frame(width: 0, height: 0)
29+
)
30+
.overlay(
31+
IntrospectionView(
32+
selector: { entity in
33+
(platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID)
34+
},
35+
customize: customize
5136
)
37+
.frame(width: 0, height: 0)
38+
)
5239
} else {
5340
self
5441
}
@@ -72,18 +59,15 @@ public protocol PlatformEntity: AnyObject {
7259
}
7360

7461
extension PlatformEntity {
75-
@_spi(Internals)
76-
public var ancestors: some Sequence<Base> {
62+
var ancestors: some Sequence<Base> {
7763
sequence(first: self~, next: { $0.ancestor~ }).dropFirst()
7864
}
7965

80-
@_spi(Internals)
81-
public var allDescendants: [Base] {
66+
var allDescendants: [Base] {
8267
self.descendants.reduce([self~]) { $0 + $1.allDescendants~ }
8368
}
8469

85-
@_spi(Internals)
86-
public func nearestCommonAncestor(with other: Base) -> Base? {
70+
func nearestCommonAncestor(with other: Base) -> Base? {
8771
var nearestAncestor: Base? = self~
8872

8973
while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) {
@@ -93,28 +77,24 @@ extension PlatformEntity {
9377
return nearestAncestor
9478
}
9579

96-
@_spi(Internals)
97-
public func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] {
98-
var entered = false
80+
func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] {
9981
var result: [Base] = []
82+
var entered = false
10083

10184
for descendant in self.allDescendants {
10285
if descendant === bottomEntity {
10386
entered = true
104-
continue
105-
}
106-
if descendant === topEntity {
107-
return result
108-
}
109-
if entered {
87+
} else if descendant === topEntity {
88+
break
89+
} else if entered {
11090
result.append(descendant)
11191
}
11292
}
11393

11494
return result
11595
}
11696

117-
fileprivate func receiver<PlatformSpecificEntity: PlatformEntity>(
97+
func receiver<PlatformSpecificEntity: PlatformEntity>(
11898
ofType type: PlatformSpecificEntity.Type,
11999
anchorID: IntrospectionAnchorID
120100
) -> PlatformSpecificEntity? {
@@ -132,7 +112,7 @@ extension PlatformEntity {
132112
.first
133113
}
134114

135-
fileprivate func ancestor<PlatformSpecificEntity: PlatformEntity>(
115+
func ancestor<PlatformSpecificEntity: PlatformEntity>(
136116
ofType type: PlatformSpecificEntity.Type
137117
) -> PlatformSpecificEntity? {
138118
self.ancestors

Sources/IntrospectionSelector.swift

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
@_spi(Internals)
2+
public struct IntrospectionSelector<Target: PlatformEntity> {
3+
private let selector: (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target?
4+
5+
static var `default`: Self { .from(Target.self, selector: { $0 }) }
6+
7+
@_spi(Internals)
8+
public static func from<Entry: PlatformEntity>(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self {
9+
.init { controller, scope, anchorID in
10+
guard let entity = { () -> (any PlatformEntity)? in
11+
if Entry.Base.self == PlatformView.self {
12+
#if canImport(UIKit)
13+
if let introspectionView = controller.viewIfLoaded {
14+
return introspectionView
15+
}
16+
#elseif canImport(AppKit)
17+
if controller.isViewLoaded {
18+
return controller.view
19+
}
20+
#endif
21+
} else if Entry.Base.self == PlatformViewController.self {
22+
return controller
23+
}
24+
return nil
25+
}() else {
26+
return nil
27+
}
28+
if
29+
scope.contains(.receiver),
30+
let entry = entity.receiver(ofType: Entry.self, anchorID: anchorID),
31+
let target = selector(entry)
32+
{
33+
return target
34+
}
35+
if
36+
scope.contains(.ancestor),
37+
let entry = entity.ancestor(ofType: Entry.self),
38+
let target = selector(entry)
39+
{
40+
return target
41+
}
42+
return nil
43+
}
44+
}
45+
46+
init(_ selector: @escaping (IntrospectionPlatformViewController, IntrospectionScope, IntrospectionAnchorID) -> Target?) {
47+
self.selector = selector
48+
}
49+
50+
func callAsFunction(
51+
_ controller: IntrospectionPlatformViewController,
52+
_ scope: IntrospectionScope,
53+
_ anchorID: IntrospectionAnchorID
54+
) -> Target? {
55+
selector(controller, scope, anchorID)
56+
}
57+
}

Sources/IntrospectionView.swift

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -81,26 +81,11 @@ struct IntrospectionView<Target: PlatformEntity>: PlatformViewControllerRepresen
8181
private let customize: (Target) -> Void
8282

8383
init(
84-
selector: @escaping (any PlatformEntity) -> Target?,
84+
selector: @escaping (IntrospectionPlatformViewController) -> Target?,
8585
customize: @escaping (Target) -> Void
8686
) {
8787
self._observed = .constant(())
88-
self.selector = { introspectionController in
89-
if Target.Base.self == PlatformView.self {
90-
#if canImport(UIKit)
91-
if let introspectionView = introspectionController.viewIfLoaded {
92-
return selector(introspectionView)
93-
}
94-
#elseif canImport(AppKit)
95-
if introspectionController.isViewLoaded {
96-
return selector(introspectionController.view)
97-
}
98-
#endif
99-
} else if Target.Base.self == PlatformViewController.self {
100-
return selector(introspectionController)
101-
}
102-
return nil
103-
}
88+
self.selector = selector
10489
self.customize = customize
10590
}
10691

Sources/PlatformViewVersion.swift

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,49 @@
11
import SwiftUI
22

3-
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformView> {
3+
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
44
let isCurrent: Bool
5+
let selector: IntrospectionSelector<PlatformSpecificEntity>?
56

6-
public static func iOS(_ versions: (iOSViewVersion<SwiftUIViewType, PlatformView>)...) -> Self {
7-
Self(isCurrent: versions.contains(where: \.isCurrent))
7+
private init<Version: PlatformVersion>(
8+
_ versions: [PlatformViewVersion<Version, SwiftUIViewType, PlatformSpecificEntity>]
9+
) {
10+
if let currentVersion = versions.first(where: \.isCurrent) {
11+
self.isCurrent = true
12+
self.selector = currentVersion.selector
13+
} else {
14+
self.isCurrent = false
15+
self.selector = nil
16+
}
817
}
918

10-
public static func tvOS(_ versions: (tvOSViewVersion<SwiftUIViewType, PlatformView>)...) -> Self {
11-
Self(isCurrent: versions.contains(where: \.isCurrent))
19+
public static func iOS(_ versions: (iOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
20+
Self(versions)
1221
}
1322

14-
public static func macOS(_ versions: (macOSViewVersion<SwiftUIViewType, PlatformView>)...) -> Self {
15-
Self(isCurrent: versions.contains(where: \.isCurrent))
23+
public static func tvOS(_ versions: (tvOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
24+
Self(versions)
25+
}
26+
27+
public static func macOS(_ versions: (macOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
28+
Self(versions)
1629
}
1730
}
1831

19-
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformView> = PlatformViewVersion<iOSVersion, SwiftUIViewType, PlatformView>
20-
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformView> = PlatformViewVersion<tvOSVersion, SwiftUIViewType, PlatformView>
21-
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformView> = PlatformViewVersion<macOSVersion, SwiftUIViewType, PlatformView>
32+
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
33+
PlatformViewVersion<iOSVersion, SwiftUIViewType, PlatformSpecificEntity>
34+
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
35+
PlatformViewVersion<tvOSVersion, SwiftUIViewType, PlatformSpecificEntity>
36+
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
37+
PlatformViewVersion<macOSVersion, SwiftUIViewType, PlatformSpecificEntity>
2238

23-
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformView> {
39+
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
2440
let isCurrent: Bool
41+
let selector: IntrospectionSelector<PlatformSpecificEntity>?
2542
}
2643

2744
extension PlatformViewVersion {
28-
@_spi(Internals) public init(for version: Version) {
29-
self.init(isCurrent: version.isCurrent)
45+
@_spi(Internals) public init(for version: Version, selector: IntrospectionSelector<PlatformSpecificEntity>? = nil) {
46+
self.init(isCurrent: version.isCurrent, selector: selector)
3047
}
3148

3249
@_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self {
@@ -43,6 +60,6 @@ extension PlatformViewVersion {
4360
https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable
4461
"""
4562
)
46-
return Self(isCurrent: false)
63+
return Self(isCurrent: false, selector: nil)
4764
}
4865
}

0 commit comments

Comments
 (0)