Full Code of soundcloud/Axt for AI

master 23a8a92bd77a cached
46 files
68.1 KB
20.7k tokens
1 requests
Download .txt
Repository: soundcloud/Axt
Branch: master
Commit: 23a8a92bd77a
Files: 46
Total size: 68.1 KB

Directory structure:
gitextract_772q8wgy/

├── .gitignore
├── Examples/
│   └── AxtExamples/
│       ├── AxtExamples/
│       │   ├── Assets.xcassets/
│       │   │   ├── AccentColor.colorset/
│       │   │   │   └── Contents.json
│       │   │   ├── AppIcon.appiconset/
│       │   │   │   └── Contents.json
│       │   │   └── Contents.json
│       │   ├── AxtExamplesApp.swift
│       │   ├── ConfirmationAlertModifier.swift
│       │   ├── ContentView.swift
│       │   ├── CustomControls.swift
│       │   ├── GestureView.swift
│       │   ├── MoreButton.swift
│       │   ├── NativeViews.swift
│       │   ├── Preview Content/
│       │   │   └── Preview Assets.xcassets/
│       │   │       └── Contents.json
│       │   └── TogglesView.swift
│       ├── AxtExamples.xcodeproj/
│       │   ├── project.pbxproj
│       │   └── project.xcworkspace/
│       │       ├── contents.xcworkspacedata
│       │       └── xcshareddata/
│       │           └── IDEWorkspaceChecks.plist
│       └── AxtExamplesTests/
│           ├── ConfirmationAlertModifierTests.swift
│           ├── CustomControlsTests.swift
│           ├── GestureViewTests.swift
│           ├── MoreButtonTests.swift
│           ├── NativeViewsTests.swift
│           └── TogglesViewTests.swift
├── Package.swift
├── README.md
└── Sources/
    └── Axt/
        ├── Axt.swift
        ├── AxtTest/
        │   ├── Axt+description.swift
        │   ├── Axt+find.swift
        │   ├── AxtChildNode.swift
        │   ├── AxtElement.swift
        │   ├── AxtNode.swift
        │   ├── AxtTest.swift
        │   ├── Publisher+Compatibility.swift
        │   ├── Publisher+firstValue.swift
        │   └── printHierarchy.swift
        ├── AxtView.swift
        ├── Modifier.swift
        ├── Native/
        │   ├── Button.swift
        │   ├── NavigationLink.swift
        │   ├── Text.swift
        │   ├── TextField.swift
        │   └── Toggle.swift
        ├── NativeView.swift
        ├── View+testData.swift
        ├── View+testId.swift
        ├── dig.swift
        └── hostAxtSheet.swift

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
.DS_Store
/.build
/Packages
/*.xcodeproj
xcuserdata/
DerivedData/
.swiftpm/config/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc


================================================
FILE: Examples/AxtExamples/AxtExamples/Assets.xcassets/AccentColor.colorset/Contents.json
================================================
{
  "colors" : [
    {
      "idiom" : "universal"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "20x20"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "20x20"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "29x29"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "40x40"
    },
    {
      "idiom" : "iphone",
      "scale" : "2x",
      "size" : "60x60"
    },
    {
      "idiom" : "iphone",
      "scale" : "3x",
      "size" : "60x60"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "20x20"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "20x20"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "29x29"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "29x29"
    },
    {
      "idiom" : "ipad",
      "scale" : "1x",
      "size" : "40x40"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "40x40"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "76x76"
    },
    {
      "idiom" : "ipad",
      "scale" : "2x",
      "size" : "83.5x83.5"
    },
    {
      "idiom" : "ios-marketing",
      "scale" : "1x",
      "size" : "1024x1024"
    }
  ],
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/AxtExamplesApp.swift
================================================
import SwiftUI

@main
struct AxtExamplesApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/ConfirmationAlertModifier.swift
================================================
import SwiftUI

struct ConfirmationAlertModifier: ViewModifier {
    @Binding var isPresented: Bool

    let message: String
    let action1: () -> Void
    let action2: () -> Void

    func body(content: Content) -> some View {
        content.alert(isPresented: $isPresented) {
            Alert(
                title: Text(message),
                primaryButton: .default(Text("1"), action: action1),
                secondaryButton: .default(Text("2"), action: action2))
        }
        .testId(insert: "button_1", when: isPresented, label: "1", action: action1)
        .testId(insert: "button_2", when: isPresented, label: "2", action: action2)
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/ContentView.swift
================================================
import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello, world!")
            .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/CustomControls.swift
================================================
import SwiftUI

struct CustomControls: View {
    @State var counter = 0

    var body: some View {
        MyButton() { counter += 1 }
            .testId("my_button")
            .testId(insert: "counter", value: counter)
    }

}

struct MyButton: View {
    let action: () -> Void

    var body: some View {
        Button("Tap me") { action() }
            .testData(action: action)
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/GestureView.swift
================================================
import SwiftUI

struct GestureView: View {
    @State private var dragY: CGFloat = 0

    var body: some View {
        knob
            .testId("knob")
            .frame(width: 50, height: 50)
            .offset(x: 0, y: dragY)
            .gesture(gesture)
            .testId(insert: "drag", value: dragY, setValue: { dragY = $0 as? CGFloat ?? 0 })
    }

    @ViewBuilder private var knob: some View {
        if abs(dragY) > 300 {
            Circle()
                .testData(value: "circle")
        }
        else {
            Rectangle()
                .testData(value: "rectangle")
        }
    }

    private var gesture: some Gesture {
        DragGesture()
            .onChanged { value in
                dragY = value.translation.height
            }
            .onEnded { value in
                withAnimation(.spring()) {
                    dragY = 0
                }
            }
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/MoreButton.swift
================================================
import SwiftUI
import Axt

struct LessMenu: View {
    @State private var isPresented = false

    var body: some View {
        Button("...") { isPresented = true }
            .testId("more_button", type: .button)
            .sheet(isPresented: $isPresented) {
                MoreMenu()
                    .hostAxtSheet()
            }
    }
}

struct MoreMenu: View {
    var body: some View {
        Text("What's more?")
            .testId("more_text", type: .text)
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/NativeViews.swift
================================================
import SwiftUI

struct NativeViews: View {
    @State var counter = 0
    @State var name = ""

    var body: some View {
        List {
            let counterDescription = "Counter: \(counter)"
            Text(counterDescription)
                .testId("counter_label", type: .text)
            Button("Tap", action: { counter += 1 })
                .testId("tap_button", type: .button)
            NavigationLink("More", destination: Text("More..."))
                .testId("more_link", type: .navigationLink)
            TextField("Name", text: $name)
                .testId("name_field", type: .textField)
        }

    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/Preview Content/Preview Assets.xcassets/Contents.json
================================================
{
  "info" : {
    "author" : "xcode",
    "version" : 1
  }
}


================================================
FILE: Examples/AxtExamples/AxtExamples/TogglesView.swift
================================================
import Foundation
import SwiftUI
import Axt

struct TogglesView: View {
    @State var showMore = false

    @State var value1 = false
    @State var value2 = false
    @State var value3 = false
    @State var value4 = false

    var body: some View {
        List {
            Toggle("1", isOn: $value1)
                .testId("toggle_1", type: .toggle)
            Toggle("Show more", isOn: $showMore)
                .testId("show_more", type: .toggle)
            if showMore {
                Toggle("2", isOn: $value2)
                    .testId("toggle_2", type: .toggle)
                Toggle("3", isOn: $value3)
                    .testId("toggle_3", type: .toggle)
                Toggle("4", isOn: $value4)
                    .testId("toggle_4", type: .toggle)
            }
        }
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamples.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 55;
	objects = {

/* Begin PBXBuildFile section */
		2E45AE70287577060042F247 /* NativeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45AE6F287577060042F247 /* NativeViews.swift */; };
		2E45AE72287577BF0042F247 /* NativeViewsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45AE71287577BF0042F247 /* NativeViewsTests.swift */; };
		2E8DFD092874755F0030D715 /* AxtExamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */; };
		2E8DFD0B2874755F0030D715 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0A2874755F0030D715 /* ContentView.swift */; };
		2E8DFD0D287475600030D715 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0C287475600030D715 /* Assets.xcassets */; };
		2E8DFD10287475600030D715 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0F287475600030D715 /* Preview Assets.xcassets */; };
		2E8DFD1A287475600030D715 /* TogglesViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD19287475600030D715 /* TogglesViewTests.swift */; };
		2E8DFD332874758B0030D715 /* TogglesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD322874758B0030D715 /* TogglesView.swift */; };
		2E8DFD39287476A90030D715 /* Axt in Frameworks */ = {isa = PBXBuildFile; productRef = 2E8DFD38287476A90030D715 /* Axt */; };
		2E8DFD3D287476BA0030D715 /* Axt in Frameworks */ = {isa = PBXBuildFile; productRef = 2E8DFD3C287476BA0030D715 /* Axt */; };
		2EB4C36B28758F520026D3C0 /* CustomControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36A28758F520026D3C0 /* CustomControls.swift */; };
		2EB4C36D287593220026D3C0 /* CustomControlsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */; };
		2EB4C36F287593FD0026D3C0 /* ConfirmationAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */; };
		2EB4C3712875959D0026D3C0 /* ConfirmationAlertModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */; };
		2EB4C3732875A2830026D3C0 /* GestureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3722875A2830026D3C0 /* GestureView.swift */; };
		2EB4C3752875A4D40026D3C0 /* GestureViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */; };
		2EB4C3772875A8500026D3C0 /* MoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3762875A8500026D3C0 /* MoreButton.swift */; };
		2EB4C3792875A8F90026D3C0 /* MoreButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		2E8DFD16287475600030D715 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = 2E8DFCFD2874755F0030D715 /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = 2E8DFD042874755F0030D715;
			remoteInfo = AxtExamples;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
		2E45AE6F287577060042F247 /* NativeViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeViews.swift; sourceTree = "<group>"; };
		2E45AE71287577BF0042F247 /* NativeViewsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeViewsTests.swift; sourceTree = "<group>"; };
		2E8DFD052874755F0030D715 /* AxtExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AxtExamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
		2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AxtExamplesApp.swift; sourceTree = "<group>"; };
		2E8DFD0A2874755F0030D715 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
		2E8DFD0C287475600030D715 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
		2E8DFD0F287475600030D715 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
		2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AxtExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
		2E8DFD19287475600030D715 /* TogglesViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TogglesViewTests.swift; sourceTree = "<group>"; };
		2E8DFD322874758B0030D715 /* TogglesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TogglesView.swift; sourceTree = "<group>"; };
		2E8DFD34287476260030D715 /* Axt */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Axt; path = ../..; sourceTree = "<group>"; };
		2EB4C36A28758F520026D3C0 /* CustomControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomControls.swift; sourceTree = "<group>"; };
		2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomControlsTests.swift; sourceTree = "<group>"; };
		2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertModifier.swift; sourceTree = "<group>"; };
		2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertModifierTests.swift; sourceTree = "<group>"; };
		2EB4C3722875A2830026D3C0 /* GestureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureView.swift; sourceTree = "<group>"; };
		2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureViewTests.swift; sourceTree = "<group>"; };
		2EB4C3762875A8500026D3C0 /* MoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreButton.swift; sourceTree = "<group>"; };
		2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreButtonTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
		2E8DFD022874755F0030D715 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2E8DFD39287476A90030D715 /* Axt in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2E8DFD12287475600030D715 /* Frameworks */ = {
			isa = PBXFrameworksBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2E8DFD3D287476BA0030D715 /* Axt in Frameworks */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
		2E8DFCFC2874755F0030D715 = {
			isa = PBXGroup;
			children = (
				2E8DFD34287476260030D715 /* Axt */,
				2E8DFD072874755F0030D715 /* AxtExamples */,
				2E8DFD18287475600030D715 /* AxtExamplesTests */,
				2E8DFD062874755F0030D715 /* Products */,
				2E8DFD37287476A90030D715 /* Frameworks */,
			);
			sourceTree = "<group>";
		};
		2E8DFD062874755F0030D715 /* Products */ = {
			isa = PBXGroup;
			children = (
				2E8DFD052874755F0030D715 /* AxtExamples.app */,
				2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */,
			);
			name = Products;
			sourceTree = "<group>";
		};
		2E8DFD072874755F0030D715 /* AxtExamples */ = {
			isa = PBXGroup;
			children = (
				2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */,
				2E8DFD0A2874755F0030D715 /* ContentView.swift */,
				2E8DFD322874758B0030D715 /* TogglesView.swift */,
				2E45AE6F287577060042F247 /* NativeViews.swift */,
				2EB4C36A28758F520026D3C0 /* CustomControls.swift */,
				2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */,
				2EB4C3722875A2830026D3C0 /* GestureView.swift */,
				2EB4C3762875A8500026D3C0 /* MoreButton.swift */,
				2E8DFD0C287475600030D715 /* Assets.xcassets */,
				2E8DFD0E287475600030D715 /* Preview Content */,
			);
			path = AxtExamples;
			sourceTree = "<group>";
		};
		2E8DFD0E287475600030D715 /* Preview Content */ = {
			isa = PBXGroup;
			children = (
				2E8DFD0F287475600030D715 /* Preview Assets.xcassets */,
			);
			path = "Preview Content";
			sourceTree = "<group>";
		};
		2E8DFD18287475600030D715 /* AxtExamplesTests */ = {
			isa = PBXGroup;
			children = (
				2E8DFD19287475600030D715 /* TogglesViewTests.swift */,
				2E45AE71287577BF0042F247 /* NativeViewsTests.swift */,
				2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */,
				2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */,
				2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */,
				2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */,
			);
			path = AxtExamplesTests;
			sourceTree = "<group>";
		};
		2E8DFD37287476A90030D715 /* Frameworks */ = {
			isa = PBXGroup;
			children = (
			);
			name = Frameworks;
			sourceTree = "<group>";
		};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
		2E8DFD042874755F0030D715 /* AxtExamples */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 2E8DFD29287475600030D715 /* Build configuration list for PBXNativeTarget "AxtExamples" */;
			buildPhases = (
				2E8DFD012874755F0030D715 /* Sources */,
				2E8DFD022874755F0030D715 /* Frameworks */,
				2E8DFD032874755F0030D715 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				2E8DFD36287476A50030D715 /* PBXTargetDependency */,
			);
			name = AxtExamples;
			packageProductDependencies = (
				2E8DFD38287476A90030D715 /* Axt */,
			);
			productName = AxtExamples;
			productReference = 2E8DFD052874755F0030D715 /* AxtExamples.app */;
			productType = "com.apple.product-type.application";
		};
		2E8DFD14287475600030D715 /* AxtExamplesTests */ = {
			isa = PBXNativeTarget;
			buildConfigurationList = 2E8DFD2C287475600030D715 /* Build configuration list for PBXNativeTarget "AxtExamplesTests" */;
			buildPhases = (
				2E8DFD11287475600030D715 /* Sources */,
				2E8DFD12287475600030D715 /* Frameworks */,
				2E8DFD13287475600030D715 /* Resources */,
			);
			buildRules = (
			);
			dependencies = (
				2E8DFD3B287476B40030D715 /* PBXTargetDependency */,
				2E8DFD17287475600030D715 /* PBXTargetDependency */,
			);
			name = AxtExamplesTests;
			packageProductDependencies = (
				2E8DFD3C287476BA0030D715 /* Axt */,
			);
			productName = AxtExamplesTests;
			productReference = 2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */;
			productType = "com.apple.product-type.bundle.unit-test";
		};
