[
  {
    "path": ".gitignore",
    "content": ".DS_Store\n/.build\n/Packages\n/*.xcodeproj\nxcuserdata/\nDerivedData/\n.swiftpm/config/registries.json\n.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata\n.netrc\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/AccentColor.colorset/Contents.json",
    "content": "{\n  \"colors\" : [\n    {\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"3x\",\n      \"size\" : \"60x60\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"29x29\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"1x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"40x40\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"76x76\"\n    },\n    {\n      \"idiom\" : \"ipad\",\n      \"scale\" : \"2x\",\n      \"size\" : \"83.5x83.5\"\n    },\n    {\n      \"idiom\" : \"ios-marketing\",\n      \"scale\" : \"1x\",\n      \"size\" : \"1024x1024\"\n    }\n  ],\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/AxtExamplesApp.swift",
    "content": "import SwiftUI\n\n@main\nstruct AxtExamplesApp: App {\n    var body: some Scene {\n        WindowGroup {\n            ContentView()\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/ConfirmationAlertModifier.swift",
    "content": "import SwiftUI\n\nstruct ConfirmationAlertModifier: ViewModifier {\n    @Binding var isPresented: Bool\n\n    let message: String\n    let action1: () -> Void\n    let action2: () -> Void\n\n    func body(content: Content) -> some View {\n        content.alert(isPresented: $isPresented) {\n            Alert(\n                title: Text(message),\n                primaryButton: .default(Text(\"1\"), action: action1),\n                secondaryButton: .default(Text(\"2\"), action: action2))\n        }\n        .testId(insert: \"button_1\", when: isPresented, label: \"1\", action: action1)\n        .testId(insert: \"button_2\", when: isPresented, label: \"2\", action: action2)\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/ContentView.swift",
    "content": "import SwiftUI\n\nstruct ContentView: View {\n    var body: some View {\n        Text(\"Hello, world!\")\n            .padding()\n    }\n}\n\nstruct ContentView_Previews: PreviewProvider {\n    static var previews: some View {\n        ContentView()\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/CustomControls.swift",
    "content": "import SwiftUI\n\nstruct CustomControls: View {\n    @State var counter = 0\n\n    var body: some View {\n        MyButton() { counter += 1 }\n            .testId(\"my_button\")\n            .testId(insert: \"counter\", value: counter)\n    }\n\n}\n\nstruct MyButton: View {\n    let action: () -> Void\n\n    var body: some View {\n        Button(\"Tap me\") { action() }\n            .testData(action: action)\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/GestureView.swift",
    "content": "import SwiftUI\n\nstruct GestureView: View {\n    @State private var dragY: CGFloat = 0\n\n    var body: some View {\n        knob\n            .testId(\"knob\")\n            .frame(width: 50, height: 50)\n            .offset(x: 0, y: dragY)\n            .gesture(gesture)\n            .testId(insert: \"drag\", value: dragY, setValue: { dragY = $0 as? CGFloat ?? 0 })\n    }\n\n    @ViewBuilder private var knob: some View {\n        if abs(dragY) > 300 {\n            Circle()\n                .testData(value: \"circle\")\n        }\n        else {\n            Rectangle()\n                .testData(value: \"rectangle\")\n        }\n    }\n\n    private var gesture: some Gesture {\n        DragGesture()\n            .onChanged { value in\n                dragY = value.translation.height\n            }\n            .onEnded { value in\n                withAnimation(.spring()) {\n                    dragY = 0\n                }\n            }\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/MoreButton.swift",
    "content": "import SwiftUI\nimport Axt\n\nstruct LessMenu: View {\n    @State private var isPresented = false\n\n    var body: some View {\n        Button(\"...\") { isPresented = true }\n            .testId(\"more_button\", type: .button)\n            .sheet(isPresented: $isPresented) {\n                MoreMenu()\n                    .hostAxtSheet()\n            }\n    }\n}\n\nstruct MoreMenu: View {\n    var body: some View {\n        Text(\"What's more?\")\n            .testId(\"more_text\", type: .text)\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/NativeViews.swift",
    "content": "import SwiftUI\n\nstruct NativeViews: View {\n    @State var counter = 0\n    @State var name = \"\"\n\n    var body: some View {\n        List {\n            let counterDescription = \"Counter: \\(counter)\"\n            Text(counterDescription)\n                .testId(\"counter_label\", type: .text)\n            Button(\"Tap\", action: { counter += 1 })\n                .testId(\"tap_button\", type: .button)\n            NavigationLink(\"More\", destination: Text(\"More...\"))\n                .testId(\"more_link\", type: .navigationLink)\n            TextField(\"Name\", text: $name)\n                .testId(\"name_field\", type: .textField)\n        }\n\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/Preview Content/Preview Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples/TogglesView.swift",
    "content": "import Foundation\nimport SwiftUI\nimport Axt\n\nstruct TogglesView: View {\n    @State var showMore = false\n\n    @State var value1 = false\n    @State var value2 = false\n    @State var value3 = false\n    @State var value4 = false\n\n    var body: some View {\n        List {\n            Toggle(\"1\", isOn: $value1)\n                .testId(\"toggle_1\", type: .toggle)\n            Toggle(\"Show more\", isOn: $showMore)\n                .testId(\"show_more\", type: .toggle)\n            if showMore {\n                Toggle(\"2\", isOn: $value2)\n                    .testId(\"toggle_2\", type: .toggle)\n                Toggle(\"3\", isOn: $value3)\n                    .testId(\"toggle_3\", type: .toggle)\n                Toggle(\"4\", isOn: $value4)\n                    .testId(\"toggle_4\", type: .toggle)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 55;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t2E45AE70287577060042F247 /* NativeViews.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45AE6F287577060042F247 /* NativeViews.swift */; };\n\t\t2E45AE72287577BF0042F247 /* NativeViewsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E45AE71287577BF0042F247 /* NativeViewsTests.swift */; };\n\t\t2E8DFD092874755F0030D715 /* AxtExamplesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */; };\n\t\t2E8DFD0B2874755F0030D715 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0A2874755F0030D715 /* ContentView.swift */; };\n\t\t2E8DFD0D287475600030D715 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0C287475600030D715 /* Assets.xcassets */; };\n\t\t2E8DFD10287475600030D715 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2E8DFD0F287475600030D715 /* Preview Assets.xcassets */; };\n\t\t2E8DFD1A287475600030D715 /* TogglesViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD19287475600030D715 /* TogglesViewTests.swift */; };\n\t\t2E8DFD332874758B0030D715 /* TogglesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2E8DFD322874758B0030D715 /* TogglesView.swift */; };\n\t\t2E8DFD39287476A90030D715 /* Axt in Frameworks */ = {isa = PBXBuildFile; productRef = 2E8DFD38287476A90030D715 /* Axt */; };\n\t\t2E8DFD3D287476BA0030D715 /* Axt in Frameworks */ = {isa = PBXBuildFile; productRef = 2E8DFD3C287476BA0030D715 /* Axt */; };\n\t\t2EB4C36B28758F520026D3C0 /* CustomControls.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36A28758F520026D3C0 /* CustomControls.swift */; };\n\t\t2EB4C36D287593220026D3C0 /* CustomControlsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */; };\n\t\t2EB4C36F287593FD0026D3C0 /* ConfirmationAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */; };\n\t\t2EB4C3712875959D0026D3C0 /* ConfirmationAlertModifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */; };\n\t\t2EB4C3732875A2830026D3C0 /* GestureView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3722875A2830026D3C0 /* GestureView.swift */; };\n\t\t2EB4C3752875A4D40026D3C0 /* GestureViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */; };\n\t\t2EB4C3772875A8500026D3C0 /* MoreButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3762875A8500026D3C0 /* MoreButton.swift */; };\n\t\t2EB4C3792875A8F90026D3C0 /* MoreButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t2E8DFD16287475600030D715 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 2E8DFCFD2874755F0030D715 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 2E8DFD042874755F0030D715;\n\t\t\tremoteInfo = AxtExamples;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXFileReference section */\n\t\t2E45AE6F287577060042F247 /* NativeViews.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeViews.swift; sourceTree = \"<group>\"; };\n\t\t2E45AE71287577BF0042F247 /* NativeViewsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeViewsTests.swift; sourceTree = \"<group>\"; };\n\t\t2E8DFD052874755F0030D715 /* AxtExamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AxtExamples.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AxtExamplesApp.swift; sourceTree = \"<group>\"; };\n\t\t2E8DFD0A2874755F0030D715 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = \"<group>\"; };\n\t\t2E8DFD0C287475600030D715 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t2E8DFD0F287475600030D715 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = \"Preview Assets.xcassets\"; sourceTree = \"<group>\"; };\n\t\t2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AxtExamplesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t2E8DFD19287475600030D715 /* TogglesViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TogglesViewTests.swift; sourceTree = \"<group>\"; };\n\t\t2E8DFD322874758B0030D715 /* TogglesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TogglesView.swift; sourceTree = \"<group>\"; };\n\t\t2E8DFD34287476260030D715 /* Axt */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Axt; path = ../..; sourceTree = \"<group>\"; };\n\t\t2EB4C36A28758F520026D3C0 /* CustomControls.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomControls.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomControlsTests.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertModifier.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertModifierTests.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C3722875A2830026D3C0 /* GestureView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureView.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GestureViewTests.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C3762875A8500026D3C0 /* MoreButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreButton.swift; sourceTree = \"<group>\"; };\n\t\t2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoreButtonTests.swift; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t2E8DFD022874755F0030D715 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2E8DFD39287476A90030D715 /* Axt in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2E8DFD12287475600030D715 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2E8DFD3D287476BA0030D715 /* Axt in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t2E8DFCFC2874755F0030D715 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2E8DFD34287476260030D715 /* Axt */,\n\t\t\t\t2E8DFD072874755F0030D715 /* AxtExamples */,\n\t\t\t\t2E8DFD18287475600030D715 /* AxtExamplesTests */,\n\t\t\t\t2E8DFD062874755F0030D715 /* Products */,\n\t\t\t\t2E8DFD37287476A90030D715 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2E8DFD062874755F0030D715 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2E8DFD052874755F0030D715 /* AxtExamples.app */,\n\t\t\t\t2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2E8DFD072874755F0030D715 /* AxtExamples */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2E8DFD082874755F0030D715 /* AxtExamplesApp.swift */,\n\t\t\t\t2E8DFD0A2874755F0030D715 /* ContentView.swift */,\n\t\t\t\t2E8DFD322874758B0030D715 /* TogglesView.swift */,\n\t\t\t\t2E45AE6F287577060042F247 /* NativeViews.swift */,\n\t\t\t\t2EB4C36A28758F520026D3C0 /* CustomControls.swift */,\n\t\t\t\t2EB4C36E287593FD0026D3C0 /* ConfirmationAlertModifier.swift */,\n\t\t\t\t2EB4C3722875A2830026D3C0 /* GestureView.swift */,\n\t\t\t\t2EB4C3762875A8500026D3C0 /* MoreButton.swift */,\n\t\t\t\t2E8DFD0C287475600030D715 /* Assets.xcassets */,\n\t\t\t\t2E8DFD0E287475600030D715 /* Preview Content */,\n\t\t\t);\n\t\t\tpath = AxtExamples;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2E8DFD0E287475600030D715 /* Preview Content */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2E8DFD0F287475600030D715 /* Preview Assets.xcassets */,\n\t\t\t);\n\t\t\tpath = \"Preview Content\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2E8DFD18287475600030D715 /* AxtExamplesTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2E8DFD19287475600030D715 /* TogglesViewTests.swift */,\n\t\t\t\t2E45AE71287577BF0042F247 /* NativeViewsTests.swift */,\n\t\t\t\t2EB4C36C287593220026D3C0 /* CustomControlsTests.swift */,\n\t\t\t\t2EB4C3702875959D0026D3C0 /* ConfirmationAlertModifierTests.swift */,\n\t\t\t\t2EB4C3742875A4D40026D3C0 /* GestureViewTests.swift */,\n\t\t\t\t2EB4C3782875A8F90026D3C0 /* MoreButtonTests.swift */,\n\t\t\t);\n\t\t\tpath = AxtExamplesTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t2E8DFD37287476A90030D715 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t2E8DFD042874755F0030D715 /* AxtExamples */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2E8DFD29287475600030D715 /* Build configuration list for PBXNativeTarget \"AxtExamples\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2E8DFD012874755F0030D715 /* Sources */,\n\t\t\t\t2E8DFD022874755F0030D715 /* Frameworks */,\n\t\t\t\t2E8DFD032874755F0030D715 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t2E8DFD36287476A50030D715 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = AxtExamples;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t2E8DFD38287476A90030D715 /* Axt */,\n\t\t\t);\n\t\t\tproductName = AxtExamples;\n\t\t\tproductReference = 2E8DFD052874755F0030D715 /* AxtExamples.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n\t\t2E8DFD14287475600030D715 /* AxtExamplesTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 2E8DFD2C287475600030D715 /* Build configuration list for PBXNativeTarget \"AxtExamplesTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t2E8DFD11287475600030D715 /* Sources */,\n\t\t\t\t2E8DFD12287475600030D715 /* Frameworks */,\n\t\t\t\t2E8DFD13287475600030D715 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t2E8DFD3B287476B40030D715 /* PBXTargetDependency */,\n\t\t\t\t2E8DFD17287475600030D715 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = AxtExamplesTests;\n\t\t\tpackageProductDependencies = (\n\t\t\t\t2E8DFD3C287476BA0030D715 /* Axt */,\n\t\t\t);\n\t\t\tproductName = AxtExamplesTests;\n\t\t\tproductReference = 2E8DFD15287475600030D715 /* AxtExamplesTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t2E8DFCFD2874755F0030D715 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = 1;\n\t\t\t\tLastSwiftUpdateCheck = 1330;\n\t\t\t\tLastUpgradeCheck = 1330;\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t2E8DFD042874755F0030D715 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.3.1;\n\t\t\t\t\t};\n\t\t\t\t\t2E8DFD14287475600030D715 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 13.3.1;\n\t\t\t\t\t\tTestTargetID = 2E8DFD042874755F0030D715;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 2E8DFD002874755F0030D715 /* Build configuration list for PBXProject \"AxtExamples\" */;\n\t\t\tcompatibilityVersion = \"Xcode 13.0\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 2E8DFCFC2874755F0030D715;\n\t\t\tproductRefGroup = 2E8DFD062874755F0030D715 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t2E8DFD042874755F0030D715 /* AxtExamples */,\n\t\t\t\t2E8DFD14287475600030D715 /* AxtExamplesTests */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t2E8DFD032874755F0030D715 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2E8DFD10287475600030D715 /* Preview Assets.xcassets in Resources */,\n\t\t\t\t2E8DFD0D287475600030D715 /* Assets.xcassets in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2E8DFD13287475600030D715 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t2E8DFD012874755F0030D715 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2E8DFD0B2874755F0030D715 /* ContentView.swift in Sources */,\n\t\t\t\t2EB4C36F287593FD0026D3C0 /* ConfirmationAlertModifier.swift in Sources */,\n\t\t\t\t2EB4C3772875A8500026D3C0 /* MoreButton.swift in Sources */,\n\t\t\t\t2E45AE70287577060042F247 /* NativeViews.swift in Sources */,\n\t\t\t\t2EB4C3732875A2830026D3C0 /* GestureView.swift in Sources */,\n\t\t\t\t2EB4C36B28758F520026D3C0 /* CustomControls.swift in Sources */,\n\t\t\t\t2E8DFD332874758B0030D715 /* TogglesView.swift in Sources */,\n\t\t\t\t2E8DFD092874755F0030D715 /* AxtExamplesApp.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t2E8DFD11287475600030D715 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t2E8DFD1A287475600030D715 /* TogglesViewTests.swift in Sources */,\n\t\t\t\t2EB4C36D287593220026D3C0 /* CustomControlsTests.swift in Sources */,\n\t\t\t\t2EB4C3792875A8F90026D3C0 /* MoreButtonTests.swift in Sources */,\n\t\t\t\t2EB4C3712875959D0026D3C0 /* ConfirmationAlertModifierTests.swift in Sources */,\n\t\t\t\t2EB4C3752875A4D40026D3C0 /* GestureViewTests.swift in Sources */,\n\t\t\t\t2E45AE72287577BF0042F247 /* NativeViewsTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t2E8DFD17287475600030D715 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 2E8DFD042874755F0030D715 /* AxtExamples */;\n\t\t\ttargetProxy = 2E8DFD16287475600030D715 /* PBXContainerItemProxy */;\n\t\t};\n\t\t2E8DFD36287476A50030D715 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tproductRef = 2E8DFD35287476A50030D715 /* Axt */;\n\t\t};\n\t\t2E8DFD3B287476B40030D715 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\tproductRef = 2E8DFD3A287476B40030D715 /* Axt */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin XCBuildConfiguration section */\n\t\t2E8DFD27287475600030D715 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2E8DFD28287475600030D715 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++17\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_WEAK = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.4;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tMTL_FAST_MATH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2E8DFD2A287475600030D715 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"AxtExamples/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = JFV288DQ9Q;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchScreen_Generation = YES;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamples;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2E8DFD2B287475600030D715 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_ASSET_PATHS = \"\\\"AxtExamples/Preview Content\\\"\";\n\t\t\t\tDEVELOPMENT_TEAM = JFV288DQ9Q;\n\t\t\t\tENABLE_PREVIEWS = YES;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;\n\t\t\t\tINFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;\n\t\t\t\tINFOPLIST_KEY_UILaunchScreen_Generation = YES;\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = \"UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tINFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = \"UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight\";\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamples;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = YES;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t2E8DFD2D287475600030D715 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = JFV288DQ9Q;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.4;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamplesTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/AxtExamples.app/AxtExamples\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t2E8DFD2E287475600030D715 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tDEVELOPMENT_TEAM = JFV288DQ9Q;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 15.4;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.soundcloud.AxtExamplesTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_EMIT_LOC_STRINGS = NO;\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/AxtExamples.app/AxtExamples\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t2E8DFD002874755F0030D715 /* Build configuration list for PBXProject \"AxtExamples\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2E8DFD27287475600030D715 /* Debug */,\n\t\t\t\t2E8DFD28287475600030D715 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2E8DFD29287475600030D715 /* Build configuration list for PBXNativeTarget \"AxtExamples\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2E8DFD2A287475600030D715 /* Debug */,\n\t\t\t\t2E8DFD2B287475600030D715 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t2E8DFD2C287475600030D715 /* Build configuration list for PBXNativeTarget \"AxtExamplesTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t2E8DFD2D287475600030D715 /* Debug */,\n\t\t\t\t2E8DFD2E287475600030D715 /* Release */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\t2E8DFD35287476A50030D715 /* Axt */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Axt;\n\t\t};\n\t\t2E8DFD38287476A90030D715 /* Axt */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Axt;\n\t\t};\n\t\t2E8DFD3A287476B40030D715 /* Axt */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Axt;\n\t\t};\n\t\t2E8DFD3C287476BA0030D715 /* Axt */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tproductName = Axt;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 2E8DFCFD2874755F0030D715 /* Project object */;\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamples.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/ConfirmationAlertModifierTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass ConfirmationAlertModifierTests: XCTestCase {\n\n//    func testWatch() async {\n//        struct MyView: View {\n//            @State var alertPresented = false\n//            var body: some View {\n//                Button(\"Present alert\") { alertPresented.toggle() }\n//                    .modifier(ConfirmationAlertModifier(isPresented: $alertPresented, message: \"Are you sure?\", action1: { print(\"yes\") }, action2: { print(\"no\") }))\n//            }\n//        }\n//        let test = await AxtTest.host(MyView())\n//        await test.watchHierarchy()\n//    }\n\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/CustomControlsTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass CustomControlsTests: XCTestCase {\n\n//    func testWatch() async {\n//        let test = await AxtTest.host(CustomControls())\n//        await test.watchHierarchy()\n//    }\n\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/GestureViewTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass GestureViewTests: XCTestCase {\n\n//    func testWatch() async {\n//        let test = await AxtTest.host(GestureView())\n//\n//        await test.watchHierarchy()\n//    }\n\n    func testKnob() async throws {\n        let test = await AxtTest.host(GestureView())\n        let knob = try XCTUnwrap(test.find(id: \"knob\"))\n        let dragValue = try XCTUnwrap(test.find(id: \"drag\"))\n\n        let drag: CGFloat = 500.0\n        dragValue.setValue(drag)\n        await AxtTest.yield()\n\n        XCTAssertEqual(knob.value as? String, \"circle\")\n    }\n\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/MoreButtonTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\nimport SwiftUI\n\n@MainActor\nclass MoreButtonTests: XCTestCase {\n\n    func test_sheet() async throws {\n        let test = await AxtTest.host(LessMenu())\n        let button = try XCTUnwrap(test.find(id: \"more_button\"))\n\n        await button.performAction()\n\n        let sheet = try XCTUnwrap(AxtTest.sheets.first?.value)\n        XCTAssertNotNil(sheet.find(id: \"more_text\"))\n    }\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/NativeViewsTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass NativeViewsTests: XCTestCase {\n\n//    func testWatch() async {\n//        let test = await AxtTest.host(NativeViews())\n//        await test.watchHierarchy()\n//    }\n\n}\n"
  },
  {
    "path": "Examples/AxtExamples/AxtExamplesTests/TogglesViewTests.swift",
    "content": "import XCTest\n@testable import AxtExamples\nimport Axt\n\n@MainActor\nclass TogglesViewTests: XCTestCase {\n\n    func testWatch() async {\n        let test = await AxtTest.host(TogglesView())\n        await test.watchHierarchy()\n    }\n\n    func testShowMore() async throws {\n        let test = await AxtTest.host(TogglesView())\n        let moreToggle = try XCTUnwrap(test.find(id: \"show_more\"))\n\n        moreToggle.performActionWithoutYielding()\n\n        try await test.waitForCondition(timeout: 1) {\n            test.find(id: \"toggle_2\") != nil\n        }\n    }\n\n}\n"
  },
  {
    "path": "Package.swift",
    "content": "// swift-tools-version: 5.6\n\nimport PackageDescription\n\nlet package = Package(\n    name: \"Axt\",\n    platforms: [.iOS(.v14)],\n    products: [\n        .library(\n            name: \"Axt\",\n            targets: [\"Axt\"]),\n    ],\n    targets: [\n        .target(\n            name: \"Axt\",\n            dependencies: [],\n            swiftSettings: [\n                .define(\"TESTABLE\", .when(configuration: .debug))\n            ]),\n    ]\n)\n"
  },
  {
    "path": "README.md",
    "content": "# 🪓 Axt \n\n![](https://user-images.githubusercontent.com/13484323/185608030-21c45ddc-f90b-42e9-a8ac-e855bb090aea.svg)\n![](https://user-images.githubusercontent.com/13484323/185608132-f90bd70e-4518-404d-9ba7-c24739f7c2b2.svg)\n![](https://user-images.githubusercontent.com/13484323/200010051-3270dd90-1edd-42ff-b94f-cb8ba3618a4e.svg)\n\nAxt is a testing library for SwiftUI.\n\nUnit tests using Axt can interact with SwiftUI views, which are running live in the simulator and are in a fully functional state.\n\n```swift\nstruct MyView: View {\n    @State var showMore = false\n\n    var body: some View {\n        VStack {\n            Toggle(\"Show more\", isOn: $showMore)\n                .testId(\"show_more_toggle\", type: .toggle)\n            if showMore {\n                Text(\"More\")\n                    .testId(\"more_text\", type: .text)\n            }\n        }\n    }\n}\n```\n\n```swift\n@MainActor\nclass MyViewTests: XCTestCase {\n    func testShowMore() async throws {\n        let test = await AxtTest.host(MyView())\n        let showMoreToggle = test.find(id: \"show_more_toggle\")\n\n        await showMoreToggle?.performAction()\n\n        XCTAssertEqual(showMoreToggle?.value as? Bool, true)\n        XCTAssertEqual(test.find(id: \"more_text\")?.label, \"More\")\n    }\n}\n```\n\n## Getting started\n\nFollow 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.\n\n1. Add the Axt Swift package as a dependency to your Xcode project.\n2. 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.\n3. 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.\n\n## Documentation \n\n### Exposing views\n\nTo expose a view, you give it an identifier with the `testId` modifier.\n\nTake this list of toggles, and notice the `toggle_1`, `show_more` and `toggle_2` identifiers.\n\n```swift\nList {\n    Toggle(\"1\", isOn: $value1)\n        .testId(\"toggle_1\", type: .toggle)\n    Toggle(\"Show more\", isOn: $showMore)\n        .testId(\"show_more\", type: .toggle)\n    if showMore {\n        Toggle(\"2\", isOn: $value2)\n            .testId(\"toggle_2\", type: .toggle)\n    }\n}\n.testId(\"toggle_list\")\n```\n\nThis will be exposed to the tests as below.\n\n```\n→ app\n  → toggle_list\n    → toggle_1 label=\"1\" value=false action\n    → show_more label=\"Show more\" value=false action\n```\n\nThere 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.\n\n#### Native views\n\nTo 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.\n\n##### Button\n\n```swift\nButton(\"Tap me\") { tap() }\n    .testId(\"tap_button\", type: .button)\n```\n\n```\n→ tap_button label=\"Tap me\" action\n```\n\n##### Toggle\n\n```swift\nToggle(\"Toggle me\", isOn: $isOn)\n    .testId(\"is_on_toggle\", type: .toggle)\n```\n\n```\n→ is_on_toggle label=\"Toggle me\" value=true action\n```\n\n##### NavigationLink\n\n```swift\nNavigationLink(\"More\", destination: Destination())\n    .testId(\"more_link\", type: .navigationLink)\n```\n\n```\n→ more_link label=\"More\" action\n```\n\n##### TextField\n\n```swift\nTextField(\"Name\", text: $name)\n    .testId(\"name_field\", type: .textField)\n```\n\n```\n→ name_field label=\"Name\" value=\"\" action\n```\n\n#### Custom views\n\nFor custom views, you can specify values or functionality manually to expose them to views.\n\n```swift\nColor.blue.frame(width: 50, height: 50)\n    .testId(\"color_1\", value: \"blue\")\nColor.red.frame(width: 50, height: 50)\n    .testId(\"color_2\", value: \"red\")\n```\n\nThese can now be accessed from tests.\n\n```\n→ app\n  → color_1 value=blue\n  → color_2 value=red\n```\n\nYou can also add closures to perform from tests (using the `action` parameter) or a way to set a value (using the `setValue` parameter).\n\n#### Re-usable controls\n\nIt 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.\n\n```swift\nstruct MyButton: View {\n    let action: () -> Void\n\n    var body: some View {\n        Button(\"Tap me!\") { action() }\n            .testData(action: action)\n    }\n}\n\nMyButton(action: action)\n    .testId(\"my_button\")\n```\n\nThere will only be a single element for this button exposed to the tests.\n\n```\n→ app\n  → my_button action\n```\n\nUsing the `testData` modifier only results in an element exposed to tests, if an identifier is provided somewhere higher up in the view hierarchy.\n\nDo not use the `testId(:type:)` modifiers for native views on custom controls. For custom controls, extracting data from views is not necessary.\n\n#### Inserting extra elements\n\nSometimes 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.\n\nFor example, here is how we can expose the contents of an alert.\n\n```swift\ncontent.alert(isPresented: $isPresented) {\n    Alert(\n        title: Text(message),\n        primaryButton: .default(Text(\"1\"), action: action1),\n        secondaryButton: .default(Text(\"2\"), action: action2))\n}\n.testId(insert: \"button_1\", when: isPresented, label: \"1\", action: action1)\n.testId(insert: \"button_2\", when: isPresented, label: \"2\", action: action2)\n```\n\nThe elements will be exposed as siblings.\n\n```\n→ app\n  → button_1 label=\"1\" action\n  → button_2 label=\"2\" action\n```\n\nAnd here we expose a drag gesture to be testable.\n\n```swift\n@State private var dragY: CGFloat = 0\n\nvar body: some View {\n    knob\n        .frame(width: 50, height: 50)\n        .offset(x: 0, y: dragY)\n        .gesture(gesture)\n        .testId(insert: \"drag\", value: dragY, setValue: { dragY = $0 as? CGFloat ?? 0 })\n}\n```\n\n```\n→ app\n  → drag value=0.0\n```\n\n#### Sheets\n\nPreferences 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.\n\n```swift\nButton(\"...\") { isPresented = true }\n    .sheet(isPresented: $isPresented) {\n        MoreMenu()\n            .hostAxtSheet()\n    }\n```\n\n### Writing tests\n\nThe first step to writing an Axt test is to create an asynchronous test method, and to host an Axt test with the view.\n\n```swift\nfunc test_myView() async {\n  let test = await AxtTest.host(MyView())\n  // ...\n```\n\nIn 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.\n\n#### Watch the hierarchy\n\nAs a first step, we can watch view updates in the console.\n\n```swift\nawait test.watchHierarchy()\n```\n\nRunning 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.\n\n#### Finding views\n\nThe `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.\n\nYou 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.\n\n```swift\nlet myButton = try XCTUnwrap(test.find(id: \"my_button\"))\n```\n\nYou 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.\n\n#### Assert on elements\n\nYou 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`).\n\nFor 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.\n\n#### The lifetime of Axt elements\n\nAn 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.\n\n```swift\nlet test = await AxtTest.host(MyView())\nlet label = try XCTUnwrap(test.find(id: \"my_label\")\nlet toggle = try XCTUnwrap(test.find(id: \"my_toggle\"))\n\nXCTAssertEqual(label.value as? String, \"yes\")\n\nawait toggle.performAction()\n\nXCTAssertEqual(label.value as? String, \"no\")\n```\n\n#### Waiting for view updates\n\nIf 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.\n\nIf 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()`.\n\n```swift\nlet test = await AxtTest.host(TogglesView())\nlet moreToggle = try XCTUnwrap(test.find(id: \"show_more\"))\n\nmoreToggle.performActionWithoutYielding()\nawait AxtTest.yield()\n\nXCTAssertNotNil(test.find(id: \"toggle_2\"))\n```\n\nIf 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.\n\n```swift\nlet test = await AxtTest.host(TogglesView())\nlet moreToggle = try XCTUnwrap(test.find(id: \"show_more\"))\n\nawait moreToggle.performAction()\n\nXCTAssertNotNil(try await test.waitForElement(id: \"toggle_2\", timeout: 1))\n```\n\nThere is also `waitForCondition` to wait for any boolean condition, and `waitForUpdate` that returns as soon as anything in the view hierarchy is changed.\n\n"
  },
  {
    "path": "Sources/Axt/Axt.swift",
    "content": "import SwiftUI\n\npublic struct Axt: Equatable {\n    public static func == (lhs: Axt, rhs: Axt) -> Bool {\n        lhs._uuid == rhs._uuid\n    }\n\n    /// Changes every time the Axt is re-evaluated\n    public let _uuid = UUID()\n\n    /// User-defined identifier\n    public let id: String?\n\n    /// Unique, does not change when Axt is re-evaluated\n    public let nodeId: UUID\n\n    public let label: String?\n    public let value: Any?\n    public let action: (() -> Void)?\n    public let setValue: ((Any?) -> Void)?\n    public let children: [Axt]\n    public let visible: Bool\n}\n\npublic struct AxtPreferenceKey: PreferenceKey {\n    public static var defaultValue: [Axt] = []\n\n    public static func reduce(value: inout [Axt], nextValue: () -> [Axt]) {\n        let axts = nextValue()\n        for axt in axts {\n            // In iOS 16, List has a bug where preferences can appear\n            // more than once, so we need to check for duplicates.\n            if !value.contains(where: { $0.nodeId == axt.nodeId }) {\n                value.append(axt)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sources/Axt/AxtTest/Axt+description.swift",
    "content": "import Foundation\n\n#if TESTABLE\n\nextension Axt {\n    func describeHierarchy() -> String {\n        describeHierarchy(level: 0)\n    }\n\n    private func describeHierarchy(level: Int = 0) -> String {\n        var description = \"\"\n        let indent = Array(repeating: \" \", count: level * 2).joined()\n        description += indent + \"→ \" + describeNode() + \"\\n\"\n        for child in children {\n            description += child.describeHierarchy(level: level + 1)\n        }\n        return description\n    }\n\n    private func describeNode() -> String {\n        var description = id ?? \"\"\n        if let label = self.label {\n            description.append(\" label=\\\"\" + label + \"\\\"\")\n        }\n        if let value = self.value {\n            description.append(\" value=\" + String(describing: value))\n        }\n        if action != nil {\n            description.append(\" action\")\n        }\n        if !visible {\n            description.append(\" hidden\")\n        }\n        return description\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/Axt+find.swift",
    "content": "import Foundation\n\n#if TESTABLE\n\nextension Axt {\n    func find(where condition: (Axt) -> Bool) -> Axt? {\n        if condition(self) { return self }\n        for child in children {\n            if let match = child.find(where: condition) {\n                return match\n            }\n        }\n        return nil\n    }\n\n    var all: [Axt] {\n        var all: [Axt] = []\n        for child in children {\n            all.append(child)\n            all.append(contentsOf: child.all)\n        }\n        return all\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtChildNode.swift",
    "content": "import Combine\nimport Foundation\n\n#if TESTABLE\n\nclass AxtChildNode: AxtNode {\n    let nodeId: UUID\n    let getRoot: () -> Axt\n    let rootDidChange: AnyPublisher<Void, Never>\n\n    init(nodeId: UUID, getRoot: @escaping () -> Axt, rootDidChange: AnyPublisher<Void, Never>) {\n        self.nodeId = nodeId\n        self.getRoot = getRoot\n        self.rootDidChange = rootDidChange\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtElement.swift",
    "content": "import Combine\nimport SwiftUI\n\n#if TESTABLE\n\n/// Automatically updated when the view it refers to changes\npublic protocol AxtElement {\n    var exists: Bool { get }\n    var id: String! { get }\n    var label: String? { get }\n    var value: Any? { get }\n\n    var children: [AxtElement] { get }\n    var all: [AxtElement] { get }\n    /// Recursively search for an element\n    func find(id: String) -> AxtElement?\n    func findAll(id: String) -> [AxtElement]\n\n    func performActionWithoutYielding()\n    func setValue(_ value: Any?)\n\n    func waitForCondition(timeout: TimeInterval, condition: @escaping () -> Bool) async throws\n    func waitForElement(id: String, timeout: TimeInterval) async throws -> AxtElement\n    func waitForUpdate(timeout: TimeInterval) async throws\n\n    func watchHierarchy() async\n}\n\npublic extension AxtElement {\n    func performAction() async {\n        performActionWithoutYielding()\n        await AxtTest.yield()\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtNode.swift",
    "content": "import Combine\nimport Foundation\n\n#if TESTABLE\n\nprotocol AxtNode: AxtElement {\n    var nodeId: UUID { get }\n    var getRoot: () -> Axt { get }\n    var rootDidChange: AnyPublisher<Void, Never> { get }\n}\n\nextension AxtNode {\n    var axt: Axt! {\n        getRoot().find(where: { $0.nodeId == self.nodeId })\n    }\n\n    func makeNode(nodeId: UUID) -> AxtChildNode {\n        AxtChildNode(nodeId: nodeId, getRoot: getRoot, rootDidChange: rootDidChange)\n    }\n\n    public var exists: Bool { axt != nil }\n\n    public var id: String! { axt?.id }\n\n    public var label: String? { axt?.label }\n\n    public var value: Any? { axt?.value }\n\n    public var children: [AxtElement] {\n        axt?.children.map { makeNode(nodeId: $0.nodeId) } ?? []\n    }\n\n    public var all: [AxtElement] {\n        axt?.all.map { makeNode(nodeId: $0.nodeId) } ?? []\n    }\n\n    public func find(id: String) -> AxtElement? {\n        if let axt = axt?.find(where: { $0.id == id }) {\n            return makeNode(nodeId: axt.nodeId)\n        }\n        return nil\n    }\n\n    public func findAll(id: String) -> [AxtElement] {\n        (axt?.all ?? []).filter { $0.id == id }.map { makeNode(nodeId: $0.nodeId) }\n    }\n\n    public func performActionWithoutYielding() {\n        axt?.action?()\n    }\n\n    public func setValue(_ value: Any?) {\n        axt?.setValue?(value)\n    }\n\n    public func waitForCondition(timeout: TimeInterval, condition: @escaping () -> Bool) async throws {\n        try await rootDidChange.filter { condition() }.firstValue(timeout: timeout)\n    }\n\n    public func waitForElement(id: String, timeout: TimeInterval) async throws -> AxtElement {\n        let axt = try await rootDidChange.map { self.axt }\n            .compactMap { axt in axt.find(where: { child in child.id == id }) }\n            .firstValue(timeout: timeout)\n        return makeNode(nodeId: axt.nodeId)\n    }\n\n    public func waitForUpdate(timeout: TimeInterval) async throws {\n        _ = try await rootDidChange.map { self.axt }\n            .removeDuplicates()\n            .firstValue(timeout: timeout)\n    }\n\n    public func watchHierarchy() async {\n        for await description in rootDidChange.compactMap({ self.axt?.describeHierarchy() }).prepend([axt.describeHierarchy()]).values {\n            print(\"────────\")\n            print(description)\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/AxtTest.swift",
    "content": "import Combine\nimport SwiftUI\n\n#if TESTABLE\n\npublic final class AxtTest {\n    private var window: UIWindow!\n    public private(set) var hostingController: UIViewController!\n\n    public internal(set) static var sheets: [UUID: AxtTest] = [:]\n    public internal(set) static var enabled = false\n\n    private let axtSubject = CurrentValueSubject<Axt?, Never>(nil)\n\n    public init<V: View>(_ view: V) {\n        let host = HostView(content: view, axtSubject: axtSubject)\n        hostingController = UIHostingController(rootView: host)\n    }\n\n    @MainActor\n    public static func host<V: View>(_ view: V) async -> AxtTest {\n        let axTest = AxtTest(view)\n        axTest.makeWindow()\n        _ = await axTest.axtSubject.dropFirst().values.first { _ in true }\n        return axTest\n    }\n\n    public func makeWindow() {\n        Self.enabled = true\n        let windowScenes = UIApplication.shared.connectedScenes\n        guard let scene = windowScenes.first as? UIWindowScene else {\n            fatalError(\"Could not connect to window scene, make sure the test is running from a host application.\")\n        }\n        window = UIWindow(windowScene: scene)\n        window.rootViewController = hostingController\n        window.makeKeyAndVisible()\n    }\n\n    public var app: AxtElement { self }\n\n    /// Let the current runloop cycle finish.\n    /// Most of the time this is enough to let SwiftUI re-evaluate any\n    /// properties and views, and can be used instead of waiting with a\n    /// time-out.\n    public static func yield() async {\n        return await withCheckedContinuation { cont in\n            DispatchQueue.main.async { [cont] in\n                cont.resume()\n            }\n        }\n    }\n}\n\nextension AxtTest: AxtNode {\n    var nodeId: UUID { axtSubject.value!.nodeId }\n\n    var getRoot: () -> Axt { { self.axtSubject.value! } }\n\n    var rootDidChange: AnyPublisher<Void, Never> { axtSubject.map { _ in }.dropFirst().eraseToAnyPublisher() }\n}\n\nprivate struct HostView<Content: View>: View {\n    let content: Content\n    let axtSubject: CurrentValueSubject<Axt?, Never>\n\n    var body: some View {\n        content\n            .border(.red, width: 2)\n            .padding()\n            .testId(\"app\")\n            .backgroundPreferenceValue(AxtPreferenceKey.self) {\n                // This is used instead of `onPreferenceChange` because that\n                // has a safety mechanism that  results in the closure not\n                // being called anymore if the preference is updated more than\n                // two times before rendering.\n                // The safety mechanism prevents infinite loops that can happen\n                // when updating a state in response to a preference change,\n                // which then causes the preference to be updated again, but we\n                // do not need that here.\n                let _ = axtSubject.send($0.first)\n                Color.clear\n            }\n    }\n}\n\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/Publisher+Compatibility.swift",
    "content": "import Combine\n\n#if TESTABLE\n\n@available(iOS, deprecated: 15.0, message: \"For iOS 14 compatibility\")\nextension Publisher {\n    var values: AsyncThrowingStream<Output, Error> {\n        AsyncThrowingStream { continuation in\n            let cancellable = sink(\n                receiveCompletion: { completion in\n                    switch completion {\n                    case .finished:\n                        continuation.finish()\n                    case let .failure(error):\n                        continuation.finish(throwing: error)\n                    }\n                }, receiveValue: { value in\n                    continuation.yield(value)\n                }\n            )\n            continuation.onTermination = { @Sendable _ in\n                cancellable.cancel()\n            }\n        }\n    }\n}\n\n@available(iOS, deprecated: 15.0, message: \"For iOS 14 compatibility\")\npublic extension Publisher where Failure == Never {\n    var values: AsyncStream<Output> {\n        AsyncStream { continuation in\n            let cancellable = sink(\n                receiveCompletion: { _ in\n                    continuation.finish()\n                }, receiveValue: { value in\n                    continuation.yield(value)\n                }\n            )\n            continuation.onTermination = { @Sendable _ in\n                cancellable.cancel()\n            }\n        }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/Publisher+firstValue.swift",
    "content": "import Combine\nimport Foundation\n\n#if TESTABLE\n\npublic struct TimeOut: Error {}\n\npublic extension Publisher where Failure == Never {\n    /// Use only for testing\n    /// Blocks the execution and returns the first value published by the\n    /// publisher. If there's no value received during the `timeout` period,\n    /// throws the `TimeOut` error.\n    func firstValue(timeout: TimeInterval = 1) async throws -> Output {\n        let value = await self.timeout(.seconds(timeout), scheduler: DispatchQueue.main)\n            .values\n            .first { _ in true }\n        guard let value = value else { throw TimeOut() }\n        return value\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtTest/printHierarchy.swift",
    "content": "import Foundation\n\n#if TESTABLE\n\n/// For debugging purposes\n/// Prints the recursive hierarchy of a Swift structure using `Mirror(reflecting:)`\npublic func printHierarchy(level: Int = 0, object: Any) {\n    let indent = Array(repeating: \" \", count: level * 2).joined()\n    let mirror = Mirror(reflecting: object)\n    print(String(describing: type(of: object)), terminator: \"\")\n    if mirror.children.isEmpty {\n        print(\" = \" + String(describing: object), terminator: \"\")\n    }\n    print(\"\")\n    for property in mirror.children {\n        print(indent + \"→ \" + (property.label ?? \"\"), terminator: \": \")\n        printHierarchy(level: level + 1, object: property.value)\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/AxtView.swift",
    "content": "import SwiftUI\n\n#if TESTABLE\n\nstruct AxtView<Content: View>: View {\n    let identifier: String?\n    let label: String?\n    let value: Any?\n    let action: (() -> Void)?\n    let setValue: ((Any?) -> Void)?\n    let content: Content\n    @State private var nodeId = UUID()\n    @State private var visible = true\n\n    var body: some View {\n        content\n            .transformPreference(AxtPreferenceKey.self) { value in\n                if value.count == 1, let placeholder = value.first, placeholder.id == nil {\n                    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)]\n                } else {\n                    value = [Axt(id: identifier, nodeId: self.nodeId, label: label, value: self.value, action: action, setValue: setValue, children: value, visible: self.visible)]\n                }\n            }\n            .onAppear { visible = true }\n            .onDisappear { visible = false }\n    }\n}\n\nstruct AxtInsertView<Content: View>: View {\n    let identifier: String\n    let condition: Bool\n    let label: String?\n    let value: Any?\n    let action: (() -> Void)?\n    let setValue: ((Any?) -> Void)?\n    let content: Content\n    @State private var nodeId = UUID()\n\n    var body: some View {\n        content\n            .transformPreference(AxtPreferenceKey.self) { value in\n                guard condition else { return }\n                value.append(Axt(id: identifier, nodeId: self.nodeId, label: label, value: self.value, action: action, setValue: setValue, children: [], visible: true))\n            }\n    }\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/Modifier.swift",
    "content": "import SwiftUI\n\npublic protocol Modifier {\n    associatedtype Content: View\n    #if TESTABLE\n    associatedtype Body: View\n    func make(_ content: Content) -> Body\n    #endif\n}\n"
  },
  {
    "path": "Sources/Axt/Native/Button.swift",
    "content": "import SwiftUI\n\npublic struct ButtonModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: Content) -> some View {\n        var action: (() -> Void)?\n        let label = dig(for: String.self, in: content) ?? \"\"\n        // Supresses a compiler warning\n        typealias Void_ = Void\n        // This type is used in `Button`\n        if let buttonAction = dig(for: (() -> Void).self, in: content) {\n            action = buttonAction\n            // This type is used in tap gestures\n        } else if let tapAction = dig(for: ((Void_) -> Void).self, in: content) {\n            action = { tapAction(()) }\n        }\n        return content.testData(label: label, value: nil, action: action)\n    }\n    #endif\n}\n\npublic extension NativeView {\n    static var button: NativeView<Content, ButtonModifier<Content>> { .init(base: .init()) }\n}\n"
  },
  {
    "path": "Sources/Axt/Native/NavigationLink.swift",
    "content": "import SwiftUI\n\npublic struct NavigationLinkModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: Content) -> some View {\n        let makeContent: (State<Bool>) -> Content = { state in\n            // This is modifying the navigation link directly and depends\n            // on the fact that the isActive: State<Bool> parameter is the\n            // first parameter in the NavigationLink structure.\n            var link = content\n            withUnsafeMutablePointer(to: &link) { pointer in\n                pointer.withMemoryRebound(to: State<Bool>.self, capacity: 1) { statePointer in\n                    statePointer.pointee = state\n                }\n            }\n            return link\n        }\n        return AxtNavigationLink(isActive: dig(for: State<Bool>.self, in: content)!, content: makeContent)\n    }\n    #endif\n}\n\n#if TESTABLE\n\nprivate struct AxtNavigationLink<Content: View>: View {\n    // Because a State variable is only ready once the body is called by\n    // SwiftUI, you cannot access State variables from the parent of a view,\n    // and they need to be moved up the hierarchy.\n    @State var isActive: Bool\n    let content: (State<Bool>) -> Content\n\n    init(isActive: State<Bool>, content: @escaping ((State<Bool>) -> Content)) {\n        _isActive = isActive\n        self.content = content\n    }\n\n    var body: some View {\n        // Prevents SwiftUI from optimizing state updating away, so that it is\n        // ready when used in the `activate` closure.\n        // swiftformat:disable:next redundantLet\n        let _ = isActive\n        content(_isActive)\n            .testData {\n                isActive.toggle()\n            }\n    }\n}\n\n#endif\n\npublic extension NativeView {\n    static var navigationLink: NativeView<Content, NavigationLinkModifier<Content>> { .init(base: .init()) }\n}\n"
  },
  {
    "path": "Sources/Axt/Native/Text.swift",
    "content": "import SwiftUI\n\npublic struct TextModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: Content) -> some View {\n        var label: String?\n        if let text = dig(for: Text.self, in: content) {\n            label = dig(for: String.self, in: text)\n        }\n        return content.testData(label: label)\n    }\n    #endif\n}\n\npublic extension NativeView {\n    static var text: NativeView<Content, TextModifier<Content>> { .init(base: .init()) }\n}\n"
  },
  {
    "path": "Sources/Axt/Native/TextField.swift",
    "content": "import SwiftUI\n\npublic struct TextFieldModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: Content) -> some View {\n        var label: String?\n        if let text = dig(for: Text.self, in: content) {\n            label = dig(for: String.self, in: text)\n        }\n        let _value = dig(for: Binding<String>.self, in: content)\n        let value = _value?.wrappedValue\n        return content.testData(label: label, value: value, setValue: { if let newValue = $0 as? String { _value?.wrappedValue = newValue } })\n    }\n    #endif\n}\n\npublic extension NativeView {\n    static var textField: NativeView<Content, TextFieldModifier<Content>> { .init(base: .init()) }\n}\n"
  },
  {
    "path": "Sources/Axt/Native/Toggle.swift",
    "content": "import SwiftUI\n\npublic struct ToggleModifier<Content: View>: Modifier {\n    #if TESTABLE\n    public func make(_ content: Content) -> some View {\n        let label = dig(for: String.self, in: content) ?? \"\"\n        let isOn: Bool?\n        let action: () -> Void\n        if #available(iOS 16, *) {\n            // Starting with iOS 16, the state of a toggle is no longer a Bool,\n            // but an internal enum that can be in an on, off or mixed state.\n            let anyToggleStateBinding = digForProperty(named: \"_toggleState\", in: content)\n            let _toggleState = withUnsafePointer(to: anyToggleStateBinding) {\n                $0.withMemoryRebound(to: Binding<ToggleState>.self, capacity: 1) {\n                    $0.pointee\n                }\n            }\n            switch _toggleState.wrappedValue {\n            case .on: isOn = true\n            case .off: isOn = false\n            case .mixed: isOn = nil\n            }\n            action = {\n                if _toggleState.wrappedValue == .off {\n                    _toggleState.wrappedValue = .on\n                } else if _toggleState.wrappedValue == .on {\n                    _toggleState.wrappedValue = .off\n                }\n            }\n        } else {\n            let _isOn = dig(for: Binding<Bool>.self, in: content)\n            isOn = _isOn?.wrappedValue\n            action = { _isOn?.wrappedValue.toggle() }\n\n        }\n        return content.testData(label: label, value: isOn, action: { withAnimation { action() } })\n    }\n    #endif\n}\n\npublic extension NativeView {\n    static var toggle: NativeView<Content, ToggleModifier<Content>> { .init(base: .init()) }\n}\n\nprivate enum ToggleState {\n    case on\n    case off\n    case mixed\n}\n"
  },
  {
    "path": "Sources/Axt/NativeView.swift",
    "content": "import SwiftUI\n\npublic struct NativeView<Content: View, M: Modifier> {\n    public let base: M\n}\n"
  },
  {
    "path": "Sources/Axt/View+testData.swift",
    "content": "import SwiftUI\n\npublic extension View {\n    @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 {\n        #if TESTABLE\n        if AxtTest.enabled {\n            AxtView(\n                identifier: nil,\n                label: label,\n                value: value,\n                action: action,\n                setValue: setValue,\n                content: type.base.make(self)\n            )\n        } else { self }\n        #else\n        self\n        #endif\n    }\n\n    @ViewBuilder func testData(label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View {\n        #if TESTABLE\n        if AxtTest.enabled {\n            AxtView(\n                identifier: nil,\n                label: label,\n                value: value,\n                action: action,\n                setValue: setValue,\n                content: self\n            )\n        } else { self }\n        #else\n        self\n        #endif\n    }\n\n}\n"
  },
  {
    "path": "Sources/Axt/View+testId.swift",
    "content": "import SwiftUI\n\npublic extension View {\n    @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 {\n        #if TESTABLE\n        if AxtTest.enabled {\n            AxtView(\n                identifier: identifier,\n                label: label,\n                value: value,\n                action: action,\n                setValue: setValue,\n                content: type.base.make(self)\n            )\n        } else { self }\n        #else\n        self\n        #endif\n    }\n\n    @ViewBuilder func testId(_ identifier: String, label: String? = nil, value: Any? = nil, action: (() -> Void)? = nil, setValue: ((Any?) -> Void)? = nil) -> some View {\n        #if TESTABLE\n        if AxtTest.enabled {\n            AxtView(\n                identifier: identifier,\n                label: label,\n                value: value,\n                action: action,\n                setValue: setValue,\n                content: self\n            )\n        } else { self }\n        #else\n        self\n        #endif\n    }\n\n    @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 {\n        #if TESTABLE\n        if AxtTest.enabled {\n            AxtInsertView(\n                identifier: identifier,\n                condition: condition,\n                label: label,\n                value: value,\n                action: action,\n                setValue: setValue,\n                content: self\n            )\n        } else { self }\n        #else\n        self\n        #endif\n    }\n}\n"
  },
  {
    "path": "Sources/Axt/dig.swift",
    "content": "import Foundation\n\n#if TESTABLE\n\n/// Recursively search for a property of a specific type in another object.\nfunc dig<T>(for _: T.Type, in object: Any) -> T? {\n    if let result = object as? T { return result }\n\n    for child in Mirror(reflecting: object).children {\n        if let result = dig(for: T.self, in: child.value) {\n            return result\n        }\n    }\n\n    return nil\n}\n\n/// Recursively search for a property with a specific name in another object.\nfunc digForProperty(named name: String, in object: Any) -> Any? {\n    for child in Mirror(reflecting: object).children {\n        if child.label == name { return child.value }\n        if let result = digForProperty(named: name, in: child.value) {\n            return result\n        }\n    }\n\n    return nil\n}\n\n#endif\n"
  },
  {
    "path": "Sources/Axt/hostAxtSheet.swift",
    "content": "import SwiftUI\n\npublic extension View {\n    /// Use this modifier on the content **inside** of a sheet, so that the\n    /// contents can be accessed using `AXTest.sheets`.\n    func hostAxtSheet() -> some View {\n        #if TESTABLE\n        AxtSheet(content: self)\n        #else\n        self\n        #endif\n    }\n}\n\n#if TESTABLE\n\nstruct AxtSheet<Content: View>: UIViewControllerRepresentable {\n    let content: Content\n\n    public struct Coordinator {\n        let id: UUID\n    }\n\n    public func makeUIViewController(context: Context) -> UIViewController {\n        let test = AxtTest(content)\n        AxtTest.sheets[context.coordinator.id] = test\n        return test.hostingController\n    }\n\n    public func updateUIViewController(_: UIViewController, context: Context) {\n        let test = AxtTest.sheets[context.coordinator.id]\n        (test?.hostingController as? UIHostingController<Content>)?.rootView = content\n    }\n\n    public static func dismantleUIViewController(_: UIViewController, coordinator: Coordinator) {\n        AxtTest.sheets.removeValue(forKey: coordinator.id)\n    }\n\n    public func makeCoordinator() -> Coordinator {\n        Coordinator(id: UUID())\n    }\n}\n\n#endif\n"
  }
]