Create Binding

Create a complete .NET MAUI native binding end-to-end: discover artifacts, fetch them, research the API, generate bindings, creat…

michalpobuta 14 updated 4mo ago
Claude CodeGeneric
View source ↗
Create a complete .NET MAUI native binding end-to-end: discover artifacts, fetch them, research the API, generate bindings, create a unified cross-platform API, generate tests, build, and iterate until everything passes.

Arguments format: `$ARGUMENTS`
Expected: `<sdk-name> <platform: ios|android|both> [type: service|view] [output-dir]`

- **type** defaults to `service` if omitted. Use `view` when the SDK provides a visual component (e.g., Lottie animation view, video player, map, ad banner).

Examples:
- `Amplitude both service ./output/amplitude`
- `Firebase ios ./output/firebase`
- `Lottie both view ./output/lottie`
- `GoogleMaps both view ./output/googlemaps`
- `Sentry android ./output/sentry` (type defaults to service)

---

## Phase 1: Setup

Parse `$ARGUMENTS`:
- **sdk-name** (required): The SDK to generate bindings for (e.g., "Amplitude", "Firebase", "Lottie")
- **platform** (required): `ios`, `android`, or `both`
- **type** (optional, defaults to `service`): `service` or `view`
  - `service` — SDK provides non-visual functionality (analytics, auth, networking, etc.)
  - `view` — SDK provides a visual component/widget (animation view, map, player, ad banner, etc.)
- **output-dir** (optional, defaults to `./output/<sdk-name-lowercase>`)

If arguments are missing or malformed, ask the user to provide them in the correct format.

Create directory structure:

/ ios/ (if platform is ios or both) android/ (if platform is android or both) unified/ (if platform is both) tests/


Use `mkdir -p` to create all needed directories.

---

## Phase 2: Fetch Artifacts

For each target platform, discover and download the native artifact. The goal is **zero user input** when possible — only ask the user if all automatic approaches fail.

### Android

1. **Search Maven Central API** — Use WebFetch on `https://search.maven.org/solrsearch/select?q=<sdk-name>&rows=10&wt=json` to find Maven coordinates (groupId, artifactId, latest version).
2. **Also WebSearch** for `<sdk-name> Android SDK Maven Central coordinates` as a fallback.
3. **Download the AAR** — Construct the Maven Central URL:
   - Convert groupId dots to slashes: `com.amplitude` → `com/amplitude`
   - URL: `https://repo1.maven.org/maven2/{groupId-path}/{artifactId}/{version}/{artifactId}-{version}.aar`
   - If `.aar` 404s, try `.jar`
   - Download with `curl -L -o <output-dir>/android/<filename> "<url>"`
4. **Check for companion/core JARs** — Kotlin-based SDKs often split their code across multiple artifacts. The main AAR may only contain the platform-specific layer, while core types (Configuration, Events, etc.) live in a separate `-core` JAR.
   - Inspect the AAR's POM file for dependencies: `https://repo1.maven.org/maven2/{groupId-path}/{artifactId}/{version}/{artifactId}-{version}.pom`
   - Look for a `*-core-*.jar` or similar artifact in the same groupId
   - Download companion JARs and add them as `<AndroidLibrary>` in the csproj
   - **This is critical**: without the core JAR, many classes (Amplitude, Configuration, Identify, BaseEvent, etc.) will be missing from the binding
5. **Verify** the download: `file <output-dir>/android/<filename>` — must be a valid archive, not an HTML error page.

### iOS

Try these approaches in order — stop at the first success:

1. **GitHub Releases** — WebSearch for `<sdk-name> iOS SDK site:github.com`. If a repo is found:
   - `gh release list --repo <owner>/<repo> --limit 5`
   - `gh release view <latest-tag> --repo <owner>/<repo> --json assets`
   - Look for `.xcframework.zip` or `.xcframework` assets. Download with `gh release download` or `curl -L`.
   - If the download is a `.zip`, unzip it and locate the `.xcframework` directory.

2. **Build from source via `swift build` + `ar` + `xcodebuild -create-xcframework`** — This is the most reliable approach when no pre-built XCFramework exists. **Do NOT use `xcodebuild archive`** as it often produces Mac Catalyst binaries instead of iOS, even with `-destination "generic/platform=iOS"`.

   ```bash
   # Clone the repo
   git clone --depth 1 --branch <tag> <repo-url> /tmp/<sdk-name>-src
   cd /tmp/<sdk-name>-src

   # Detect Xcode path — check for both Xcode.app and Xcode-beta.app
   XCODE_PATH=""
   if [ -d "/Applications/Xcode.app" ]; then
       XCODE_PATH="/Applications/Xcode.app/Contents/Developer"
   elif [ -d "/Applications/Xcode-beta.app" ]; then
       XCODE_PATH="/Applications/Xcode-beta.app/Contents/Developer"
   fi
   export DEVELOPER_DIR="$XCODE_PATH"

   # Get SDK paths
   IPHONEOS_SDK=$(xcrun --sdk iphoneos --show-sdk-path)
   IPHONESIMULATOR_SDK=$(xcrun --sdk iphonesimulator --show-sdk-path)

   # Check for SPM dependencies and resolve them
   if [ -f "Package.swift" ]; then
       swift package resolve
   fi

   # Build for device (arm64-apple-ios)
   swift build --triple arm64-apple-ios \
       --sdk "$IPHONEOS_SDK" \
       -Xswiftc "-sdk" -Xswiftc "$IPHONEOS_SDK"

   # Build for simulator (arm64-apple-ios-simulator)
   swift build --triple arm64-apple-ios-simulator \
       --sdk "$IPHONESIMULATOR_SDK" \
       -Xswiftc "-sdk" -Xswiftc "$IPHONESIMULATOR_SDK"

   # Collect all .o files and create static libraries with ar
   DEVICE_OBJECTS=$(find .build/arm64-apple-ios -name "*.o" -path "*/<SdkName>.build/*")
   ar rcs /tmp/<sdk-name>-device.a $DEVICE_OBJECTS

   SIM_OBJECTS=$(find .build/arm64-apple-ios-simulator -name "*.o" -path "*/<SdkName>.build/*")
   ar rcs /tmp/<sdk-name>-sim.a $SIM_OBJECTS

   # Collect headers — find all public .h files in the source or build
   # For ObjC projects: find headers in the source's include/Headers directory
   mkdir -p /tmp/<sdk-name>-headers
   find . -name "*.h" -path "*/include/*" -exec cp {} /tmp/<sdk-name>-headers/ \;
   # Also check build output for generated headers
   find .build -name "*.h" -path "*/Headers/*" -exec cp {} /tmp/<sdk-name>-headers/ \;

   # Create a module.modulemap
   cat > /tmp/<sdk-name>-headers/module.modulemap << 'MAPEOF'

Maintain Create Binding?

Let people know it's listed here — add the badge (live metrics, light/dark aware) or a plain link to your README or docs.

[Create Binding on getagentictools](https://getagentictools.com/loops/michalpobuta-list-aar-contents?ref=badge)