/* End PBXNativeTarget section */

/* Begin PBXProject section */
		2E8DFCFD2874755F0030D715 /* Project object */ = {
			isa = PBXProject;
			attributes = {
				BuildIndependentTargetsInParallel = 1;
				LastSwiftUpdateCheck = 1330;
				LastUpgradeCheck = 1330;
				TargetAttributes = {
					2E8DFD042874755F0030D715 = {
						CreatedOnToolsVersion = 13.3.1;
					};
					2E8DFD14287475600030D715 = {
						CreatedOnToolsVersion = 13.3.1;
						TestTargetID = 2E8DFD042874755F0030D715;
					};
				};
			};
			buildConfigurationList = 2E8DFD002874755F0030D715 /* Build configuration list for PBXProject "AxtExamples" */;
			compatibilityVersion = "Xcode 13.0";
			developmentRegion = en;
			hasScannedForEncodings = 0;
			knownRegions = (
				en,
				Base,
			);
			mainGroup = 2E8DFCFC2874755F0030D715;
			productRefGroup = 2E8DFD062874755F0030D715 /* Products */;
			projectDirPath = "";
			projectRoot = "";
			targets = (
				2E8DFD042874755F0030D715 /* AxtExamples */,
				2E8DFD14287475600030D715 /* AxtExamplesTests */,
			);
		};
/* End PBXProject section */

