Skip to content

Commit

Permalink
Finalize 0.9.2 release
Browse files Browse the repository at this point in the history
  • Loading branch information
nalexn authored Sep 17, 2022
2 parents 6b88c4e + 90e4132 commit ac7df67
Show file tree
Hide file tree
Showing 45 changed files with 639 additions and 107 deletions.
82 changes: 67 additions & 15 deletions Sources/ViewInspector/Modifiers/AccessibilityModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ public extension InspectableView {
func accessibilityLabel() throws -> InspectableView<ViewType.Text> {
let text: Text
let call = "accessibilityLabel"
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
text = try v3AccessibilityElement(
path: "some|text", type: Text.self,
call: call, { $0.accessibilityLabel("") })
} else if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
text = try v3AccessibilityElement(
type: Text.self, call: call, { $0.accessibilityLabel("") })
} else {
Expand Down Expand Up @@ -67,7 +71,11 @@ public extension InspectableView {

func accessibilityIdentifier() throws -> String {
let call = "accessibilityIdentifier"
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
return try v3AccessibilityElement(
path: "some|rawValue", type: String.self,
call: call, { $0.accessibilityIdentifier("") })
} else if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return try v3AccessibilityElement(
type: String.self, call: call, { $0.accessibilityIdentifier("") })
} else {
Expand Down Expand Up @@ -96,7 +104,11 @@ public extension InspectableView {
}

func accessibilityActivationPoint() throws -> UnitPoint {
if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
return try v3AccessibilityElement(
path: "some|activate|some|unitPoint", type: UnitPoint.self,
call: "accessibilityIdentifier", { $0.accessibilityActivationPoint(.center) })
} else if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) {
return try v3AccessibilityElement(
path: "some|unitPoint", type: UnitPoint.self,
call: "accessibilityIdentifier", { $0.accessibilityActivationPoint(.center) })
Expand Down Expand Up @@ -201,15 +213,20 @@ private struct AccessibilityProperty {
self.value = try Inspector.attribute(path: "super|value", value: property)
}

init(key: UInt64, value: Any) throws {
self.keyPointerValue = key
self.value = try Inspector.attribute(path: "typedValue", value: value)
}

static var noisePointerValues: Set<UInt64> = {
let view1 = EmptyView().accessibility(label: Text(""))
let view2 = EmptyView().accessibility(hint: Text(""))
do {
let props1 = try view1.inspect()
.v3AccessibilityProperties(call: "")
.v3v4AccessibilityProperties(call: "")
.map { $0.keyPointerValue }
let props2 = try view2.inspect()
.v3AccessibilityProperties(call: "")
.v3v4AccessibilityProperties(call: "")
.map { $0.keyPointerValue }
return Set(props1).intersection(Set(props2))
} catch { return .init() }
Expand All @@ -223,10 +240,10 @@ private extension InspectableView {
) throws -> T where V: SwiftUI.View {
let noiseValues = AccessibilityProperty.noisePointerValues
guard let referenceValue = try reference(EmptyView()).inspect()
.v3AccessibilityProperties(call: call)
.v3v4AccessibilityProperties(call: call)
.map({ $0.keyPointerValue })
.first(where: { !noiseValues.contains($0) }),
let property = try v3AccessibilityProperties(call: call)
let property = try v3v4AccessibilityProperties(call: call)
.first(where: { $0.keyPointerValue == referenceValue })
else {
throw InspectionError
Expand Down Expand Up @@ -260,34 +277,69 @@ private extension InspectableView {
.modifierNotFound(parent: Inspector.typeName(value: content.view),
modifier: call, index: 0)
}
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
throw InspectionError.notSupported(
"""
Accessibility actions are currently unavailable for \
inspection on iOS 16. Situation may change with a minor \
OS version update. In the meanwhile, please add XCTSkip \
for iOS 16 and use an earlier OS version for testing.
""")
}
return try Inspector.attribute(label: "handler", value: action, type: T.self)
}

func v3AccessibilityActions(call: String) throws -> [Any] {
let noiseValues = AccessibilityProperty.noisePointerValues
guard let referenceValue = try EmptyView().accessibilityAction(.default, { })
.inspect()
.v3AccessibilityProperties(call: call)
.v3v4AccessibilityProperties(call: call)
.map({ $0.keyPointerValue })
.first(where: { !noiseValues.contains($0) })
else {
throw InspectionError
.modifierNotFound(parent: Inspector.typeName(value: content.view),
modifier: call, index: 0)
}
return try v3AccessibilityProperties(call: call)
return try v3v4AccessibilityProperties(call: call)
.filter({ $0.keyPointerValue == referenceValue })
.compactMap { $0.value as? [Any] }
.flatMap { $0 }
.map { try Inspector.attribute(path: "base|base", value: $0) }
}

func v3AccessibilityProperties(call: String) throws -> [AccessibilityProperty] {
return try modifierAttribute(
modifierName: "AccessibilityAttachmentModifier",
path: "modifier|storage|propertiesComponent",
type: [Any].self, call: call)
.map { try AccessibilityProperty(property: $0) }
func v3v4AccessibilityProperties(call: String) throws -> [AccessibilityProperty] {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
return try modifierAttribute(
modifierName: "AccessibilityAttachmentModifier",
path: "modifier|storage|value|properties|storage",
type: AccessibilityKeyValues.self, call: call)
.accessibilityKeyValues()
.map { try AccessibilityProperty(key: $0.key, value: $0.value) }
} else {
return try modifierAttribute(
modifierName: "AccessibilityAttachmentModifier",
path: "modifier|storage|propertiesComponent",
type: [Any].self, call: call)
.map { try AccessibilityProperty(property: $0) }
}
}
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
protocol AccessibilityKeyValues {
func accessibilityKeyValues() throws -> [(key: UInt64, value: Any)]
}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
extension Dictionary: AccessibilityKeyValues {
func accessibilityKeyValues() throws -> [(key: UInt64, value: Any)] {
return try self.keys.compactMap { key -> (key: UInt64, value: Any)? in
guard let value = self[key] else { return nil }
let keyPointerValue = try Inspector.attribute(
path: "rawValue|pointerValue", value: key, type: UInt64.self)
return (key: keyPointerValue, value: value as Any)
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions Sources/ViewInspector/Modifiers/ConfigurationModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import SwiftUI
public extension InspectableView {

func labelsHidden() -> Bool {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
return (try? modifierAttribute(
modifierName: "LabelsHiddenModifier", transitive: true,
path: "modifier", type: Any.self, call: "labelsHidden")) != nil
}
return (try? modifierAttribute(modifierLookup: { modifier -> Bool in
modifier.modifierType.hasPrefix("_LabeledViewStyleModifier<HiddenLabel")
}, transitive: true, path: "modifier|style",
Expand Down
34 changes: 32 additions & 2 deletions Sources/ViewInspector/Modifiers/EventsModifiers.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import SwiftUI

// MARK: - ViewEvents

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
Expand All @@ -18,11 +20,39 @@ public extension InspectableView {
}

func callOnChange<E: Equatable>(newValue value: E, index: Int = 0) throws {
let callback = try modifierAttribute(
modifierName: "_ValueActionModifier<\(type(of: value))>",
let typeName = Inspector.typeName(type: E.self)
if let callback = try? modifierAttribute(
modifierName: "_ValueActionModifier<\(typeName)>",
path: "modifier|action",
type: ((E) -> Void).self,
call: "onChange", index: index) {
callback(value)
return
}
let callback = try modifierAttribute(
modifierName: "_ValueActionModifier<Optional<\(typeName)>>",
path: "modifier|action",
type: ((E?) -> Void).self,
call: "onChange", index: index)
callback(value)
}
}

@available(iOS 15, macOS 12, tvOS 15, watchOS 8, *)
public extension InspectableView {

func callOnSubmit(of triggers: SubmitTriggers = .text) throws {
let callback = try modifierAttribute(
modifierLookup: { modifier -> Bool in
guard modifier.modifierType.contains("OnSubmitModifier"),
let modifierTriggers = try? Inspector
.attribute(path: "modifier|allowed", value: modifier, type: SubmitTriggers.self)
else { return false }
return modifierTriggers.contains(triggers)
},
path: "modifier|action",
type: (() -> Void).self,
call: "onSubmit")
callback()
}
}
19 changes: 19 additions & 0 deletions Sources/ViewInspector/Modifiers/NavigationBarModifiers.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
import SwiftUI

#if os(macOS) && !MAC_OS_VERSION_13_0
struct ToolbarPlacement {
static var navigationBar: ToolbarPlacement { .init() }
}
#endif

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
public extension InspectableView {

@available(iOS 13.0, tvOS 13.0, *)
@available(macOS, unavailable)
func navigationBarHidden() throws -> Bool {
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
let value = try modifierAttribute(modifierLookup: { modifier -> Bool in
guard modifier.modifierType.contains("ToolbarAppearanceModifier"),
let bars = try? Inspector.attribute(
path: "modifier|bars", value: modifier, type: [ToolbarPlacement].self)
else { return false }
return bars.contains(.navigationBar)
}, path: "modifier|visibility|some", type: Any.self, call: "navigationBarHidden")
return String(describing: value) != "visible"
}
let value = try modifierAttribute(
modifierName: "_PreferenceWritingModifier<NavigationBarHiddenKey>",
path: "modifier|value", type: Any.self, call: "navigationBarHidden")
Expand Down Expand Up @@ -34,6 +50,9 @@ public extension InspectableView {
}
}

@available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *)
extension ToolbarPlacement: BinaryEquatable { }

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
internal extension ViewType {
struct EnvironmentReaderView { }
Expand Down
21 changes: 17 additions & 4 deletions Sources/ViewInspector/Modifiers/TextInputModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,23 @@ public extension InspectableView {
}

func keyboardType() throws -> UIKeyboardType {
let reference = EmptyView().keyboardType(.default)
let keyPath = try Inspector.environmentKeyPath(Int.self, reference)
let value = try environment(keyPath, call: "keyboardType")
return UIKeyboardType(rawValue: value)!
guard #available(iOS 15.0, tvOS 15.0, watchOS 8.0, *) else {
let reference = EmptyView().keyboardType(.default)
let keyPath = try Inspector.environmentKeyPath(Int.self, reference)
let value = try environment(keyPath, call: "keyboardType")
return UIKeyboardType(rawValue: value)!
}
guard let modifier = content.medium.environmentModifiers.last(where: { modifier in
guard let keyPath = try? modifier.keyPath()
else { return false }
let keyPathType = Inspector.typeName(value: keyPath)
return keyPathType == "WritableKeyPath<EnvironmentValues, KeyboardType>"
}) else {
throw InspectionError.modifierNotFound(
parent: Inspector.typeName(value: content.view), modifier: "keyboardType", index: 0)
}
return try Inspector.attribute(
label: "type", value: try modifier.value(), type: UIKeyboardType.self)
}

func autocapitalization() throws -> UITextAutocapitalizationType {
Expand Down
12 changes: 6 additions & 6 deletions Sources/ViewInspector/Modifiers/TransformingModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@ public extension InspectableView {
}

struct Rotation3D {
let angle: Angle
let axis: Axis
let anchor: UnitPoint
let anchorZ: CGFloat
let perspective: CGFloat
typealias Axis = (x: CGFloat, y: CGFloat, z: CGFloat)
public let angle: Angle
public let axis: Axis
public let anchor: UnitPoint
public let anchorZ: CGFloat
public let perspective: CGFloat
public typealias Axis = (x: CGFloat, y: CGFloat, z: CGFloat)
}

func rotation3D() throws -> Rotation3D {
Expand Down
1 change: 1 addition & 0 deletions Sources/ViewInspector/Modifiers/TransitiveModifiers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ extension ModifiedContent: PossiblyTransitiveModifier {
"_FlipForRTLEffect",
"_AllowsHitTestingModifier",
"_PreferenceWritingModifier<PreferredColorSchemeKey>",
"LabelsHiddenModifier",
].contains(name) || [
"_LabeledViewStyleModifier<HiddenLabel",
].contains(where: { name.hasPrefix($0) }) {
Expand Down
7 changes: 6 additions & 1 deletion Sources/ViewInspector/SwiftUI/DatePicker.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ public extension InspectableView where View == ViewType.DatePicker {

func select(date: Date) throws {
try guardIsResponsive()
let binding = try Inspector.attribute(path: "selection", value: content.view, type: Binding<Date>.self)
let binding: Binding<Date>
if let value = try? Inspector.attribute(path: "_selection", value: content.view, type: Binding<Date>.self) {
binding = value
} else {
binding = try Inspector.attribute(path: "selection", value: content.view, type: Binding<Date>.self)
}
binding.wrappedValue = date
}
}
Expand Down
7 changes: 6 additions & 1 deletion Sources/ViewInspector/SwiftUI/EditButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,12 @@ public extension InspectableView where View: MultipleViewContent {
public extension InspectableView where View == ViewType.EditButton {

func editMode() throws -> Binding<EditMode>? {
let editMode = try Inspector.attribute(label: "editMode", value: content.view)
let editMode: Any
if let mode = try? Inspector.attribute(label: "_editMode", value: content.view) {
editMode = mode
} else {
editMode = try Inspector.attribute(label: "editMode", value: content.view)
}
typealias Env = Environment<Binding<EditMode>?>
return (editMode as? Env)?.wrappedValue
}
Expand Down
3 changes: 1 addition & 2 deletions Sources/ViewInspector/SwiftUI/Gesture.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,7 @@ extension SequenceGesture: Inspectable {}
@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
extension SimultaneousGesture: Inspectable {}

@available(iOS 13.0, macOS 10.15, *)
@available(tvOS, unavailable)
@available(iOS 13.0, macOS 10.15, tvOS 16.0, *)
extension TapGesture: Inspectable {}

@available(iOS 13.0, macOS 10.15, tvOS 13.0, *)
Expand Down
13 changes: 11 additions & 2 deletions Sources/ViewInspector/SwiftUI/Image.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,17 @@ public extension InspectableView where View == ViewType.Image {
}

func labelView() throws -> InspectableView<ViewType.Text> {
return try View.supplementaryChildren(self).element(at: 0)
.asInspectableView(ofType: ViewType.Text.self)
let label = try View.supplementaryChildren(self).element(at: 0)
if Inspector.typeName(value: label.content.view) == "AccessibilityImageLabel" {
let name = try Inspector.attribute(label: "systemSymbol", value: label.content.view, type: String.self)
let content = Content(Text(name), medium: label.content.medium)
return try .init(content, parent: label.parentView)
}
if Inspector.typeName(value: label.content.view) == "ImageLabel" {
let content = Content(Text(try actualImage().name()), medium: label.content.medium)
return try .init(content, parent: label.parentView)
}
return try label.asInspectableView(ofType: ViewType.Text.self)
}
}

Expand Down
15 changes: 15 additions & 0 deletions Sources/ViewInspector/SwiftUI/Menu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ public extension InspectableView where View == ViewType.Menu {
}
}

@available(iOS 15.0, macOS 12.0, *)
@available(tvOS, unavailable)
public extension InspectableView where View == ViewType.Menu {

func callPrimaryAction() throws {
typealias Callback = () -> Void
let callback = try Inspector.attribute(
label: "primaryAction", value: content.view, type: Callback?.self)
guard let callback = callback else {
throw InspectionError.attributeNotFound(label: "primaryAction", type: "Menu")
}
callback()
}
}

// MARK: - Global View Modifiers

@available(iOS 14.0, macOS 11.0, *)
Expand Down
Loading

0 comments on commit ac7df67

Please sign in to comment.