diff --git a/.vscodeignore b/.vscodeignore index 98b4927..8fa4baf 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -14,3 +14,4 @@ Makefile **/*.tsbuildinfo node_modules/** builds/** +examples/** diff --git a/README.md b/README.md index 19ba231..afb04c6 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@

Installation • + Demo • Features • AI Tools • Physical Devices • @@ -34,6 +35,16 @@ Install [XBridge from the Visual Studio Marketplace](https://marketplace.visuals --- +## See XBridge in action + +![XBridge feature tour showing project controls, passing tests, and an app running in Simulator](docs/images/feature-tour.gif) + +The tour uses the included [XBridgeDemo Xcode project](examples/XBridgeDemo). View the full-resolution captures: [project and destination controls](docs/images/overview.png), [XCTest and Swift Testing in Test Explorer](docs/images/tests.png), [build and run](docs/images/build-and-run.png), and [the iPhone app](docs/images/demo-app.png). + +[Explore every XBridge command and AI tool](docs/capabilities.md). + +--- + ## Why XBridge? - Run the core Xcode workflow from the editor where you write Swift. diff --git a/docs/capabilities.md b/docs/capabilities.md new file mode 100644 index 0000000..0c3be71 --- /dev/null +++ b/docs/capabilities.md @@ -0,0 +1,80 @@ +# XBridge capabilities + +XBridge puts the everyday Xcode workflow in VS Code. These are the commands and tools available in version 1.4.2. Open the Command Palette and type `XBridge:` to find a command; the most common actions also appear in the XBridge sidebar and status bar. + +## Project and workflow + +| Command | What it does | +|:---|:---| +| `XBridge: Select Xcode Project or Workspace` | Choose an `.xcodeproj`, `.xcworkspace`, or Swift package when more than one is available. | +| `XBridge: Select Scheme` | Choose the Xcode scheme. | +| `XBridge: Select Build Configuration` | Switch between configurations such as Debug and Release. | +| `XBridge: Select Scheme, Configuration or Destination` | Open one picker for the active build target. | +| `XBridge: Refresh Schemes` | Reload schemes and targets from Xcode. | +| `XBridge: Open in Xcode` | Open the active project or workspace in Xcode. | +| `XBridge: Getting Started` | Open the guided VS Code walkthrough. | + +## Build, run, test, and packages + +| Command | What it does | +|:---|:---| +| `XBridge: Build` | Compile the active scheme. | +| `XBridge: Build & Run` | Build, install, and launch the app on the selected destination. | +| `XBridge: Stop` | Cancel the active operation or stop the running app. | +| `XBridge: Stop Running App` | Terminate the launched app without shutting down the simulator. | +| `XBridge: Clean Build Folder` | Remove the active project's DerivedData build products. | +| `XBridge: Run Tests` | Run the selected test plan or target with `xcodebuild`. | +| `XBridge: Select Test Plan` | Choose an Xcode test plan, if the scheme has one. | +| `XBridge: Select Test Target` | Limit a run to one test target. | +| `XBridge: Resolve Package Dependencies` | Resolve Swift Package Manager dependencies through Xcode. | + +The native VS Code Test Explorer discovers XCTest methods and Swift Testing `@Test` methods, shows per-test results, and lets you run individual tests. Build errors and test failures appear in Problems and at their source locations. + +## Results and logs + +| Command | What it does | +|:---|:---| +| `XBridge: Show Build Output` | Open the `XBridge Build` output channel. | +| `XBridge: Show Build Issues` | Browse errors and warnings and jump to the source line. | +| `XBridge: Show App Console` | Open the launched app's console output. | + +The status bar also shows the selected scheme and destination, build activity, and error and warning counts. + +## Simulators and devices + +| Command | What it does | +|:---|:---| +| `XBridge: Select Destination (Simulator/Device)` | Choose a simulator or connected physical device. | +| `XBridge: Set as Destination` | Use the selected device from the Destinations tree. | +| `XBridge: Boot Simulator` | Start a simulator. | +| `XBridge: Shutdown Simulator` | Shut down a simulator. | +| `XBridge: Open Simulator.app` | Bring Apple's Simulator app forward. | +| `XBridge: Refresh Simulators` | Reload the local simulator list. | +| `XBridge: Refresh Devices & Simulators` | Reload simulator and connected-device discovery. | +| `XBridge: Erase Simulator Content` | Reset a simulator's content and settings. | +| `XBridge: Copy Simulator UDID` | Copy the simulator identifier. | +| `XBridge: Open URL or Deep Link…` | Open a URL in a simulator. | +| `XBridge: Take Screenshot` | Save a PNG from a booted simulator. | +| `XBridge: Start / Stop Screen Recording` | Save a simulator recording as video. | +| `XBridge: Toggle Light / Dark Appearance` | Change the simulator appearance. | +| `XBridge: Open App Data Container` | Open the installed app's container in Finder. | +| `XBridge: Uninstall App from Simulator` | Remove the installed app. | + +Connected iPhones and iPads appear in Destinations through Apple's `devicectl`. Physical-device builds require working code signing and Developer Mode. + +## AI agent tools + +Compatible VS Code AI agents can call these eight tools directly: + +| Tool | Capability | +|:---|:---| +| `xbridge_build` | Build and return compiler diagnostics. | +| `xbridge_test` | Run tests and return failures. | +| `xbridge_run` | Build and launch an app. | +| `xbridge_listSchemes` | List schemes, configurations, and targets. | +| `xbridge_listSimulators` | List available simulators and status. | +| `xbridge_bootSimulator` | Boot a simulator. | +| `xbridge_readDiagnostics` | Read the last build or test diagnostics. | +| `xbridge_clean` | Clean the Xcode build folder. | + +See the [main README](../README.md#settings) for the eleven configurable settings and [XBridgeDemo](../examples/XBridgeDemo) for a project you can try locally. diff --git a/docs/images/build-and-run.png b/docs/images/build-and-run.png new file mode 100644 index 0000000..2b6772c Binary files /dev/null and b/docs/images/build-and-run.png differ diff --git a/docs/images/demo-app.png b/docs/images/demo-app.png new file mode 100644 index 0000000..af59a8a Binary files /dev/null and b/docs/images/demo-app.png differ diff --git a/docs/images/feature-tour.gif b/docs/images/feature-tour.gif new file mode 100644 index 0000000..7dcf4e0 Binary files /dev/null and b/docs/images/feature-tour.gif differ diff --git a/docs/images/overview.png b/docs/images/overview.png new file mode 100644 index 0000000..cc1011c Binary files /dev/null and b/docs/images/overview.png differ diff --git a/docs/images/tests.png b/docs/images/tests.png new file mode 100644 index 0000000..0de32bd Binary files /dev/null and b/docs/images/tests.png differ diff --git a/examples/XBridgeDemo/.gitignore b/examples/XBridgeDemo/.gitignore new file mode 100644 index 0000000..445b095 --- /dev/null +++ b/examples/XBridgeDemo/.gitignore @@ -0,0 +1,4 @@ +.build/ +.vscode/ +*.xcuserstate +xcuserdata/ diff --git a/examples/XBridgeDemo/README.md b/examples/XBridgeDemo/README.md new file mode 100644 index 0000000..87dcb3e --- /dev/null +++ b/examples/XBridgeDemo/README.md @@ -0,0 +1,9 @@ +# XBridge Demo + +A small, buildable SwiftUI app used to demonstrate XBridge in VS Code. It includes one XCTest and one Swift Testing test so both appear in Test Explorer. + +1. Open this folder in VS Code on a Mac with Xcode installed. +2. Select the `XBridgeDemo` scheme and an iOS Simulator in the XBridge sidebar. +3. Use **Build & Run** to launch the app, or open Test Explorer and run both tests. + +The app has no external packages or signing requirement for Simulator. Xcode build products and local VS Code settings are ignored by Git. diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj new file mode 100644 index 0000000..bb2437b --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj @@ -0,0 +1,459 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; }; + 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */; }; + 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */; }; + 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */; }; + 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C94386DDF7FC57A34733389 /* ContentView.swift */; }; + A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 926908D120004E2CDF95387F /* Project object */; + proxyType = 1; + remoteGlobalIDString = 38C28A02E02E1A716344BAFD; + remoteInfo = XBridgeDemo; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoApp.swift; sourceTree = ""; }; + 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ModernTests.swift; sourceTree = ""; }; + 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XBridgeDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBridgeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoTests.swift; sourceTree = ""; }; + 8C94386DDF7FC57A34733389 /* ContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + D21DF1F5E075569C85A5A80D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5623CC290D6BB5B3555C9E37 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 797F4E7CC0A84E8ADD920E9C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4F75EA34634134EB2327C973 /* Products */ = { + isa = PBXGroup; + children = ( + 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */, + 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 5AABD117A77BFAA3F72F4001 = { + isa = PBXGroup; + children = ( + 4F75EA34634134EB2327C973 /* Products */, + 76777653C83C84E6DFCCB739 /* Frameworks */, + A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */, + 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */, + ); + sourceTree = ""; + }; + 76777653C83C84E6DFCCB739 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C4778F146AEF9B99291A3F90 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */ = { + isa = PBXGroup; + children = ( + 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */, + 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */, + ); + name = XBridgeDemoTests; + path = XBridgeDemoTests; + sourceTree = ""; + }; + A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */ = { + isa = PBXGroup; + children = ( + 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */, + 8C94386DDF7FC57A34733389 /* ContentView.swift */, + ); + name = XBridgeDemo; + path = XBridgeDemo; + sourceTree = ""; + }; + C4778F146AEF9B99291A3F90 /* iOS */ = { + isa = PBXGroup; + children = ( + D21DF1F5E075569C85A5A80D /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 38C28A02E02E1A716344BAFD /* XBridgeDemo */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */; + buildPhases = ( + 3ED4D77EFBB1BAF4890B3D9F /* Sources */, + 797F4E7CC0A84E8ADD920E9C /* Frameworks */, + 5A26789469FEB06DBB37C2A2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = XBridgeDemo; + productName = XBridgeDemo; + productReference = 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */; + productType = "com.apple.product-type.application"; + }; + E343816C6E356CED400EAB1F /* XBridgeDemoTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */; + buildPhases = ( + 95855EDEB98C88C5E50A19C9 /* Sources */, + 5623CC290D6BB5B3555C9E37 /* Frameworks */, + 56C8A09B750384342F4A89E2 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */, + ); + name = XBridgeDemoTests; + productName = XBridgeDemoTests; + productReference = 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 926908D120004E2CDF95387F /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 2660; + LastUpgradeCheck = 2660; + }; + buildConfigurationList = 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 5AABD117A77BFAA3F72F4001; + minimizedProjectReferenceProxies = 0; + preferredProjectObjectVersion = 77; + productRefGroup = 4F75EA34634134EB2327C973 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 38C28A02E02E1A716344BAFD /* XBridgeDemo */, + E343816C6E356CED400EAB1F /* XBridgeDemoTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 56C8A09B750384342F4A89E2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5A26789469FEB06DBB37C2A2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 3ED4D77EFBB1BAF4890B3D9F /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */, + 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 95855EDEB98C88C5E50A19C9 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */, + 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = XBridgeDemo; + target = 38C28A02E02E1A716344BAFD /* XBridgeDemo */; + targetProxy = 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 03F3B0CAB58D500D2D3DCF86 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo"; + }; + name = Debug; + }; + 44B5F68238D33181F33D81DC /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 5915172CE0D64BA5F77731AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7E6DCA70F2C65BCFF5E627F8 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + B3BC7A1BB930F45E88B8E6F9 /* 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++14"; + CLANG_CXX_LIBRARY = "libc++"; + 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; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + C0216E6A1D67B593D084CC55 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B3BC7A1BB930F45E88B8E6F9 /* Debug */, + 44B5F68238D33181F33D81DC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7E6DCA70F2C65BCFF5E627F8 /* Release */, + 03F3B0CAB58D500D2D3DCF86 /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C0216E6A1D67B593D084CC55 /* Release */, + 5915172CE0D64BA5F77731AE /* Debug */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 926908D120004E2CDF95387F /* Project object */; +} diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme new file mode 100644 index 0000000..be305a4 --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/XBridgeDemo/XBridgeDemo/ContentView.swift b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift new file mode 100644 index 0000000..775e19f --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift @@ -0,0 +1,71 @@ +import SwiftUI + +struct ContentView: View { + private let features = [ + Feature(title: "Build", detail: "Compile with xcodebuild", symbol: "hammer.fill", color: .blue), + Feature(title: "Test", detail: "XCTest & Swift Testing", symbol: "checkmark.seal.fill", color: .green), + Feature(title: "Run", detail: "Simulator or device", symbol: "play.circle.fill", color: .indigo), + Feature(title: "Diagnose", detail: "Inline build issues", symbol: "waveform.path.ecg", color: .orange), + ] + + var body: some View { + NavigationStack { + ScrollView { + VStack(alignment: .leading, spacing: 24) { + VStack(alignment: .leading, spacing: 8) { + Image(systemName: "point.3.connected.trianglepath.dotted") + .font(.system(size: 42, weight: .semibold)) + .foregroundStyle(.blue.gradient) + + Text("Xcode power.\nVS Code flow.") + .font(.system(size: 36, weight: .bold, design: .rounded)) + + Text("A tiny project built to demonstrate XBridge.") + .foregroundStyle(.secondary) + } + + LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 14) { + ForEach(features) { feature in + VStack(alignment: .leading, spacing: 10) { + Image(systemName: feature.symbol) + .font(.title2) + .foregroundStyle(feature.color) + Text(feature.title) + .font(.headline) + Text(feature.detail) + .font(.caption) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity, minHeight: 108, alignment: .leading) + .padding() + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 18)) + } + } + + Label("Connected to XBridge", systemImage: "checkmark.circle.fill") + .font(.subheadline.weight(.semibold)) + .foregroundStyle(.green) + .frame(maxWidth: .infinity) + .padding() + .background(.green.opacity(0.1), in: Capsule()) + } + .padding(24) + } + .background(Color(.systemGroupedBackground)) + .navigationTitle("XBridge Demo") + } + } +} + +private struct Feature: Identifiable { + let title: String + let detail: String + let symbol: String + let color: Color + + var id: String { title } +} + +#Preview { + ContentView() +} diff --git a/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift new file mode 100644 index 0000000..6595fd1 --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct XBridgeDemoApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift new file mode 100644 index 0000000..8cd4cae --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift @@ -0,0 +1,9 @@ +import Testing +@testable import XBridgeDemo + +struct ModernTests { + @Test("XBridge discovers Swift Testing tests") + func xbridgeDiscoversModernTests() { + #expect("XBridge".hasPrefix("X")) + } +} diff --git a/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift new file mode 100644 index 0000000..1ce42de --- /dev/null +++ b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift @@ -0,0 +1,8 @@ +import XCTest +@testable import XBridgeDemo + +final class XBridgeDemoTests: XCTestCase { + func testDemoProjectLoads() { + XCTAssertEqual(2 + 2, 4) + } +}