/* Begin PBXResourcesBuildPhase section */
		2E8DFD032874755F0030D715 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2E8DFD10287475600030D715 /* Preview Assets.xcassets in Resources */,
				2E8DFD0D287475600030D715 /* Assets.xcassets in Resources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2E8DFD13287475600030D715 /* Resources */ = {
			isa = PBXResourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXResourcesBuildPhase section */

/* Begin PBXSourcesBuildPhase section */
		2E8DFD012874755F0030D715 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2E8DFD0B2874755F0030D715 /* ContentView.swift in Sources */,
				2EB4C36F287593FD0026D3C0 /* ConfirmationAlertModifier.swift in Sources */,
				2EB4C3772875A8500026D3C0 /* MoreButton.swift in Sources */,
				2E45AE70287577060042F247 /* NativeViews.swift in Sources */,
				2EB4C3732875A2830026D3C0 /* GestureView.swift in Sources */,
				2EB4C36B28758F520026D3C0 /* CustomControls.swift in Sources */,
				2E8DFD332874758B0030D715 /* TogglesView.swift in Sources */,
				2E8DFD092874755F0030D715 /* AxtExamplesApp.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		2E8DFD11287475600030D715 /* Sources */ = {
			isa = PBXSourcesBuildPhase;
			buildActionMask = 2147483647;
			files = (
				2E8DFD1A287475600030D715 /* TogglesViewTests.swift in Sources */,
				2EB4C36D287593220026D3C0 /* CustomControlsTests.swift in Sources */,
				2EB4C3792875A8F90026D3C0 /* MoreButtonTests.swift in Sources */,
				2EB4C3712875959D0026D3C0 /* ConfirmationAlertModifierTests.swift in Sources */,
				2EB4C3752875A4D40026D3C0 /* GestureViewTests.swift in Sources */,
				2E45AE72287577BF0042F247 /* NativeViewsTests.swift in Sources */,
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
/* End PBXSourcesBuildPhase section */

/* Begin PBXTargetDependency section */
		2E8DFD17287475600030D715 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			target = 2E8DFD042874755F0030D715 /* AxtExamples */;
			targetProxy = 2E8DFD16287475600030D715 /* PBXContainerItemProxy */;
		};
		2E8DFD36287476A50030D715 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			productRef = 2E8DFD35287476A50030D715 /* Axt */;
		};
		2E8DFD3B287476B40030D715 /* PBXTargetDependency */ = {
			isa = PBXTargetDependency;
			productRef = 2E8DFD3A287476B40030D715 /* Axt */;
		};
/* End PBXTargetDependency section */

/* Begin XCBuildConfiguration section */
		2E8DFD27287475600030D715 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = dwarf;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				ENABLE_TESTABILITY = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_DYNAMIC_NO_PIC = NO;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_OPTIMIZATION_LEVEL = 0;
				GCC_PREPROCESSOR_DEFINITIONS = (
					"DEBUG=1",
					"$(inherited)",
				);
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 15.4;
				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
				MTL_FAST_MATH = YES;
				ONLY_ACTIVE_ARCH = YES;
				SDKROOT = iphoneos;
				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
			};
			name = Debug;
		};
		2E8DFD28287475600030D715 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_SEARCH_USER_PATHS = NO;
				CLANG_ANALYZER_NONNULL = YES;
				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
				CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
				CLANG_ENABLE_MODULES = YES;
				CLANG_ENABLE_OBJC_ARC = YES;
				CLANG_ENABLE_OBJC_WEAK = YES;
				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
				CLANG_WARN_BOOL_CONVERSION = YES;
				CLANG_WARN_COMMA = YES;
				CLANG_WARN_CONSTANT_CONVERSION = YES;
				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
				CLANG_WARN_EMPTY_BODY = YES;
				CLANG_WARN_ENUM_CONVERSION = YES;
				CLANG_WARN_INFINITE_RECURSION = YES;
				CLANG_WARN_INT_CONVERSION = YES;
				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
				CLANG_WARN_STRICT_PROTOTYPES = YES;
				CLANG_WARN_SUSPICIOUS_MOVE = YES;
				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
				CLANG_WARN_UNREACHABLE_CODE = YES;
				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
				COPY_PHASE_STRIP = NO;
				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
				ENABLE_NS_ASSERTIONS = NO;
				ENABLE_STRICT_OBJC_MSGSEND = YES;
				GCC_C_LANGUAGE_STANDARD = gnu11;
				GCC_NO_COMMON_BLOCKS = YES;
				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
				GCC_WARN_UNDECLARED_SELECTOR = YES;
				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
				GCC_WARN_UNUSED_FUNCTION = YES;
				GCC_WARN_UNUSED_VARIABLE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 15.4;
				MTL_ENABLE_DEBUG_INFO = NO;
				MTL_FAST_MATH = YES;
				SDKROOT = iphoneos;
				SWIFT_COMPILATION_MODE = wholemodule;
				SWIFT_OPTIMIZATION_LEVEL = "-O";
				VALIDATE_PRODUCT = YES;
			};
			name = Release;
		};
		2E8DFD2A287475600030D715 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				DEVELOPMENT_ASSET_PATHS = "\"AxtExamples/Preview Content\"";
				DEVELOPMENT_TEAM = JFV288DQ9Q;
				ENABLE_PREVIEWS = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
				INFOPLIST_KEY_UILaunchScreen_Generation = YES;
				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamples;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Debug;
		};
		2E8DFD2B287475600030D715 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				DEVELOPMENT_ASSET_PATHS = "\"AxtExamples/Preview Content\"";
				DEVELOPMENT_TEAM = JFV288DQ9Q;
				ENABLE_PREVIEWS = YES;
				GENERATE_INFOPLIST_FILE = YES;
				INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
				INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
				INFOPLIST_KEY_UILaunchScreen_Generation = YES;
				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
				INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
				LD_RUNPATH_SEARCH_PATHS = (
					"$(inherited)",
					"@executable_path/Frameworks",
				);
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamples;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = YES;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
			};
			name = Release;
		};
		2E8DFD2D287475600030D715 /* Debug */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				DEVELOPMENT_TEAM = JFV288DQ9Q;
				GENERATE_INFOPLIST_FILE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 15.4;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamplesTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = NO;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AxtExamples.app/AxtExamples";
			};
			name = Debug;
		};
		2E8DFD2E287475600030D715 /* Release */ = {
			isa = XCBuildConfiguration;
			buildSettings = {
				ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
				BUNDLE_LOADER = "$(TEST_HOST)";
				CODE_SIGN_STYLE = Automatic;
				CURRENT_PROJECT_VERSION = 1;
				DEVELOPMENT_TEAM = JFV288DQ9Q;
				GENERATE_INFOPLIST_FILE = YES;
				IPHONEOS_DEPLOYMENT_TARGET = 15.4;
				MARKETING_VERSION = 1.0;
				PRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamplesTests;
				PRODUCT_NAME = "$(TARGET_NAME)";
				SWIFT_EMIT_LOC_STRINGS = NO;
				SWIFT_VERSION = 5.0;
				TARGETED_DEVICE_FAMILY = "1,2";
				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AxtExamples.app/AxtExamples";
			};
			name = Release;
		};
/* End XCBuildConfiguration section */

/* Begin XCConfigurationList section */
		2E8DFD002874755F0030D715 /* Build configuration list for PBXProject "AxtExamples" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2E8DFD27287475600030D715 /* Debug */,
				2E8DFD28287475600030D715 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		2E8DFD29287475600030D715 /* Build configuration list for PBXNativeTarget "AxtExamples" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2E8DFD2A287475600030D715 /* Debug */,
				2E8DFD2B287475600030D715 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
		2E8DFD2C287475600030D715 /* Build configuration list for PBXNativeTarget "AxtExamplesTests" */ = {
			isa = XCConfigurationList;
			buildConfigurations = (
				2E8DFD2D287475600030D715 /* Debug */,
				2E8DFD2E287475600030D715 /* Release */,
			);
			defaultConfigurationIsVisible = 0;
			defaultConfigurationName = Release;
		};
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
		2E8DFD35287476A50030D715 /* Axt */ = {
			isa = XCSwiftPackageProductDependency;
			productName = Axt;
		};
		2E8DFD38287476A90030D715 /* Axt */ = {
			isa = XCSwiftPackageProductDependency;
			productName = Axt;
		};
		2E8DFD3A287476B40030D715 /* Axt */ = {
			isa = XCSwiftPackageProductDependency;
			productName = Axt;
		};
		2E8DFD3C287476BA0030D715 /* Axt */ = {
			isa = XCSwiftPackageProductDependency;
			productName = Axt;
		};
/* End XCSwiftPackageProductDependency section */
	};
	rootObject = 2E8DFCFD2874755F0030D715 /* Project object */;
}


================================================
FILE: Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>IDEDidComputeMac32BitWarning</key>
	<true/>
</dict>
</plist>


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/ConfirmationAlertModifierTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt
import SwiftUI

@MainActor
class ConfirmationAlertModifierTests: XCTestCase {

//    func testWatch() async {
//        struct MyView: View {
//            @State var alertPresented = false
//            var body: some View {
//                Button("Present alert") { alertPresented.toggle() }
//                    .modifier(ConfirmationAlertModifier(isPresented: $alertPresented, message: "Are you sure?", action1: { print("yes") }, action2: { print("no") }))
//            }
//        }
//        let test = await AxtTest.host(MyView())
//        await test.watchHierarchy()
//    }

}


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/CustomControlsTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt

@MainActor
class CustomControlsTests: XCTestCase {

//    func testWatch() async {
//        let test = await AxtTest.host(CustomControls())
//        await test.watchHierarchy()
//    }

}


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/GestureViewTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt
import SwiftUI

@MainActor
class GestureViewTests: XCTestCase {

//    func testWatch() async {
//        let test = await AxtTest.host(GestureView())
//
//        await test.watchHierarchy()
//    }

