Skip to main content

new-api-support

Add introspection support for a SwiftUI API (view type, modifier, or View extension function). Use when the user wants to add support for a new SwiftUI entity to ViewInspector.

معلومات المصدر

المستودع
nalexn/ViewInspector
آخر نشاط في المصدر
١ فبراير ٢٠٢٦ في ١٩:٣٠
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٢٬٦٣٥
التفرعات
١٩٦

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
new-api-support
description
Add introspection support for a SwiftUI API (view type, modifier, or View extension function). Use when the user wants to add support for a new SwiftUI entity to ViewInspector.
argument-hint
<entity_name>
# new-api-support Add introspection support for a SwiftUI API (view type, modifier, or View extension function). ## Usage ``` /new-api-support <entity_name> ``` Where `<entity_name>` is: - A SwiftUI struct name (e.g., `ContentUnavailableView`, `ProgressView`) - A View extension function name (e.g., `onAppear`, `disabled`, `opacity`) - A modifier struct name (e.g., `ScaledMetric`) ## Workflow ### Step 1: Locate and Catalog the API **Find the API in the local iOS SDK** (prefer local over network): ```bash # Find SwiftUI interface files in Xcode SDK find /Applications/Xcode.app/Contents/Developer/Platforms -name "SwiftUI.swiftmodule" -type d 2>/dev/null | head -5 # Search for the entity in SwiftUI interfaces grep -r "<entity_name>" /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/*.swiftinterface 2>/dev/null | head -50 # For macOS SDK grep -r "<entity_name>" /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/*.swiftinterface 2>/dev/null | head -50 ``` **Catalog ALL related APIs:** - For functions: Find all overloads (same name, different parameters) - For structs: Find the struct definition AND any View extension functions that return this type - Note ALL `@available` attributes for each API variant Example catalog format: ``` Entity: .buttonStyle(_:) Type: View extension function Related APIs: 1. func buttonStyle<S>(_ style: S) -> some View where S : ButtonStyle @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) 2. func buttonStyle<S>(_ style: S) -> some View where S : PrimitiveButtonStyle @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) ``` ### Step 2: Research Usage Context **Understand HOW the API is meant to be used** by researching its typical context: 1. **Search for documentation and usage patterns:** - Use web search to find Apple documentation and WWDC sessions - Look for common usage patterns in tutorials and Stack Overflow - Identify which parent views/containers the API is typically used with 2. **Identify related parent/child relationships:** | Entity Type | Find Related Context | |-------------|---------------------| | View inside container | Which container views typically hold this view? (e.g., `Tab` goes inside `TabView`) | | View modifier | Which views is this modifier typically applied to? (e.g., `subscriptionStoreButtonLabel` applies to `SubscriptionStoreView`) | | Container-specific modifier | Which container makes this modifier meaningful? (e.g., `listRowBackground` on views inside `List`) | | Style modifier | Which view type does this style affect? (e.g., `buttonStyle` on `Button`) | 3. **Document the context for test design:** Example context analysis: ``` Entity: Tab Type: View Typical Context: Used as direct child of TabView Related APIs: TabView, tabItem (deprecated predecessor) Test Structure: Tab should be tested INSIDE TabView hierarchy Entity: subscriptionStoreButtonLabel Type: View modifier Typical Context: Applied to SubscriptionStoreView Related APIs: SubscriptionStoreView, SubscriptionStoreButton Test Structure: Apply modifier to SubscriptionStoreView, not EmptyView ``` 4. **Check for container-dependent behavior:** - Some modifiers only work in specific contexts (e.g., `listRowInsets` only meaningful in `List`) - Some views only function inside specific parents (e.g., `Section` in `List` or `Form`) - Test in the correct context to ensure real-world usability ### Step 3: Determine File Placement **For new View types** (structs like `ProgressView`, `ContentUnavailableView`): - Create new file: `Sources/ViewInspector/SwiftUI/<ViewName>.swift` - Create test file: `Tests/ViewInspectorTests/SwiftUI/<ViewName>Tests.swift` **For View modifiers/functions**, find the appropriate existing file by category: | Category | Source File | Test File | |----------|-------------|-----------| | Animation (.animation, .transition) | `Modifiers/AnimationModifiers.swift` | `ViewModifiers/AnimationModifiersTests.swift` | | Configuration (.disabled, .labelsHidden) | `Modifiers/ConfigurationModifiers.swift` | `ViewModifiers/ConfigurationModifiersTests.swift` | | Environment (.environment, .environmentObject) | `Modifiers/EnvironmentModifiers.swift` | `ViewModifiers/EnvironmentModifiersTests.swift` | | Interaction (.onTapGesture, .onAppear) | `Modifiers/InteractionModifiers.swift` | `ViewModifiers/InteractionModifiersTests.swift` | | Positioning (.offset, .position) | `Modifiers/PositioningModifiers.swift` | `ViewModifiers/PositioningModifiersTests.swift` | | Sizing (.frame, .fixedSize) | `Modifiers/SizingModifiers.swift` | `ViewModifiers/SizingModifiersTests.swift` | | Text input (.keyboardType, .textContentType) | `Modifiers/TextInputModifiers.swift` | `ViewModifiers/TextInputModifiersTests.swift` | | Transform (.rotationEffect, .scaleEffect) | `Modifiers/TransformingModifiers.swift` | `ViewModifiers/TransformingModifiersTests.swift` | | Navigation bar (.navigationTitle) | `Modifiers/NavigationBarModifiers.swift` | - | | Custom styles (.buttonStyle, .pickerStyle) | `Modifiers/CustomStyleModifiers.swift` | - | Check existing files to confirm the pattern: ```bash grep -l "similar_modifier" Sources/ViewInspector/Modifiers/*.swift ``` ### Step 4: Reverse Engineering Investigation **Create a reverse engineering test** to understand the internal structure: ```swift import XCTest import SwiftUI @testable import ViewInspector final class ReverseEngineeringTests: XCTestCase { func testInvestigate_<EntityName>() throws { // Create a simple view using the target API let sut = EmptyView().<targetAPI>() // Print the internal structure print("\(Inspector.print(sut) as AnyObject)") } } ``` **Run the investigation test:** ```bash swift test --filter "testInvestigate_" ``` **Analyze the output** to identify: 1. The internal modifier/view type name (e.g., `_AppearanceActionModifier`) 2. Property names and their paths (e.g., `appear`, `disappear`) 3. Nested structure for complex types 4. Whether it uses `ModifiedContent` wrapper Example Inspector.print output: ``` EmptyView → _AppearanceActionModifier modifier: _AppearanceActionModifier appear: Optional<() -> ()> some: (Function) disappear: Optional<() -> ()> none ``` **Iterate investigation** for each API variant and parameter combination to understand all internal structures. ### Step 5: Implement Introspection Support **For View modifiers**, add to the appropriate Modifiers file: ```swift @available(iOS 13.0, macOS 10.15, tvOS 13.0, *) public extension InspectableView { // For simple value extraction func <modifierName>() throws -> <ReturnType> { return try modifierAttribute( modifierName: "<InternalModifierName>", // From Inspector.print path: "modifier|<propertyPath>", // Path to the value type: <ReturnType>.self, call: "<modifierName>") } // For callback invocation func call<CallbackName>() throws { let callback = try modifierAttribute( modifierName: "<InternalModifierName>", path: "modifier|<callbackPath>", type: (() -> Void).self, call: "call<CallbackName>") callback() } } ``` **For new View types**, create the full ViewType structure: ```swift @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) public extension ViewType { struct NewViewType: KnownViewType { public static let typePrefix: String = "NewViewType" // From Inspector.print public static var namespacedPrefixes: [String] { ["SwiftUI.NewViewType"] } } } // MARK: - Content Extraction @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) extension ViewType.NewViewType: SingleViewContent { // or MultipleViewContent public static func child(_ content: Content) throws -> Content { return try Inspector.attribute(path: "content", value: content.view) } } // MARK: - Extraction from View hierarchy @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) public extension InspectableView where View == ViewType.NewViewType { // Add attribute getters based on Inspector.print analysis func someAttribute() throws -> SomeType { return try Inspector.attribute( path: "attributePath", value: content.view, type: SomeType.self) } } // MARK: - Global View hierarchy access @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) public extension InspectableView { func newViewType(_ index: Int? = nil) throws -> InspectableView<ViewType.NewViewType> { return try contentForModifierLookup.newViewType(parent: self, index: index) } } @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) internal extension Content { func newViewType(parent: UnwrappedView, index: Int?) throws -> InspectableView<ViewType.NewViewType> { let call = "newViewType(\(index == nil ? "" : "\(index!)"))" return try .init(try Inspector.attribute(path: "content", value: view), parent: parent, call: call, index: index) } } ``` ### Step 6: Add Tests **IMPORTANT: Use contextual test structure based on Step 2 research.** Tests should reflect real-world usage patterns, not just technical functionality. **For views that belong inside specific containers:** ```swift import XCTest import SwiftUI @testable import ViewInspector // Example: Tab is meant to be used inside TabView @available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *) final class TabTests: XCTestCase { // Test extraction in proper context (inside TabView) func testTabInsideTabView() throws { let sut = TabView { Tab("Home", systemImage: "house") { Text("Home Content") } Tab("Settings", systemImage: "gear") { Text("Settings Content") } } let tab = try sut.inspect().tabView().tab(0) XCTAssertEqual(try tab.labelView().text().string(), "Home") } // Test searching for content inside Tab inside TabView func testSearchForContentInsideTab() throws { let sut = TabView { Tab("Home", systemImage: "house") { Text("Home Content") } } XCTAssertEqual( try sut.inspect().find(text: "Home Content").pathToRoot, "tabView().tab(0).text()") } } ``` **For modifiers that apply to specific view types:** ```swift // Example: subscriptionStoreButtonLabel applies to SubscriptionStoreView @available(iOS 17.0, macOS 14.0, *) final class SubscriptionModifiersTests: XCTestCase { func testSubscriptionStoreButtonLabel() throws { let sut = SubscriptionStoreView(productIDs: ["com.app.subscription"]) .subscriptionStoreButtonLabel(.multiline) let label = try sut.inspect().subscriptionStoreView().subscriptionStoreButtonLabel() XCTAssertEqual(label, .multiline) } } // Example: listRowBackground applies to views inside List @available(iOS 13.0, macOS 10.15, tvOS 13.0, *) final class ListModifiersTests: XCTestCase { func testListRowBackgroundInsideList() throws { let sut = List { Text("Row") .listRowBackground(Color.red) } let background = try sut.inspect().list().text(0).listRowBackground() // Verify the background color } } ``` **For style modifiers, test with the view type they style:** ```swift // Example: buttonStyle applies to Button @available(iOS 13.0, macOS 10.15, tvOS 13.0, *) final class ButtonStyleTests: XCTestCase { func testButtonStyleOnButton() throws { let sut = Button("Tap") { } .buttonStyle(.borderedProminent) let style = try sut.inspect().button().buttonStyle() // Verify style properties } } ``` **Generic test file structure (when no specific context applies):** ```swift import XCTest import SwiftUI @testable import ViewInspector @available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) final class NewViewTypeTests: XCTestCase { // Test basic extraction func testExtractionFromSingleViewContainer() throws { let view = AnyView(NewViewType()) XCTAssertNoThrow(try view.inspect().anyView().newViewType()) } // Test attribute inspection func testSomeAttributeInspection() throws { let sut = NewViewType(someParam: .value) let value = try sut.inspect().newViewType().someAttribute() XCTAssertEqual(value, .value) } // Test in view hierarchy func testSearch() throws { let view = HStack { NewViewType() } XCTAssertEqual(try view.inspect().find(ViewType.NewViewType.self).pathToRoot, "hStack().newViewType(0)") } } ``` **For generic modifiers (not container-specific):** ```swift @available(iOS 13.0, macOS 10.15, tvOS 13.0, *) final class SomeModifierTests: XCTestCase { func testModifierApplication() throws { let sut = EmptyView().someModifier(value: 42) XCTAssertNoThrow(try sut.inspect().emptyView()) } func testModifierValueInspection() throws {
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub