diff --git a/.github/workflows/arkdrop-swift-bindings-release.yml b/.github/workflows/arkdrop-swift-bindings-release.yml new file mode 100644 index 00000000..6d68c422 --- /dev/null +++ b/.github/workflows/arkdrop-swift-bindings-release.yml @@ -0,0 +1,229 @@ +name: ARK Drop Swift Bindings Release + +on: + push: + paths: + - "drop-core/entities/**" + - "drop-core/exchanges/**" + - "drop-core/uniffi/**" + - ".github/workflows/arkdrop-swift-bindings-release.yml" + workflow_dispatch: + +jobs: + build-and-publish: + runs-on: macos-latest + permissions: + contents: write + packages: write + + steps: + - uses: actions/checkout@v4 + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-ios,x86_64-apple-ios,aarch64-apple-ios-sim,x86_64-apple-darwin,aarch64-apple-darwin + + - name: Verify Rust targets + run: | + rustup target list --installed + echo "Verifying targets are installed..." + for target in aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim x86_64-apple-darwin aarch64-apple-darwin; do + if rustup target list --installed | grep -q "$target"; then + echo "✓ $target is installed" + else + echo "✗ $target is NOT installed" + rustup target add "$target" + fi + done + + - uses: Swatinem/rust-cache@v2 + with: + workspaces: drop-core/uniffi + + - name: Install Xcode Command Line Tools + run: | + xcode-select --install 2>/dev/null || true + xcode-select -p + + - name: Generate Swift Bindings + working-directory: ./drop-core/uniffi/bindings/swift + run: | + chmod +x generate-bindings.sh + ./generate-bindings.sh + env: + RUST_BACKTRACE: 1 + + - name: Verify Generated Bindings + working-directory: ./drop-core/uniffi/bindings/swift + run: | + echo "Checking generated files..." + ls -la + echo "" + echo "Required files check:" + for file in ArkDrop.swift arkdrop_uniffiFFI.h module.modulemap; do + if [ -f "$file" ]; then + echo "✓ $file exists" + echo " Size: $(wc -c < "$file") bytes" + else + echo "✗ $file missing!" + exit 1 + fi + done + + - name: Build XCFramework + working-directory: ./drop-core/uniffi/bindings/swift + run: | + chmod +x build-xcframework.sh + ./build-xcframework.sh + env: + RUST_BACKTRACE: 1 + + - name: Create Swift Package Archive + working-directory: ./drop-core/uniffi/bindings/swift + run: | + echo "Creating distributable Swift package..." + + # Create package directory structure + mkdir -p ArkDrop-Swift-Package + mkdir -p ArkDrop-Swift-Package/Sources/ArkDrop + + # Verify all required files exist before copying + echo "Verifying files before packaging..." + for file in arkdrop_uniffiFFI.xcframework ArkDrop.swift Package.swift arkdrop_uniffiFFI.h module.modulemap; do + if [ ! -e "$file" ]; then + echo "ERROR: Required file missing: $file" + exit 1 + fi + done + + # Copy XCFramework + echo "Copying XCFramework..." + cp -r arkdrop_uniffiFFI.xcframework ArkDrop-Swift-Package/ + + # Copy Swift source to Sources directory + echo "Copying Swift bindings..." + cp ArkDrop.swift ArkDrop-Swift-Package/Sources/ArkDrop/ + + # Copy Package.swift + echo "Copying Package.swift..." + cp Package.swift ArkDrop-Swift-Package/ + + # Copy header and modulemap for reference + echo "Copying headers and modulemap..." + cp arkdrop_uniffiFFI.h ArkDrop-Swift-Package/ + cp module.modulemap ArkDrop-Swift-Package/ + + # Create README with usage instructions + cat > ArkDrop-Swift-Package/README.md << 'EOF' + # ArkDrop Swift Bindings + + This package contains Swift bindings for ARK Drop. + + ## Contents + + - `arkdrop_uniffiFFI.xcframework` - Pre-built XCFramework for iOS and macOS + - `Sources/ArkDrop/ArkDrop.swift` - Swift bindings generated by UniFFI + - `Package.swift` - Swift Package Manager configuration + - `arkdrop_uniffiFFI.h` - C header file (reference) + - `module.modulemap` - Module map (reference) + + ## Installation + + ### Swift Package Manager + + Add to your `Package.swift`: + + ```swift + dependencies: [ + .package(path: "path/to/ArkDrop-Swift-Package") + ] + ``` + + Or add the extracted package directory to your Xcode project via: + File → Add Package Dependencies → Add Local... + + ## Usage + + Import the module in your Swift code: + + ```swift + import ArkDrop + ``` + EOF + + # List package contents + echo "" + echo "Package contents:" + find ArkDrop-Swift-Package -type f | sort + + # Create a zip archive + echo "" + echo "Creating archive..." + zip -r ArkDrop-Swift-Package.zip ArkDrop-Swift-Package/ + + # Create checksum + echo "Creating checksum..." + shasum -a 256 ArkDrop-Swift-Package.zip > ArkDrop-Swift-Package.zip.sha256 + + echo "" + echo "Package created successfully!" + cat ArkDrop-Swift-Package.zip.sha256 + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: swift-bindings + path: | + drop-core/uniffi/bindings/swift/ArkDrop-Swift-Package.zip + drop-core/uniffi/bindings/swift/ArkDrop-Swift-Package.zip.sha256 + retention-days: 30 + + - name: Create Release + if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/')) + uses: softprops/action-gh-release@v2 + with: + tag_name: swift-bindings-${{ github.run_id }} + name: Swift Bindings Release ${{ github.run_id }} + draft: false + prerelease: false + files: | + drop-core/uniffi/bindings/swift/ArkDrop-Swift-Package.zip + drop-core/uniffi/bindings/swift/ArkDrop-Swift-Package.zip.sha256 + body: | + ## Swift Bindings for ARK Drop + + This release contains the Swift bindings for ARK Drop, including: + - XCFramework for iOS (device and simulator) and macOS + - Swift source files + - Swift Package Manager configuration + + ### Installation + + #### Swift Package Manager + + 1. Download and extract `ArkDrop-Swift-Package.zip` + 2. Add the package to your Xcode project or include it in your `Package.swift`: + + ```swift + dependencies: [ + .package(path: "path/to/ArkDrop-Swift-Package") + ] + ``` + + #### Manual Installation + + 1. Download `ArkDrop-Swift-Package.zip` + 2. Extract and add `arkdrop_uniffiFFI.xcframework` to your Xcode project + 3. Add `drop.swift` to your project sources + + ### Verification + + Verify the package integrity using the SHA256 checksum: + ```bash + shasum -a 256 -c ArkDrop-Swift-Package.zip.sha256 + ``` + + Generated from commit: ${{ github.sha }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/drop-core/uniffi/Cargo.toml b/drop-core/uniffi/Cargo.toml index 34744a7a..39fe431e 100644 --- a/drop-core/uniffi/Cargo.toml +++ b/drop-core/uniffi/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "arkdrop-uniffi" -version = "1.1.0" +version = "1.2.0" edition = "2024" [lib] name = "arkdrop_uniffi" -crate-type = ["cdylib"] +crate-type = ["staticlib", "cdylib"] bench = false [[bin]] @@ -13,6 +13,11 @@ name = "uniffi-bindgen" path = "uniffi-bindgen.rs" bench = false +[[bin]] +name = "uniffi-bindgen-swift" +path = "uniffi-bindgen-swift.rs" +bench = false + [dependencies] arkdropx-sender = { path = "../exchanges/sender" } arkdropx-receiver = { path = "../exchanges/receiver" } diff --git a/drop-core/uniffi/bindings/swift/.gitignore b/drop-core/uniffi/bindings/swift/.gitignore new file mode 100644 index 00000000..22ca1f5c --- /dev/null +++ b/drop-core/uniffi/bindings/swift/.gitignore @@ -0,0 +1,21 @@ +# Build artifacts +build/ +*.xcframework + +# Generated files from uniffi-bindgen +ArkDrop.swift +arkdrop_uniffiFFI.h +module.modulemap +arkdrop_uniffiFFI.modulemap + +# Swift Package Manager +.build/ +.swiftpm/ + +# Xcode +xcuserdata/ +*.xcworkspace +DerivedData/ + +# macOS +.DS_Store diff --git a/drop-core/uniffi/bindings/swift/Package.swift b/drop-core/uniffi/bindings/swift/Package.swift new file mode 100644 index 00000000..bd998545 --- /dev/null +++ b/drop-core/uniffi/bindings/swift/Package.swift @@ -0,0 +1,29 @@ +// swift-tools-version: 5.9 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "ArkDrop", + platforms: [ + .iOS(.v13), + .macOS(.v10_15) + ], + products: [ + .library( + name: "ArkDrop", + targets: ["ArkDrop"] + ), + ], + targets: [ + .target( + name: "ArkDrop", + dependencies: ["arkdrop_uniffiFFI"], + path: "Sources" + ), + .binaryTarget( + name: "arkdrop_uniffiFFI", + path: "arkdrop_uniffiFFI.xcframework" + ) + ] +) diff --git a/drop-core/uniffi/bindings/swift/build-xcframework.sh b/drop-core/uniffi/bindings/swift/build-xcframework.sh new file mode 100755 index 00000000..1fc96ab8 --- /dev/null +++ b/drop-core/uniffi/bindings/swift/build-xcframework.sh @@ -0,0 +1,137 @@ +#!/bin/bash +set -e + +# Build the Rust library for iOS and macOS and create an XCFramework + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +UNIFFI_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +WORKSPACE_ROOT="$(cd "$UNIFFI_DIR/../.." && pwd)" +BUILD_DIR="$SCRIPT_DIR/build" +XCFRAMEWORK_NAME="arkdrop_uniffiFFI.xcframework" + +echo "Building Rust library for iOS and macOS..." + +# Clean previous builds +rm -rf "$BUILD_DIR" +rm -rf "$SCRIPT_DIR/$XCFRAMEWORK_NAME" +mkdir -p "$BUILD_DIR" + +cd "$UNIFFI_DIR" + +# Build for iOS device (arm64) +echo "Building for iOS (aarch64-apple-ios)..." +if ! cargo rustc --lib --release --target aarch64-apple-ios --crate-type staticlib; then + echo "ERROR: Failed to build for aarch64-apple-ios" + echo "This target may not be compatible with the current dependencies." + exit 1 +fi +echo "Checking output..." +ls -lh "$WORKSPACE_ROOT/target/aarch64-apple-ios/release/" | grep libarkdrop_uniffi || echo "Warning: No library found for aarch64-apple-ios" + +# Build for iOS Simulator (x86_64 and arm64) +echo "Building for iOS Simulator (x86_64)..." +if ! cargo rustc --lib --release --target x86_64-apple-ios --crate-type staticlib; then + echo "ERROR: Failed to build for x86_64-apple-ios" + exit 1 +fi +echo "Checking output..." +ls -lh "$WORKSPACE_ROOT/target/x86_64-apple-ios/release/" | grep libarkdrop_uniffi || echo "Warning: No library found for x86_64-apple-ios" + +echo "Building for iOS Simulator (aarch64)..." +if ! cargo rustc --lib --release --target aarch64-apple-ios-sim --crate-type staticlib; then + echo "ERROR: Failed to build for aarch64-apple-ios-sim" + exit 1 +fi +echo "Checking output..." +ls -lh "$WORKSPACE_ROOT/target/aarch64-apple-ios-sim/release/" | grep libarkdrop_uniffi || echo "Warning: No library found for aarch64-apple-ios-sim" + +# Build for macOS (x86_64 and arm64) +echo "Building for macOS (x86_64)..." +if ! cargo rustc --lib --release --target x86_64-apple-darwin --crate-type staticlib; then + echo "ERROR: Failed to build for x86_64-apple-darwin" + exit 1 +fi +echo "Checking output..." +ls -lh "$WORKSPACE_ROOT/target/x86_64-apple-darwin/release/" | grep libarkdrop_uniffi || echo "Warning: No library found for x86_64-apple-darwin" + +echo "Building for macOS (aarch64)..." +if ! cargo rustc --lib --release --target aarch64-apple-darwin --crate-type staticlib; then + echo "ERROR: Failed to build for aarch64-apple-darwin" + exit 1 +fi +echo "Checking output..." +ls -lh "$WORKSPACE_ROOT/target/aarch64-apple-darwin/release/" | grep libarkdrop_uniffi || echo "Warning: No library found for aarch64-apple-darwin" + +# Verify all required libraries exist before proceeding +echo "" +echo "Verifying all libraries are built..." +for target_lib in \ + "$WORKSPACE_ROOT/target/aarch64-apple-ios/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/x86_64-apple-ios/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/aarch64-apple-ios-sim/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/x86_64-apple-darwin/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/aarch64-apple-darwin/release/libarkdrop_uniffi.a"; do + if [ ! -f "$target_lib" ]; then + echo "ERROR: Required library not found: $target_lib" + echo "Build may have failed. Please check the cargo build output above." + exit 1 + else + echo "✓ Found: $target_lib" + fi +done +echo "" + +# Create fat libraries +echo "Creating universal libraries..." +mkdir -p "$BUILD_DIR/ios-simulator" +mkdir -p "$BUILD_DIR/macos" + +lipo -create \ + "$WORKSPACE_ROOT/target/x86_64-apple-ios/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/aarch64-apple-ios-sim/release/libarkdrop_uniffi.a" \ + -output "$BUILD_DIR/ios-simulator/libarkdrop_uniffi.a" + +lipo -create \ + "$WORKSPACE_ROOT/target/x86_64-apple-darwin/release/libarkdrop_uniffi.a" \ + "$WORKSPACE_ROOT/target/aarch64-apple-darwin/release/libarkdrop_uniffi.a" \ + -output "$BUILD_DIR/macos/libarkdrop_uniffi.a" + +# Copy iOS device library +mkdir -p "$BUILD_DIR/ios" +cp "$WORKSPACE_ROOT/target/aarch64-apple-ios/release/libarkdrop_uniffi.a" "$BUILD_DIR/ios/" + +# Copy headers and modulemap to each platform directory +for platform_dir in "$BUILD_DIR/ios" "$BUILD_DIR/ios-simulator" "$BUILD_DIR/macos"; do + mkdir -p "$platform_dir/Headers" + + # Copy the C header + if [ -f "$SCRIPT_DIR/arkdrop_uniffiFFI.h" ]; then + cp "$SCRIPT_DIR/arkdrop_uniffiFFI.h" "$platform_dir/Headers/" + else + echo "Error: arkdrop_uniffiFFI.h not found. Run generate-bindings.sh first." + exit 1 + fi + + # Copy the modulemap + if [ -f "$SCRIPT_DIR/module.modulemap" ]; then + cp "$SCRIPT_DIR/module.modulemap" "$platform_dir/Headers/" + else + echo "Error: module.modulemap not found. Run generate-bindings.sh first." + exit 1 + fi +done + +# Create XCFramework +echo "Creating XCFramework..." +xcodebuild -create-xcframework \ + -library "$BUILD_DIR/ios/libarkdrop_uniffi.a" \ + -headers "$BUILD_DIR/ios/Headers" \ + -library "$BUILD_DIR/ios-simulator/libarkdrop_uniffi.a" \ + -headers "$BUILD_DIR/ios-simulator/Headers" \ + -library "$BUILD_DIR/macos/libarkdrop_uniffi.a" \ + -headers "$BUILD_DIR/macos/Headers" \ + -output "$SCRIPT_DIR/$XCFRAMEWORK_NAME" + +echo "XCFramework created successfully: $SCRIPT_DIR/$XCFRAMEWORK_NAME" +echo "" +echo "You can now use the Swift package with Xcode or Swift Package Manager" diff --git a/drop-core/uniffi/bindings/swift/generate-bindings.sh b/drop-core/uniffi/bindings/swift/generate-bindings.sh new file mode 100755 index 00000000..d3a53e9f --- /dev/null +++ b/drop-core/uniffi/bindings/swift/generate-bindings.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -e + +# Generate Swift bindings using uniffi-bindgen +# This creates the .swift file and .modulemap for the Swift module + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +UNIFFI_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)" +WORKSPACE_ROOT="$(cd "$UNIFFI_DIR/../.." && pwd)" +OUTPUT_DIR="$SCRIPT_DIR" + +echo "Generating Swift bindings..." +echo "Workspace root: $WORKSPACE_ROOT" +echo "UniFFI directory: $UNIFFI_DIR" +echo "Output directory: $OUTPUT_DIR" + +cd "$UNIFFI_DIR" + +echo "Building library for binding generation..." +cargo build --lib --release + +LIBRARY_PATH="$WORKSPACE_ROOT/target/release/libarkdrop_uniffi.a" +if [ ! -f "$LIBRARY_PATH" ]; then + echo "ERROR: Library not found at $LIBRARY_PATH" + echo "Looking for library in target directory..." + find "$WORKSPACE_ROOT/target" -name "libarkdrop_uniffi.*" || true + exit 1 +fi + +echo "Using library: $LIBRARY_PATH" + +echo "Generating Swift sources..." +if ! cargo run --bin uniffi-bindgen-swift -- --swift-sources "$LIBRARY_PATH" "$OUTPUT_DIR"; then + echo "ERROR: Failed to generate Swift sources" + exit 1 +fi + +echo "Generating C headers..." +if ! cargo run --bin uniffi-bindgen-swift -- --headers "$LIBRARY_PATH" "$OUTPUT_DIR"; then + echo "ERROR: Failed to generate headers" + exit 1 +fi + +echo "Generating modulemap..." +if ! cargo run --bin uniffi-bindgen-swift -- --modulemap "$LIBRARY_PATH" "$OUTPUT_DIR"; then + echo "ERROR: Failed to generate modulemap" + exit 1 +fi + +echo "" +echo "Listing output directory contents:" +ls -la "$OUTPUT_DIR" | grep -E '\.(swift|h|modulemap)' || echo "Warning: No binding files found" + +if [ -f "$OUTPUT_DIR/arkdrop_uniffi.modulemap" ]; then + mv "$OUTPUT_DIR/arkdrop_uniffi.modulemap" "$OUTPUT_DIR/module.modulemap" + echo "Renamed modulemap to module.modulemap" +fi + +# Verify required files exist +echo "" +echo "Verifying generated files..." +for required_file in \ + "$OUTPUT_DIR/ArkDrop.swift" \ + "$OUTPUT_DIR/arkdrop_uniffiFFI.h" \ + "$OUTPUT_DIR/module.modulemap"; do + if [ ! -f "$required_file" ]; then + echo "ERROR: Required file not generated: $required_file" + exit 1 + else + echo "✓ Found: $required_file" + fi +done + +echo "" +echo "Swift bindings generated successfully!" diff --git a/drop-core/uniffi/uniffi-bindgen-swift.rs b/drop-core/uniffi/uniffi-bindgen-swift.rs new file mode 100644 index 00000000..5641fc6e --- /dev/null +++ b/drop-core/uniffi/uniffi-bindgen-swift.rs @@ -0,0 +1,3 @@ +fn main() { + uniffi::uniffi_bindgen_swift() +} diff --git a/drop-core/uniffi/uniffi.toml b/drop-core/uniffi/uniffi.toml index 6bdd7a50..47f12a9e 100644 --- a/drop-core/uniffi/uniffi.toml +++ b/drop-core/uniffi/uniffi.toml @@ -1,3 +1,8 @@ [bindings.kotlin] package_name = "dev.arkbuilders.drop" cdylib_name = "arkdrop_uniffi" + +[bindings.swift] +module_name = "ArkDrop" +cdylib_name = "arkdrop_uniffi" +ffi_module_name = "arkdrop_uniffiFFI"