This commit is contained in:
David Roman 2023-06-03 13:25:18 +01:00
parent 567d5b69d3
commit f3ddb7ab6b
No known key found for this signature in database
GPG Key ID: 7058646EEFCB70A7
3 changed files with 83 additions and 48 deletions

View File

@ -19,36 +19,23 @@ extension View {
scope: IntrospectionScope? = nil,
customize: @escaping (PlatformSpecificEntity) -> Void
) -> some View {
if platforms.contains(where: \.isCurrent) {
let id = IntrospectionAnchorID()
if let platform = platforms.first(where: \.isCurrent) {
let anchorID = IntrospectionAnchorID()
self.background(
IntrospectionAnchorView(
id: id
)
.frame(width: 0, height: 0)
IntrospectionAnchorView(
id: anchorID
)
.overlay(
IntrospectionView(
selector: { entity in
let scope = scope ?? viewType.scope
if
scope.contains(.receiver),
let target = entity.receiver(ofType: PlatformSpecificEntity.self, anchorID: id)
{
return target
}
if
scope.contains(.ancestor),
let target = entity.ancestor(ofType: PlatformSpecificEntity.self)
{
return target
}
return nil
},
customize: customize
)
.frame(width: 0, height: 0)
.frame(width: 0, height: 0)
)
.overlay(
IntrospectionView(
selector: { entity in
(platform.selector ?? .default)(entity, scope ?? viewType.scope, anchorID)
},
customize: customize
)
.frame(width: 0, height: 0)
)
} else {
self
}
@ -72,18 +59,15 @@ public protocol PlatformEntity: AnyObject {
}
extension PlatformEntity {
@_spi(Internals)
public var ancestors: some Sequence<Base> {
var ancestors: some Sequence<Base> {
sequence(first: self~, next: { $0.ancestor~ }).dropFirst()
}
@_spi(Internals)
public var allDescendants: [Base] {
var allDescendants: [Base] {
self.descendants.reduce([self~]) { $0 + $1.allDescendants~ }
}
@_spi(Internals)
public func nearestCommonAncestor(with other: Base) -> Base? {
func nearestCommonAncestor(with other: Base) -> Base? {
var nearestAncestor: Base? = self~
while let currentEntity = nearestAncestor, !other.isDescendant(of: currentEntity~) {
@ -93,8 +77,7 @@ extension PlatformEntity {
return nearestAncestor
}
@_spi(Internals)
public func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] {
func descendantsBetween(_ bottomEntity: Base, and topEntity: Base) -> [Base] {
var result: [Base] = []
var entered = false
@ -111,7 +94,7 @@ extension PlatformEntity {
return result
}
fileprivate func receiver<PlatformSpecificEntity: PlatformEntity>(
func receiver<PlatformSpecificEntity: PlatformEntity>(
ofType type: PlatformSpecificEntity.Type,
anchorID: IntrospectionAnchorID
) -> PlatformSpecificEntity? {
@ -129,7 +112,7 @@ extension PlatformEntity {
.first
}
fileprivate func ancestor<PlatformSpecificEntity: PlatformEntity>(
func ancestor<PlatformSpecificEntity: PlatformEntity>(
ofType type: PlatformSpecificEntity.Type
) -> PlatformSpecificEntity? {
self.ancestors

View File

@ -0,0 +1,38 @@
public struct IntrospectionSelector<Target: PlatformEntity> {
private let selector: (any PlatformEntity, IntrospectionScope, IntrospectionAnchorID) -> Target?
static var `default`: Self { .from(Target.self, selector: { $0 }) }
@_spi(Internals)
public static func from<Entry: PlatformEntity>(_ entryType: Entry.Type, selector: @escaping (Entry) -> Target?) -> Self {
.init { entity, scope, anchorID in
if
scope.contains(.receiver),
let entry = entity.receiver(ofType: Entry.self, anchorID: anchorID),
let target = selector(entry)
{
return target
}
if
scope.contains(.ancestor),
let entry = entity.ancestor(ofType: Entry.self),
let target = selector(entry)
{
return target
}
return nil
}
}
init(_ selector: @escaping (any PlatformEntity, IntrospectionScope, IntrospectionAnchorID) -> Target?) {
self.selector = selector
}
func callAsFunction(
_ entity: any PlatformEntity,
_ scope: IntrospectionScope,
_ anchorID: IntrospectionAnchorID
) -> Target? {
selector(entity, scope, anchorID)
}
}

View File

@ -1,35 +1,49 @@
import SwiftUI
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity> {
public struct PlatformViewVersions<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
let isCurrent: Bool
let selector: IntrospectionSelector<PlatformSpecificEntity>?
private init<Version: PlatformVersion>(
_ versions: [PlatformViewVersion<Version, SwiftUIViewType, PlatformSpecificEntity>]
) {
if let currentVersion = versions.first(where: \.isCurrent) {
self.isCurrent = true
self.selector = currentVersion.selector
} else {
self.isCurrent = false
self.selector = nil
}
}
public static func iOS(_ versions: (iOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(isCurrent: versions.contains(where: \.isCurrent))
Self(versions)
}
public static func tvOS(_ versions: (tvOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(isCurrent: versions.contains(where: \.isCurrent))
Self(versions)
}
public static func macOS(_ versions: (macOSViewVersion<SwiftUIViewType, PlatformSpecificEntity>)...) -> Self {
Self(isCurrent: versions.contains(where: \.isCurrent))
Self(versions)
}
}
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity> =
public typealias iOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<iOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity> =
public typealias tvOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<tvOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity> =
public typealias macOSViewVersion<SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> =
PlatformViewVersion<macOSVersion, SwiftUIViewType, PlatformSpecificEntity>
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity> {
public struct PlatformViewVersion<Version: PlatformVersion, SwiftUIViewType: IntrospectableViewType, PlatformSpecificEntity: PlatformEntity> {
let isCurrent: Bool
let selector: IntrospectionSelector<PlatformSpecificEntity>?
}
extension PlatformViewVersion {
@_spi(Internals) public init(for version: Version) {
self.init(isCurrent: version.isCurrent)
@_spi(Internals) public init(for version: Version, selector: IntrospectionSelector<PlatformSpecificEntity>? = nil) {
self.init(isCurrent: version.isCurrent, selector: selector)
}
@_spi(Internals) public static func unavailable(file: StaticString = #file, line: UInt = #line) -> Self {
@ -46,6 +60,6 @@ extension PlatformViewVersion {
https://github.com/siteline/swiftui-introspect/issues/new?title=`\(fileName):\(line)`+should+be+marked+unavailable
"""
)
return Self(isCurrent: false)
return Self(isCurrent: false, selector: nil)
}
}