    func testKnob() async throws {
        let test = await AxtTest.host(GestureView())
        let knob = try XCTUnwrap(test.find(id: "knob"))
        let dragValue = try XCTUnwrap(test.find(id: "drag"))

        let drag: CGFloat = 500.0
        dragValue.setValue(drag)
        await AxtTest.yield()

        XCTAssertEqual(knob.value as? String, "circle")
    }

}


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/MoreButtonTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt
import SwiftUI

@MainActor
class MoreButtonTests: XCTestCase {

    func test_sheet() async throws {
        let test = await AxtTest.host(LessMenu())
        let button = try XCTUnwrap(test.find(id: "more_button"))

        await button.performAction()

        let sheet = try XCTUnwrap(AxtTest.sheets.first?.value)
        XCTAssertNotNil(sheet.find(id: "more_text"))
    }
}


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/NativeViewsTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt

@MainActor
class NativeViewsTests: XCTestCase {

//    func testWatch() async {
//        let test = await AxtTest.host(NativeViews())
//        await test.watchHierarchy()
//    }

}


================================================
FILE: Examples/AxtExamples/AxtExamplesTests/TogglesViewTests.swift
================================================
import XCTest
@testable import AxtExamples
import Axt

@MainActor
class TogglesViewTests: XCTestCase {

    func testWatch() async {
        let test = await AxtTest.host(TogglesView())
        await test.watchHierarchy()
    }

    func testShowMore() async throws {
        let test = await AxtTest.host(TogglesView())
        let moreToggle = try XCTUnwrap(test.find(id: "show_more"))

        moreToggle.performActionWithoutYielding()

        try await test.waitForCondition(timeout: 1) {
            test.find(id: "toggle_2") != nil
        }
    }

}


================================================
FILE: Package.swift
================================================
// swift-tools-version: 5.6

import PackageDescription

let package = Package(
    name: "Axt",
    platforms: [.iOS(.v14)],
    products: [
        .library(
            name: "Axt",
            targets: ["Axt"]),
    ],
    targets: [
        .target(
            name: "Axt",
            dependencies: [],
            swiftSettings: [
                .define("TESTABLE", .when(configuration: .debug))
            ]),
    ]
)


================================================
FILE: README.md
================================================
# 🪓 Axt 

![](https://user-images.githubusercontent.com/13484323/185608030-21c45ddc-f90b-42e9-a8ac-e855bb090aea.svg)
![](https://user-images.githubusercontent.com/13484323/185608132-f90bd70e-4518-404d-9ba7-c24739f7c2b2.svg)
![](https://user-images.githubusercontent.com/13484323/200010051-3270dd90-1edd-42ff-b94f-cb8ba3618a4e.svg)

Axt is a testing library for SwiftUI.

Unit tests using Axt can interact with SwiftUI views, which are running live in the simulator and are in a fully functional state.

```swift
struct MyView: View {
    @State var showMore = false

    var body: some View {
        VStack {
            Toggle("Show more", isOn: $showMore)
                .testId("show_more_toggle", type: .toggle)
            if showMore {
                Text("More")
                    .testId("more_text", type: .text)
            }
        }
    }
}
```

```swift
@MainActor
class MyViewTests: XCTestCase {
    func testShowMore() async throws {
        let test = await AxtTest.host(MyView())
        let showMoreToggle = test.find(id: "show_more_toggle")

        await showMoreToggle?.performAction()

        XCTAssertEqual(showMoreToggle?.value as? Bool, true)
        XCTAssertEqual(test.find(id: "more_text")?.label, "More")
    }
}
```

## Getting started

Follow the steps below to add Axt to an existing project. Note that Axt should be used with unit test targets, and not with UI test targets.

1. Add the Axt Swift package as a dependency to your Xcode project.
2. Link both your app target and unit test target to the Axt library. If the project is built for release, it will only contain stubs for Axt and no inspection code.
3. Make sure your unit test target has a host application. We need some app to host the views to test, but the views do not need to be part of this host application.

## Documentation 

### Exposing views

To expose a view, you give it an identifier with the `testId` modifier.

Take this list of toggles, and notice the `toggle_1`, `show_more` and `toggle_2` identifiers.

```swift
List {
    Toggle("1", isOn: $value1)
        .testId("toggle_1", type: .toggle)
    Toggle("Show more", isOn: $showMore)
        .testId("show_more", type: .toggle)
    if showMore {
        Toggle("2", isOn: $value2)
            .testId("toggle_2", type: .toggle)
    }
}
.testId("toggle_list")
```

This will be exposed to the tests as below.

```
→ app
  → toggle_list
    → toggle_1 label="1" value=false action
    → show_more label="Show more" value=false action
```

There are different ways to expose views to unit tests, depending on whether they are built-in or custom views. You can also attach Axt elements without explicit child views to a view.

#### Native views

To enable Axt on native SwiftUI views, you need to tell Axt what kind of view it needs to look for. The following built-in views are supported.

##### Button

```swift
Button("Tap me") { tap() }
    .testId("tap_button", type: .button)
```

```
→ tap_button label="Tap me" action
```

##### Toggle

```swift
Toggle("Toggle me", isOn: $isOn)
    .testId("is_on_toggle", type: .toggle)
```

```
→ is_on_toggle label="Toggle me" value=true action
```

##### NavigationLink

```swift
NavigationLink("More", destination: Destination())
    .testId("more_link", type: .navigationLink)
```

```
→ more_link label="More" action
```

##### TextField

```swift
TextField("Name", text: $name)
    .testId("name_field", type: .textField)
```

```
→ name_field label="Name" value="" action
```

#### Custom views

For custom views, you can specify values or functionality manually to expose them to views.

```swift
Color.blue.frame(width: 50, height: 50)
    .testId("color_1", value: "blue")
Color.red.frame(width: 50, height: 50)
    .testId("color_2", value: "red")
```

These can now be accessed from tests.

```
→ app
  → color_1 value=blue
  → color_2 value=red
```

You can also add closures to perform from tests (using the `action` parameter) or a way to set a value (using the `setValue` parameter).

#### Re-usable controls

It is common to want to specify values or functionality for re-usable controls, but allow clients to set the test identifier or override values or functionality. This would be the case for custom buttons or search bars. For this, use the `testData` modifier.

```swift
struct MyButton: View {
    let action: () -> Void

    var body: some View {
        Button("Tap me!") { action() }
            .testData(action: action)
    }
}

MyButton(action: action)
    .testId("my_button")
```

There will only be a single element for this button exposed to the tests.

```
→ app
  → my_button action
```

Using the `testData` modifier only results in an element exposed to tests, if an identifier is provided somewhere higher up in the view hierarchy.

Do not use the `testId(:type:)` modifiers for native views on custom controls. For custom controls, extracting data from views is not necessary.

#### Inserting extra elements

Sometimes it can be useful to insert Axt elements that do not correspond to a SwiftUI view. This can be useful to expose buttons that are handled in UIKit, or to interact with gestures or other objects that are not views, or provide an easy way to interact with view state when testing a view modifier.

For example, here is how we can expose the contents of an alert.

```swift
content.alert(isPresented: $isPresented) {
    Alert(
        title: Text(message),
        primaryButton: .default(Text("1"), action: action1),
        secondaryButton: .default(Text("2"), action: action2))
}
.testId(insert: "button_1", when: isPresented, label: "1", action: action1)
.testId(insert: "button_2", when: isPresented, label: "2", action: action2)
```

The elements will be exposed as siblings.

```
→ app
  → button_1 label="1" action
  → button_2 label="2" action
```

And here we expose a drag gesture to be testable.

```swift
@State private var dragY: CGFloat = 0

var body: some View {
    knob
        .frame(width: 50, height: 50)
        .offset(x: 0, y: dragY)
        .gesture(gesture)
        .testId(insert: "drag", value: dragY, setValue: { dragY = $0 as? CGFloat ?? 0 })
}
```

```
→ app
  → drag value=0.0
```

#### Sheets

Preferences that are set on the contents of a SwiftUI sheet are never transferred to the view presenting the sheet. You can still expose contents of a sheet, but this should be a last resort. Use the following code to add a new `AxtTest` to the `AxtTest.sheets` variable.

```swift
Button("...") { isPresented = true }
    .sheet(isPresented: $isPresented) {
        MoreMenu()
            .hostAxtSheet()
    }
```

### Writing tests

The first step to writing an Axt test is to create an asynchronous test method, and to host an Axt test with the view.

```swift
func test_myView() async {
  let test = await AxtTest.host(MyView())
  // ...
```

In addition to creating the test, this will also display `MyView` in the simulator or iPhone. It will be displayed with a red border around it, to indicite that it is presented by Axt and distinguish it from the rest of the app contents.

#### Watch the hierarchy

As a first step, we can watch view updates in the console.

```swift
await test.watchHierarchy()
```

Running this test prints the current view hierarchy in the console. The view is also interactive. If you interact with the view, a new view hierarchy will be printed in the console any time it changes.

#### Finding views

The `test` we created before is also an Axt element, namely the root element. If you have an element, you can use it to search for other elements.

You can use the `find(id: "my_button")` method to recursively search for an element with id `my_button`, or `findAll(id: "my_button")` to get an array of all the elements with this id.

```swift
let myButton = try XCTUnwrap(test.find(id: "my_button"))
```

You can also get the direct children of an element using the `children` method. To recursively get all elements underneath another element, use the `all` property instead.

#### Assert on elements

You can check if an Axt element (still) exists (`exists`). It has an identifier given to it through the `testId` modifier (`id`), and optionally a label (`label`), value (`value`), way to perform an action (`performAction()`), and way to set the value (`setValue`).

For any Axt element, you can use `await element.watchHierarchy()` to see how the hierarchy changes while interacting with it in the simulator or on your iPhone.

#### The lifetime of Axt elements

An Axt element points to a view that is exposed to Axt by the methods presented before, but it differs to a view in that it is a reference type. If a view is re-evaluated, an Axt element that points to that view will be updated, but the same object. The Axt element will track changes in the view. That means you can store an Axt element, make changes to the SwiftUI state, and then check the Axt element again.

```swift
let test = await AxtTest.host(MyView())
let label = try XCTUnwrap(test.find(id: "my_label")
let toggle = try XCTUnwrap(test.find(id: "my_toggle"))

XCTAssertEqual(label.value as? String, "yes")

await toggle.performAction()

XCTAssertEqual(label.value as? String, "no")
```

#### Waiting for view updates

If you change the state of a variable in a SwiftUI view, for example by performing an action on a control or changing a value, SwiftUI will trigger a re-evaluation of your view. However, SwiftUI does not re-evaluate the view immediately. This is done for efficiency reasons. Therefore, you cannot make an assertion immediately after changing state.

If you expect an update to happen after an action immediately after the current run loop cycle, use `performAction()`. If you don't want to give SwiftUI the time to update the views, use `performActionWithoutYielding()` instead. You can then give SwiftUI the time to update the views by calling `AxtTest.yield()`.

```swift
let test = await AxtTest.host(TogglesView())
let moreToggle = try XCTUnwrap(test.find(id: "show_more"))

moreToggle.performActionWithoutYielding()
await AxtTest.yield()

XCTAssertNotNil(test.find(id: "toggle_2"))
```

If you expect that it might take longer for the view hierarchy to update, for example because the changes are animated, you can use the `waitFor` functions on Axt elements. These functions are efficient, because they only check for changes when the view hierarchy was changed.

```swift
let test = await AxtTest.host(TogglesView())
let moreToggle = try XCTUnwrap(test.find(id: "show_more"))

await moreToggle.performAction()

XCTAssertNotNil(try await test.waitForElement(id: "toggle_2", timeout: 1))
```

There is also `waitForCondition` to wait for any boolean condition, and `waitForUpdate` that returns as soon as anything in the view hierarchy is changed.



================================================
FILE: Sources/Axt/Axt.swift
================================================
import SwiftUI

public struct Axt: Equatable {
    public static func == (lhs: Axt, rhs: Axt) -> Bool {
        lhs._uuid == rhs._uuid
    }

    /// Changes every time the Axt is re-evaluated
    public let _uuid = UUID()

    /// User-defined identifier
    public let id: String?

    /// Unique, does not change when Axt is re-evaluated
    public let nodeId: UUID

    public let label: String?
    public let value: Any?
    public let action: (() -> Void)?
    public let setValue: ((Any?) -> Void)?
    public let children: [Axt]
    public let visible: Bool
}

public struct AxtPreferenceKey: PreferenceKey {
    public static var defaultValue: [Axt] = []

    public static func reduce(value: inout [Axt], nextValue: () -> [Axt]) {
        let axts = nextValue()
        for axt in axts {
            // In iOS 16, List has a bug where preferences can appear
            // more than once, so we need to check for duplicates.
            if !value.contains(where: { $0.nodeId == axt.nodeId }) {
                value.append(axt)
            }
        }
    }
}


================================================
FILE: Sources/Axt/AxtTest/Axt+description.swift
================================================
import Foundation

#if TESTABLE

extension Axt {
    func describeHierarchy() -> String {
        describeHierarchy(level: 0)
    }

    private func describeHierarchy(level: Int = 0) -> String {
        var description = ""
        let indent = Array(repeating: " ", count: level * 2).joined()
        description += indent + "→ " + describeNode() + "\n"
        for child in children {
            description += child.describeHierarchy(level: level + 1)
        }
        return description
    }

    private func describeNode() -> String {
        var description = id ?? ""
        if let label = self.label {
            description.append(" label=\"" + label + "\"")
        }
        if let value = self.value {
            description.append(" value=" + String(describing: value))
        }
        if action != nil {
            description.append(" action")
        }
        if !visible {
            description.append(" hidden")
        }
        return description
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/Axt+find.swift
================================================
import Foundation

#if TESTABLE

extension Axt {
    func find(where condition: (Axt) -> Bool) -> Axt? {
        if condition(self) { return self }
        for child in children {
            if let match = child.find(where: condition) {
                return match
            }
        }
        return nil
    }

    var all: [Axt] {
        var all: [Axt] = []
        for child in children {
            all.append(child)
            all.append(contentsOf: child.all)
        }
        return all
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/AxtChildNode.swift
================================================
import Combine
import Foundation

#if TESTABLE

class AxtChildNode: AxtNode {
    let nodeId: UUID
    let getRoot: () -> Axt
    let rootDidChange: AnyPublisher<Void, Never>

    init(nodeId: UUID, getRoot: @escaping () -> Axt, rootDidChange: AnyPublisher<Void, Never>) {
        self.nodeId = nodeId
        self.getRoot = getRoot
        self.rootDidChange = rootDidChange
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/AxtElement.swift
================================================
import Combine
import SwiftUI

#if TESTABLE

/// Automatically updated when the view it refers to changes
public protocol AxtElement {
    var exists: Bool { get }
    var id: String! { get }
    var label: String? { get }
    var value: Any? { get }

    var children: [AxtElement] { get }
    var all: [AxtElement] { get }
    /// Recursively search for an element
    func find(id: String) -> AxtElement?
    func findAll(id: String) -> [AxtElement]

    func performActionWithoutYielding()
    func setValue(_ value: Any?)

    func waitForCondition(timeout: TimeInterval, condition: @escaping () -> Bool) async throws
    func waitForElement(id: String, timeout: TimeInterval) async throws -> AxtElement
    func waitForUpdate(timeout: TimeInterval) async throws

    func watchHierarchy() async
}

public extension AxtElement {
    func performAction() async {
        performActionWithoutYielding()
        await AxtTest.yield()
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/AxtNode.swift
================================================
import Combine
import Foundation

#if TESTABLE

protocol AxtNode: AxtElement {
    var nodeId: UUID { get }
    var getRoot: () -> Axt { get }
    var rootDidChange: AnyPublisher<Void, Never> { get }
}

extension AxtNode {
    var axt: Axt! {
        getRoot().find(where: { $0.nodeId == self.nodeId })
    }

    func makeNode(nodeId: UUID) -> AxtChildNode {
        AxtChildNode(nodeId: nodeId, getRoot: getRoot, rootDidChange: rootDidChange)
    }

    public var exists: Bool { axt != nil }

    public var id: String! { axt?.id }

    public var label: String? { axt?.label }

    public var value: Any? { axt?.value }

    public var children: [AxtElement] {
        axt?.children.map { makeNode(nodeId: $0.nodeId) } ?? []
    }

    public var all: [AxtElement] {
        axt?.all.map { makeNode(nodeId: $0.nodeId) } ?? []
    }

    public func find(id: String) -> AxtElement? {
        if let axt = axt?.find(where: { $0.id == id }) {
            return makeNode(nodeId: axt.nodeId)
        }
        return nil
    }

    public func findAll(id: String) -> [AxtElement] {
        (axt?.all ?? []).filter { $0.id == id }.map { makeNode(nodeId: $0.nodeId) }
    }

    public func performActionWithoutYielding() {
        axt?.action?()
    }

    public func setValue(_ value: Any?) {
        axt?.setValue?(value)
    }

    public func waitForCondition(timeout: TimeInterval, condition: @escaping () -> Bool) async throws {
        try await rootDidChange.filter { condition() }.firstValue(timeout: timeout)
    }

    public func waitForElement(id: String, timeout: TimeInterval) async throws -> AxtElement {
        let axt = try await rootDidChange.map { self.axt }
            .compactMap { axt in axt.find(where: { child in child.id == id }) }
            .firstValue(timeout: timeout)
        return makeNode(nodeId: axt.nodeId)
    }

    public func waitForUpdate(timeout: TimeInterval) async throws {
        _ = try await rootDidChange.map { self.axt }
            .removeDuplicates()
            .firstValue(timeout: timeout)
    }

    public func watchHierarchy() async {
        for await description in rootDidChange.compactMap({ self.axt?.describeHierarchy() }).prepend([axt.describeHierarchy()]).values {
            print("────────")
            print(description)
        }
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/AxtTest.swift
================================================
import Combine
import SwiftUI

#if TESTABLE

public final class AxtTest {
    private var window: UIWindow!
    public private(set) var hostingController: UIViewController!

    public internal(set) static var sheets: [UUID: AxtTest] = [:]
    public internal(set) static var enabled = false

    private let axtSubject = CurrentValueSubject<Axt?, Never>(nil)

    public init<V: View>(_ view: V) {
        let host = HostView(content: view, axtSubject: axtSubject)
        hostingController = UIHostingController(rootView: host)
    }

    @MainActor
    public static func host<V: View>(_ view: V) async -> AxtTest {
        let axTest = AxtTest(view)
        axTest.makeWindow()
        _ = await axTest.axtSubject.dropFirst().values.first { _ in true }
        return axTest
    }

    public func makeWindow() {
        Self.enabled = true
        let windowScenes = UIApplication.shared.connectedScenes
        guard let scene = windowScenes.first as? UIWindowScene else {
            fatalError("Could not connect to window scene, make sure the test is running from a host application.")
        }
        window = UIWindow(windowScene: scene)
        window.rootViewController = hostingController
        window.makeKeyAndVisible()
    }

    public var app: AxtElement { self }

    /// Let the current runloop cycle finish.
    /// Most of the time this is enough to let SwiftUI re-evaluate any
    /// properties and views, and can be used instead of waiting with a
    /// time-out.
    public static func yield() async {
        return await withCheckedContinuation { cont in
            DispatchQueue.main.async { [cont] in
                cont.resume()
            }
        }
    }
}

extension AxtTest: AxtNode {
    var nodeId: UUID { axtSubject.value!.nodeId }

    var getRoot: () -> Axt { { self.axtSubject.value! } }

    var rootDidChange: AnyPublisher<Void, Never> { axtSubject.map { _ in }.dropFirst().eraseToAnyPublisher() }
}

private struct HostView<Content: View>: View {
    let content: Content
    let axtSubject: CurrentValueSubject<Axt?, Never>

    var body: some View {
        content
            .border(.red, width: 2)
            .padding()
            .testId("app")
            .backgroundPreferenceValue(AxtPreferenceKey.self) {
                // This is used instead of `onPreferenceChange` because that
                // has a safety mechanism that  results in the closure not
                // being called anymore if the preference is updated more than
                // two times before rendering.
                // The safety mechanism prevents infinite loops that can happen
                // when updating a state in response to a preference change,
                // which then causes the preference to be updated again, but we
                // do not need that here.
                let _ = axtSubject.send($0.first)
                Color.clear
            }
    }
}


#endif


================================================
FILE: Sources/Axt/AxtTest/Publisher+Compatibility.swift
================================================
import Combine

#if TESTABLE

@available(iOS, deprecated: 15.0, message: "For iOS 14 compatibility")
extension Publisher {
    var values: AsyncThrowingStream<Output, Error> {
        AsyncThrowingStream { continuation in
            let cancellable = sink(
                receiveCompletion: { completion in
                    switch completion {
                    case .finished:
                        continuation.finish()
                    case let .failure(error):
                        continuation.finish(throwing: error)
                    }
                }, receiveValue: { value in
                    continuation.yield(value)
                }
            )
            continuation.onTermination = { @Sendable _ in
                cancellable.cancel()
            }
        }
    }
}

@available(iOS, deprecated: 15.0, message: "For iOS 14 compatibility")
public extension Publisher where Failure == Never {
    var values: AsyncStream<Output> {
        AsyncStream { continuation in
            let cancellable = sink(
                receiveCompletion: { _ in
                    continuation.finish()
                }, receiveValue: { value in
                    continuation.yield(value)
                }
            )
            continuation.onTermination = { @Sendable _ in
                cancellable.cancel()
            }
        }
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/Publisher+firstValue.swift
================================================
import Combine
import Foundation

#if TESTABLE

public struct TimeOut: Error {}

public extension Publisher where Failure == Never {
    /// Use only for testing
    /// Blocks the execution and returns the first value published by the
    /// publisher. If there's no value received during the `timeout` period,
    /// throws the `TimeOut` error.
    func firstValue(timeout: TimeInterval = 1) async throws -> Output {
        let value = await self.timeout(.seconds(timeout), scheduler: DispatchQueue.main)
            .values
            .first { _ in true }
        guard let value = value else { throw TimeOut() }
        return value
    }
}

#endif


================================================
FILE: Sources/Axt/AxtTest/printHierarchy.swift
================================================
import Foundation

#if TESTABLE

/// For debugging purposes
/// Prints the recursive hierarchy of a Swift structure using `Mirror(reflecting:)`
public func printHierarchy(level: Int = 0, object: Any) {
    let indent = Array(repeating: " ", count: level * 2).joined()
    let mirror = Mirror(reflecting: object)
    print(String(describing: type(of: object)), terminator: "")
    if mirror.children.isEmpty {
        print(" = " + String(describing: object), terminator: "")
    }
    print("")
    for property in mirror.children {
        print(indent + "→ " + (property.label ?? ""), terminator: ": ")
        printHierarchy(level: level + 1, object: property.value)
    }
}

#endif


================================================
FILE: Sources/Axt/AxtView.swift
================================================
import SwiftUI

#if TESTABLE

struct AxtView<Content: View>: View {
    let identifier: String?
    let label: String?
    let value: Any?
    let action: (() -> Void)?
    let setValue: ((Any?) -> Void)?
    let content: Content
    @State private var nodeId = UUID()
    @State private var visible = true

    var body: some View {
        content
            .transformPreference(AxtPreferenceKey.self) { value in
                if value.count == 1, let placeholder = value.first, placeholder.id == nil {
                    value = [Axt(id: identifier, nodeId: self.nodeId , label: self.label ?? placeholder.label, value: self.value ?? placeholder.value, action: self.action ?? placeholder.action, setValue: self.setValue ?? placeholder.setValue, children: placeholder.children, visible: self.visible)]
                } else {
                    value = [Axt(id: identifier, nodeId: self.nodeId, label: label, value: self.value, action: action, setValue: setValue, children: value, visible: self.visible)]
                }
            }
            .onAppear { visible = true }
            .onDisappear { visible = false }
    }
}

struct AxtInsertView<Content: View>: View {
    let identifier: String
    let condition: Bool
    let label: String?
    let value: Any?
    let action: (() -> Void)?
    let setValue: ((Any?) -> Void)?
    let content: Content
    @State private var nodeId = UUID()

    var body: some View {
        content
            .transformPreference(AxtPreferenceKey.self) { value in
                guard condition else { return }
                value.append(Axt(id: identifier, nodeId: self.nodeId, label: label, value: self.value, action: action, setValue: setValue, children: [], visible: true))
            }
    }
}

#endif


================================================
FILE: Sources/Axt/Modifier.swift
================================================
import SwiftUI

public protocol Modifier {
    associatedtype Content: View
    #if TESTABLE
    associatedtype Body: View
    func make(_ content: Content) -> Body
    #endif
}


================================================
FILE: Sources/Axt/Native/Button.swift
================================================
import SwiftUI

public struct ButtonModifier<Content: View>: Modifier {
    #if TESTABLE
    public func make(_ content: Content) -> some View {
        var action: (() -> Void)?
        let label = dig(for: String.self, in: content) ?? ""
        // Supresses a compiler warning
        typealias Void_ = Void
        // This type is used in `Button`
        if let buttonAction = dig(for: (() -> Void).self, in: content) {
            action = buttonAction
            // This type is used in tap gestures
        } else if let tapAction = dig(for: ((Void_) -> Void).self, in: content) {
            action = { tapAction(()) }
        }
        return content.testData(label: label, value: nil, action: action)
    }
    #endif
}

public extension NativeView {
    static var button: NativeView<Content, ButtonModifier<Content>> { .init(base: .init()) }
}


================================================
FILE: Sources/Axt/Native/NavigationLink.swift
================================================
import SwiftUI

public struct NavigationLinkModifier<Content: View>: Modifier {
    #if TESTABLE
    public func make(_ content: Content) -> some View {
        let makeContent: (State<Bool>) -> Content = { state in
            // This is modifying the navigation link directly and depends
            // on the fact that the isActive: State<Bool> parameter is the
            // first parameter in the NavigationLink structure.
            var link = content
            withUnsafeMutablePointer(to: &link) { pointer in
                pointer.withMemoryRebound(to: State<Bool>.self, capacity: 1) { statePointer in
                    statePointer.pointee = state
                }
            }
            return link
        }
        return AxtNavigationLink(isActive: dig(for: State<Bool>.self, in: content)!, content: makeContent)
    }
    #endif
}

#if TESTABLE

private struct AxtNavigationLink<Content: View>: View {
    // Because a State variable is only ready once the body is called by
    // SwiftUI, you cannot access State variables from the parent of a view,
    // and they need to be moved up the hierarchy.
    @State var isActive: Bool
    let content: (State<Bool>) -> Content

    init(isActive: State<Bool>, content: @escaping ((State<Bool>) -> Content)) {
        _isActive = isActive
        self.content = content
    }

    var body: some View {
        // Prevents SwiftUI from optimizing state updating away, so that it is
        // ready when used in the `activate` closure.
        // swiftformat:disable:next redundantLet
        let _ = isActive
        content(_isActive)
            .testData {
                isActive.toggle()
            }
    }
}

#endif

public extension NativeView {
    static var navigationLink: NativeView<Content, NavigationLinkModifier<Content>> { .init(base: .init()) }
}


================================================
FILE: Sources/Axt/Native/Text.swift
================================================
import SwiftUI

public struct TextModifier<Content: View>: Modifier {
    #if TESTABLE
    public func make(_ content: Content) -> some View {
        var label: String?
        if let text = dig(for: Text.self, in: content) {
            label = dig(for: String.self, in: text)
        }
        return content.testData(label: label)
    }
    #endif
}

public extension NativeView {
    static var text: NativeView<Content, TextModifier<Content>> { .init(base: .init()) }
}


================================================
FILE: Sources/Axt/Native/TextField.swift
================================================
import SwiftUI

public struct TextFieldModifier<Content: View>: Modifier {
    #if TESTABLE
    public func make(_ content: Content) -> some View {
        var label: String?
        if let text = dig(for: Text.self, in: content) {
            label = dig(for: String.self, in: text)
        }
        let _value = dig(for: Binding<String>.self, in: content)
        let value = _value?.wrappedValue
        return content.testData(label: label, value: value, setValue: { if let newValue = $0 as? String { _value?.wrappedValue = newValue } })
    }
    #endif
}

public extension NativeView {
    static var textField: NativeView<Content, TextFieldModifier<Content>> { .init(base: .init()) }
}


================================================
FILE: Sources/Axt/Native/Toggle.swift
================================================
import SwiftUI

public struct ToggleModifier<Content: View>: Modifier {
    #if TESTABLE
    public func make(_ content: Content) -> some View {
        let label = dig(for: String.self, in: content) ?? ""
        let isOn: Bool?
        let action: () -> Void
        if #available(iOS 16, *) {
            // Starting with iOS 16, the state of a toggle is no longer a Bool,
            // but an internal enum that can be in an on, off or mixed state.
            let anyToggleStateBinding = digForProperty(named: "_toggleState", in: content)
            let _toggleState = withUnsafePointer(to: anyToggleStateBinding) {
                $0.withMemoryRebound(to: Binding<ToggleState>.self, capacity: 1) {
                    $0.pointee
                }
            }
            switch _toggleState.wrappedValue {
            case .on: isOn = true
            case .off: isOn = false
            case .mixed: isOn = nil
            }
            action = {
                if _toggleState.wrappedValue == .off {
                    _toggleState.wrappedValue = .on
                } else if _toggleState.wrappedValue == .on {
                    _toggleState.wrappedValue = .off
                }
            }
        } else {
            let _isOn = dig(for: Binding<Bool>.self, in: content)
            isOn = _isOn?.wrappedValue
            action = { _isOn?.wrappedValue.toggle() }

        }
        return content.testData(label: label, value: isOn, action: { withAnimation { action() } })
    }
    #endif
}

public extension NativeView {
    static var toggle: NativeView<Content, ToggleModifier<Content>> { .init(base: .init()) }
}

private enum ToggleState {
    case on
    case off
    case mixed
}


================================================
FILE: Sources/Axt/NativeView.swift
================================================
import SwiftUI

public struct NativeView<Content: View, M: Modifier> {
    public let base: M
}


================================================
FILE: Sources/Axt/View+testData.swift
================================================
import SwiftUI

public extension View {
    @ViewBuilder func testData<M: Modifier>(type: NativeView<Self, M>, label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View where M.Content == Self {
        #if TESTABLE
        if AxtTest.enabled {
            AxtView(
                identifier: nil,
                label: label,
                value: value,
                action: action,
                setValue: setValue,
                content: type.base.make(self)
            )
        } else { self }
        #else
        self
        #endif
    }

    @ViewBuilder func testData(label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View {
        #if TESTABLE
        if AxtTest.enabled {
            AxtView(
                identifier: nil,
                label: label,
                value: value,
                action: action,
                setValue: setValue,
                content: self
            )
        } else { self }
        #else
        self
        #endif
    }

}


================================================
FILE: Sources/Axt/View+testId.swift
================================================
import SwiftUI

public extension View {
    @ViewBuilder func testId<M: Modifier>(_ identifier: String, type: NativeView<Self, M>, label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View where M.Content == Self {
        #if TESTABLE
        if AxtTest.enabled {
            AxtView(
                identifier: identifier,
                label: label,
                value: value,
                action: action,
                setValue: setValue,
                content: type.base.make(self)
            )
        } else { self }
        #else
        self
        #endif
    }

    @ViewBuilder func testId(_ identifier: String, label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View {
        #if TESTABLE
        if AxtTest.enabled {
            AxtView(
                identifier: identifier,
                label: label,
                value: value,
                action: action,
                setValue: setValue,
                content: self
            )
        } else { self }
        #else
        self
        #endif
    }

    @ViewBuilder func testId(insert identifier: String, when condition: Bool = true, label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View {
        #if TESTABLE
        if AxtTest.enabled {
            AxtInsertView(
                identifier: identifier,
                condition: condition,
                label: label,
                value: value,
                action: action,
                setValue: setValue,
                content: self
            )
        } else { self }
        #else
        self
        #endif
    }
}


================================================
FILE: Sources/Axt/dig.swift
================================================
import Foundation

#if TESTABLE

/// Recursively search for a property of a specific type in another object.
func dig<T>(for _: T.Type, in object: Any) -> T? {
    if let result = object as? T { return result }

    for child in Mirror(reflecting: object).children {
        if let result = dig(for: T.self, in: child.value) {
            return result
        }
    }

    return nil
}

/// Recursively search for a property with a specific name in another object.
func digForProperty(named name: String, in object: Any) -> Any? {
    for child in Mirror(reflecting: object).children {
        if child.label == name { return child.value }
        if let result = digForProperty(named: name, in: child.value) {
            return result
        }
    }

    return nil
}

#endif


================================================
FILE: Sources/Axt/hostAxtSheet.swift
================================================
import SwiftUI

public extension View {
    /// Use this modifier on the content **inside** of a sheet, so that the
    /// contents can be accessed using `AXTest.sheets`.
    func hostAxtSheet() -> some View {
        #if TESTABLE
        AxtSheet(content: self)
        #else
        self
        #endif
    }
}

#if TESTABLE

struct AxtSheet<Content: View>: UIViewControllerRepresentable {
    let content: Content

    public struct Coordinator {
        let id: UUID
    }

    public func makeUIViewController(context: Context) -> UIViewController {
        let test = AxtTest(content)
        AxtTest.sheets[context.coordinator.id] = test
        return test.hostingController
    }

    public func updateUIViewController(_: UIViewController, context: Context) {
        let test = AxtTest.sheets[context.coordinator.id]
        (test?.hostingController as? UIHostingController<Content>)?.rootView = content
    }

    public static func dismantleUIViewController(_: UIViewController, coordinator: Coordinator) {
        AxtTest.sheets.removeValue(forKey: coordinator.id)
    }

    public func makeCoordinator() -> Coordinator {
        Coordinator(id: UUID())
    }
}

#endif
Download .txt
gitextract_772q8wgy/

├── .gitignore
├── Examples/
│   └── AxtExamples/
│       ├── AxtExamples/
│       │   ├── Assets.xcassets/
│       │   │   ├── AccentColor.colorset/
│       │   │   │   └── Contents.json
│       │   │   ├── AppIcon.appiconset/
│       │   │   │   └── Contents.json
│       │   │   └── Contents.json
│       │   ├── AxtExamplesApp.swift
│       │   ├── ConfirmationAlertModifier.swift
│       │   ├── ContentView.swift
│       │   ├── CustomControls.swift
│       │   ├── GestureView.swift
│       │   ├── MoreButton.swift
│       │   ├── NativeViews.swift
│       │   ├── Preview Content/
│       │   │   └── Preview Assets.xcassets/
│       │   │       └── Contents.json
│       │   └── TogglesView.swift
│       ├── AxtExamples.xcodeproj/
│       │   ├── project.pbxproj
│       │   └── project.xcworkspace/
│       │       ├── contents.xcworkspacedata
│       │       └── xcshareddata/
│       │           └── IDEWorkspaceChecks.plist
│       └── AxtExamplesTests/
│           ├── ConfirmationAlertModifierTests.swift
│           ├── CustomControlsTests.swift
│           ├── GestureViewTests.swift
│           ├── MoreButtonTests.swift
│           ├── NativeViewsTests.swift
│           └── TogglesViewTests.swift
├── Package.swift
├── README.md
└── Sources/
    └── Axt/
        ├── Axt.swift
        ├── AxtTest/
        │   ├── Axt+description.swift
        │   ├── Axt+find.swift
        │   ├── AxtChildNode.swift
        │   ├── AxtElement.swift
        │   ├── AxtNode.swift
        │   ├── AxtTest.swift
        │   ├── Publisher+Compatibility.swift
        │   ├── Publisher+firstValue.swift
        │   └── printHierarchy.swift
        ├── AxtView.swift
        ├── Modifier.swift
        ├── Native/
        │   ├── Button.swift
        │   ├── NavigationLink.swift
        │   ├── Text.swift
        │   ├── TextField.swift
        │   └── Toggle.swift
        ├── NativeView.swift
        ├── View+testData.swift
        ├── View+testId.swift
        ├── dig.swift
        └── hostAxtSheet.swift
Condensed preview — 46 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (78K chars).
[
  {
    "path": ".gitignore",
    "chars": 165,
    "preview": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/config/registries.json\n.swiftpm/xcode/package"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/AccentColor.colorset/Contents.json",
    "chars": 123,
    "preview": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1509,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/AxtExamplesApp.swift",
    "chars": 144,
    "preview": "import SwiftUI\n\n@main\nstruct AxtExamplesApp: App {\n    var body: some Scene {\n        WindowGroup {\n            ContentV"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/ConfirmationAlertModifier.swift",
    "chars": 663,
    "preview": "import SwiftUI\n\nstruct ConfirmationAlertModifier: ViewModifier {\n    @Binding var isPresented: Bool\n\n    let message: St"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/ContentView.swift",
    "chars": 245,
    "preview": "import SwiftUI\n\nstruct ContentView: View {\n    var body: some View {\n        Text(\"Hello, world!\")\n            .padding("
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/CustomControls.swift",
    "chars": 396,
    "preview": "import SwiftUI\n\nstruct CustomControls: View {\n    @State var counter = 0\n\n    var body: some View {\n        MyButton() {"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/GestureView.swift",
    "chars": 918,
    "preview": "import SwiftUI\n\nstruct GestureView: View {\n    @State private var dragY: CGFloat = 0\n\n    var body: some View {\n        "
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/MoreButton.swift",
    "chars": 483,
    "preview": "import SwiftUI\nimport Axt\n\nstruct LessMenu: View {\n    @State private var isPresented = false\n\n    var body: some View {"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/NativeViews.swift",
    "chars": 635,
    "preview": "import SwiftUI\n\nstruct NativeViews: View {\n    @State var counter = 0\n    @State var name = \"\"\n\n    var body: some View "
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Preview Content/Preview Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/TogglesView.swift",
    "chars": 810,
    "preview": "import Foundation\nimport SwiftUI\nimport Axt\n\nstruct TogglesView: View {\n    @State var showMore = false\n\n    @State var "
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.pbxproj",
    "chars": 24791,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/ConfirmationAlertModifierTests.swift",
    "chars": 645,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass ConfirmationAlertModifierTests: X"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/CustomControlsTests.swift",
    "chars": 245,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass CustomControlsTests: XCTestCase {\n\n//    func te"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/GestureViewTests.swift",
    "chars": 624,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass GestureViewTests: XCTestCase {\n\n/"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/MoreButtonTests.swift",
    "chars": 433,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass MoreButtonTests: XCTestCase {\n\n  "
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/NativeViewsTests.swift",
    "chars": 239,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass NativeViewsTests: XCTestCase {\n\n//    func testW"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/TogglesViewTests.swift",
    "chars": 558,
    "preview": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass TogglesViewTests: XCTestCase {\n\n    func testWat"
  },
  {
    "path": "Package.swift",
    "chars": 428,
    "preview": "// swift-tools-version: 5.6\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"Axt\",\n    platforms: [.iOS(.v1"
  },
  {
    "path": "README.md",
    "chars": 10781,
    "preview": "# 🪓 Axt \n\n![](https://user-images.githubusercontent.com/13484323/185608030-21c45ddc-f90b-42e9-a8ac-e855bb090aea.svg)\n![]"
  },
  {
    "path": "Sources/Axt/Axt.swift",
    "chars": 1071,
    "preview": "import SwiftUI\n\npublic struct Axt: Equatable {\n    public static func == (lhs: Axt, rhs: Axt) -> Bool {\n        lhs._uui"
  },
  {
    "path": "Sources/Axt/AxtTest/Axt+description.swift",
    "chars": 997,
    "preview": "import Foundation\n\n#if TESTABLE\n\nextension Axt {\n    func describeHierarchy() -> String {\n        describeHierarchy(leve"
  },
  {
    "path": "Sources/Axt/AxtTest/Axt+find.swift",
    "chars": 519,
    "preview": "import Foundation\n\n#if TESTABLE\n\nextension Axt {\n    func find(where condition: (Axt) -> Bool) -> Axt? {\n        if cond"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtChildNode.swift",
    "chars": 392,
    "preview": "import Combine\nimport Foundation\n\n#if TESTABLE\n\nclass AxtChildNode: AxtNode {\n    let nodeId: UUID\n    let getRoot: () -"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtElement.swift",
    "chars": 952,
    "preview": "import Combine\nimport SwiftUI\n\n#if TESTABLE\n\n/// Automatically updated when the view it refers to changes\npublic protoco"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtNode.swift",
    "chars": 2320,
    "preview": "import Combine\nimport Foundation\n\n#if TESTABLE\n\nprotocol AxtNode: AxtElement {\n    var nodeId: UUID { get }\n    var getR"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtTest.swift",
    "chars": 2938,
    "preview": "import Combine\nimport SwiftUI\n\n#if TESTABLE\n\npublic final class AxtTest {\n    private var window: UIWindow!\n    public p"
  },
  {
    "path": "Sources/Axt/AxtTest/Publisher+Compatibility.swift",
    "chars": 1386,
    "preview": "import Combine\n\n#if TESTABLE\n\n@available(iOS, deprecated: 15.0, message: \"For iOS 14 compatibility\")\nextension Publisher"
  },
  {
    "path": "Sources/Axt/AxtTest/Publisher+firstValue.swift",
    "chars": 657,
    "preview": "import Combine\nimport Foundation\n\n#if TESTABLE\n\npublic struct TimeOut: Error {}\n\npublic extension Publisher where Failur"
  },
  {
    "path": "Sources/Axt/AxtTest/printHierarchy.swift",
    "chars": 686,
    "preview": "import Foundation\n\n#if TESTABLE\n\n/// For debugging purposes\n/// Prints the recursive hierarchy of a Swift structure usin"
  },
  {
    "path": "Sources/Axt/AxtView.swift",
    "chars": 1765,
    "preview": "import SwiftUI\n\n#if TESTABLE\n\nstruct AxtView<Content: View>: View {\n    let identifier: String?\n    let label: String?\n "
  },
  {
    "path": "Sources/Axt/Modifier.swift",
    "chars": 178,
    "preview": "import SwiftUI\n\npublic protocol Modifier {\n    associatedtype Content: View\n    #if TESTABLE\n    associatedtype Body: Vi"
  },
  {
    "path": "Sources/Axt/Native/Button.swift",
    "chars": 858,
    "preview": "import SwiftUI\n\npublic struct ButtonModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content:"
  },
  {
    "path": "Sources/Axt/Native/NavigationLink.swift",
    "chars": 1840,
    "preview": "import SwiftUI\n\npublic struct NavigationLinkModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ "
  },
  {
    "path": "Sources/Axt/Native/Text.swift",
    "chars": 476,
    "preview": "import SwiftUI\n\npublic struct TextModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: C"
  },
  {
    "path": "Sources/Axt/Native/TextField.swift",
    "chars": 694,
    "preview": "import SwiftUI\n\npublic struct TextFieldModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ conte"
  },
  {
    "path": "Sources/Axt/Native/Toggle.swift",
    "chars": 1713,
    "preview": "import SwiftUI\n\npublic struct ToggleModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content:"
  },
  {
    "path": "Sources/Axt/NativeView.swift",
    "chars": 96,
    "preview": "import SwiftUI\n\npublic struct NativeView<Content: View, M: Modifier> {\n    public let base: M\n}\n"
  },
  {
    "path": "Sources/Axt/View+testData.swift",
    "chars": 1122,
    "preview": "import SwiftUI\n\npublic extension View {\n    @ViewBuilder func testData<M: Modifier>(type: NativeView<Self, M>, label: St"
  },
  {
    "path": "Sources/Axt/View+testId.swift",
    "chars": 1780,
    "preview": "import SwiftUI\n\npublic extension View {\n    @ViewBuilder func testId<M: Modifier>(_ identifier: String, type: NativeView"
  },
  {
    "path": "Sources/Axt/dig.swift",
    "chars": 780,
    "preview": "import Foundation\n\n#if TESTABLE\n\n/// Recursively search for a property of a specific type in another object.\nfunc dig<T>"
  },
  {
    "path": "Sources/Axt/hostAxtSheet.swift",
    "chars": 1186,
    "preview": "import SwiftUI\n\npublic extension View {\n    /// Use this modifier on the content **inside** of a sheet, so that the\n    "
  }
]

About this extraction

This page contains the full source code of the soundcloud/Axt GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 46 files (68.1 KB), approximately 20.7k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!