From 0f3e0655f5379ea6ff73906f9358b35495ef637a Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 01:54:59 -0500 Subject: [PATCH 01/27] Prototype: SPM distribution via prebuilt xcframework (Apple) First-pass scaffold to support Swift Package Manager on Apple platforms (the successor to CocoaPods, whose trunk goes read-only Dec 2 2026; there is no official in-repo podspec today). SPM cannot practically compile the C++ tree (CMake/Bond/sqlite/zlib/platform conditionals), so the compiled C++ core + Obj-C wrappers ship as a prebuilt MATTelemetry.xcframework (.binaryTarget) and the thin Swift layer (wrappers/swift/Sources/OneDSSwift) is compiled from source on top of the Obj-C module the xcframework vends. - Package.swift (root): binaryTarget (xcframework) + OneDSSwift source target; documents the path: -> url:+checksum: switch for releases. - tools/apple/build-xcframework.sh: per-slice static libmat.a via build-ios.sh (iOS device + simulator), lipo, then xcodebuild -create-xcframework; emits a zip + SPM checksum. - tools/apple/module.modulemap + MATTelemetry-umbrella.h: vend the `ObjCModule` Clang module the existing Swift sources already import. - tools/apple/README.md: approach, build/consume steps, release wiring, and the macOS-validation TODOs (macOS/Catalyst slices, conditional modules, header flattening, signing). NOT yet validated on macOS -- needs a mac with Xcode to run the build and adjust the static-lib path / header layout. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 65 ++++++++++++++++++++++ tools/apple/MATTelemetry-umbrella.h | 29 ++++++++++ tools/apple/README.md | 70 ++++++++++++++++++++++++ tools/apple/build-xcframework.sh | 84 +++++++++++++++++++++++++++++ tools/apple/module.modulemap | 9 ++++ 5 files changed, 257 insertions(+) create mode 100644 Package.swift create mode 100644 tools/apple/MATTelemetry-umbrella.h create mode 100644 tools/apple/README.md create mode 100755 tools/apple/build-xcframework.sh create mode 100644 tools/apple/module.modulemap diff --git a/Package.swift b/Package.swift new file mode 100644 index 000000000..ad4364e70 --- /dev/null +++ b/Package.swift @@ -0,0 +1,65 @@ +// swift-tools-version: 5.9 +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Swift Package Manager manifest for the 1DS C++ SDK (Microsoft Applications +// Telemetry) on Apple platforms. +// +// PROTOTYPE — distribution model: +// * The compiled C++ core + Obj-C wrappers ship as a prebuilt binary +// xcframework (built by tools/apple/build-xcframework.sh). This avoids +// compiling the CMake/Bond/sqlite/zlib C++ tree through SPM, which is not +// practical. +// * The thin Swift wrapper (wrappers/swift/Sources/OneDSSwift) is compiled +// from source on top of the Obj-C module vended by the xcframework. +// +// Local development: +// 1. Run `tools/apple/build-xcframework.sh release` on macOS with Xcode. +// It produces ./build/apple/MATTelemetry.xcframework. +// 2. `swift build` (or add this package as a local dependency). +// +// Release distribution (so consumers can add the repo by URL in Xcode): +// 1. Build the xcframework, zip it, and attach it to the GitHub Release. +// 2. Run `swift package compute-checksum MATTelemetry.xcframework.zip`. +// 3. Replace the `.binaryTarget(... path:)` below with the `url:`+`checksum:` +// form shown in the comment. The vcpkg-release-bump workflow pattern can be +// extended to automate steps 1-3 on each release tag. + +import PackageDescription + +let package = Package( + name: "OneDSSwift", + platforms: [ + .iOS(.v12), + .macOS(.v10_15), + ], + products: [ + .library(name: "OneDSSwift", targets: ["OneDSSwift"]), + ], + targets: [ + // Prebuilt C++ core + Obj-C wrappers. The xcframework's bundled + // module map vends the Clang module `ObjCModule` (see + // tools/apple/module.modulemap), which the Swift layer imports. + // + // For a tagged release, swap the local path for the hosted artifact: + // + // .binaryTarget( + // name: "MATTelemetry", + // url: "https://github.com/microsoft/cpp_client_telemetry/releases/download/v3.10.161.1/MATTelemetry.xcframework.zip", + // checksum: ""), + .binaryTarget( + name: "MATTelemetry", + path: "build/apple/MATTelemetry.xcframework"), + + // Thin Swift API layer (source). Depends on the Obj-C module from the + // xcframework. NOTE: the conditional source exclusions in + // wrappers/swift/Package.swift (PrivacyGuard / Sanitizer / DataViewer + // when those private modules aren't built) should be carried over here + // and kept in sync with the headers baked into the xcframework. + .target( + name: "OneDSSwift", + dependencies: ["MATTelemetry"], + path: "wrappers/swift/Sources/OneDSSwift"), + ] +) diff --git a/tools/apple/MATTelemetry-umbrella.h b/tools/apple/MATTelemetry-umbrella.h new file mode 100644 index 000000000..562509962 --- /dev/null +++ b/tools/apple/MATTelemetry-umbrella.h @@ -0,0 +1,29 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 +// +// Umbrella header for the Obj-C surface vended by MATTelemetry.xcframework. +// +// build-xcframework.sh flattens the ODW*.h headers into the framework's +// Headers/ directory, so these are imported by bare name (not by the +// ../../obj-c/ relative path the in-repo Swift bridging header uses). +// +// Keep this list in sync with: +// * the ODW*.h headers copied by tools/apple/build-xcframework.sh, and +// * the Swift sources compiled in wrappers/swift/Sources/OneDSSwift. +// +// TODO(validate on macOS): confirm the ODW headers reference each other by bare +// name (or adjust the copy step) so the flattened layout compiles cleanly. + +#import + +#import "ODWDiagnosticDataViewer.h" +#import "ODWEventProperties.h" +#import "ODWLogConfiguration.h" +#import "ODWLogger.h" +#import "ODWLogManager.h" +#import "ODWPrivacyGuard.h" +#import "ODWPrivacyGuardInitConfig.h" +#import "ODWSanitizer.h" +#import "ODWSanitizerInitConfig.h" +#import "ODWSemanticContext.h" diff --git a/tools/apple/README.md b/tools/apple/README.md new file mode 100644 index 000000000..5c5848e8f --- /dev/null +++ b/tools/apple/README.md @@ -0,0 +1,70 @@ +# Swift Package Manager (xcframework) — prototype + +**Status: prototype / not yet validated on macOS.** This is a first-pass scaffold +for distributing the 1DS C++ SDK to Apple app developers via **Swift Package +Manager (SPM)**, the successor to CocoaPods (the CocoaPods trunk goes read-only +on 2 Dec 2026, and there is no official in-repo podspec today). + +## Approach + +SPM cannot practically compile this SDK's C++ tree from source (CMake build, +Bond codegen, vendored sqlite3/zlib, heavy platform conditionals). So: + +| Layer | How it ships | +| --- | --- | +| C++ core + Obj-C wrappers (`ODW*`) | **Prebuilt binary** — `MATTelemetry.xcframework` (`.binaryTarget`) | +| Swift API (`OneDSSwift`) | **Source** — `wrappers/swift/Sources/OneDSSwift`, depends on the Obj-C module from the xcframework | + +The Obj-C wrappers already compile into `libmat.a` on Apple +(`lib/CMakeLists.txt:217`), and the Swift sources already `import ObjCModule`, +so the xcframework just needs to vend a Clang module named `ObjCModule` +(`tools/apple/module.modulemap` + `MATTelemetry-umbrella.h`). + +## Files + +| File | Purpose | +| --- | --- | +| `Package.swift` (repo root) | Distributable SPM manifest: `binaryTarget` (xcframework) + `OneDSSwift` source target | +| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice via `build-ios.sh`, lipo's the simulator archs, and assembles the xcframework with `xcodebuild -create-xcframework` | +| `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module the Swift layer imports | +| `tools/apple/MATTelemetry-umbrella.h` | Umbrella over the `ODW*.h` headers baked into the xcframework | + +## Build (on macOS) + +```bash +tools/apple/build-xcframework.sh release +# -> build/apple/MATTelemetry.xcframework +# -> build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) +swift build # resolves Package.swift against the local xcframework +``` + +## Consume + +- **Local:** point a sample app at this package directory (path dependency). +- **Released:** in Xcode, *File → Add Packages…* and enter the repo URL once the + release flow below is in place. + +## Release wiring (to make it URL-consumable) + +1. Build the xcframework and zip it (the script does both). +2. Attach `MATTelemetry.xcframework.zip` to the GitHub Release for the tag. +3. Switch the `.binaryTarget` in `Package.swift` from `path:` to + `url:`+`checksum:` (the `compute-checksum` value the script prints). + +This mirrors the `vcpkg-release-bump` workflow: a release-triggered job can build +the xcframework, upload it to the Release, and bump the `binaryTarget` URL + +checksum automatically. + +## Known gaps / TODO (validate on macOS) + +- **macOS / Catalyst / visionOS slices** — only iOS device + simulator are wired + up in this first pass; add the macOS slice (see section 3 of the script). +- **Conditional modules** — carry over the `moduleExists()` source exclusions + from `wrappers/swift/Package.swift` (PrivacyGuard / Sanitizer / DataViewer) and + keep the umbrella header in sync with the headers actually built. +- **Header flattening** — confirm the `ODW*.h` headers reference each other by + bare name in the flattened `Headers/` layout (adjust the copy step if not). +- **Static-lib path** — verify `out/lib/libmat.a` is the actual artifact name and + that `-DBUILD_SHARED_LIBS=OFF` yields a static archive for every slice. +- **Code signing** — release xcframeworks are typically signed; add a signing + step before zipping for distribution. diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh new file mode 100755 index 000000000..728a90dae --- /dev/null +++ b/tools/apple/build-xcframework.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 +# +# PROTOTYPE: build MATTelemetry.xcframework (1DS C++ core + Obj-C wrappers) for +# Apple platforms, for Swift Package Manager distribution. +# +# Run on macOS with Xcode + CMake installed. Usage: +# tools/apple/build-xcframework.sh [release|debug] +# +# Produces: +# build/apple/MATTelemetry.xcframework +# build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) +# +# Slices built here: iOS device (arm64) and iOS simulator (arm64 + x86_64 fat). +# A macOS slice (and visionOS, Catalyst) can be folded in the same way -- see +# the note in section 3. +# +# NOTE: this is a first-pass scaffold. It has NOT been executed on macOS yet; +# validate on a mac and adjust the static-lib path / header flattening as needed. + +set -euo pipefail + +CONFIG="${1:-release}" +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +OUT="$ROOT/build/apple" +LIB="libmat.a" # mat target; the Obj-C wrappers compile into it (lib/CMakeLists.txt:217) + +# Force a STATIC libmat that includes the Obj-C wrappers, regardless of the +# repo's default library type. +export CMAKE_OPTS="-DBUILD_SHARED_LIBS=OFF -DBUILD_OBJC_WRAPPER=YES ${CMAKE_OPTS:-}" + +rm -rf "$OUT" +mkdir -p "$OUT" + +# --- 1. Public Obj-C headers + module map (vended by the xcframework) -------- +# Flatten the ODW*.h headers + umbrella + modulemap into one Headers dir. The +# module is named `ObjCModule` to match what wrappers/swift sources import. +HDRS="$OUT/Headers" +mkdir -p "$HDRS" +cp "$ROOT"/wrappers/obj-c/ODW*.h "$HDRS/" +cp "$ROOT"/tools/apple/MATTelemetry-umbrella.h "$HDRS/" +cp "$ROOT"/tools/apple/module.modulemap "$HDRS/" + +# --- 2. Build one static lib per (arch, platform) ---------------------------- +build_slice() { # arch platform out-subdir + local arch="$1" plat="$2" sub="$3" + echo "=== building $arch / $plat ($CONFIG) ===" + ( cd "$ROOT" && ./build-ios.sh clean "$CONFIG" "$arch" "$plat" ) + mkdir -p "$OUT/$sub" + cp "$ROOT/out/lib/$LIB" "$OUT/$sub/$LIB" +} + +build_slice arm64 iphoneos ios-arm64 +build_slice arm64 iphonesimulator ios-arm64-sim +build_slice x86_64 iphonesimulator ios-x86_64-sim + +# Fat simulator archive (arm64 + x86_64) -- a single xcframework slice cannot +# mix device and simulator, but it can contain multiple archs for one platform. +mkdir -p "$OUT/ios-simulator" +lipo -create "$OUT/ios-arm64-sim/$LIB" "$OUT/ios-x86_64-sim/$LIB" \ + -output "$OUT/ios-simulator/$LIB" + +# --- 3. (Optional) macOS slice ------------------------------------------------ +# Add a native macOS build (e.g. cmake -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" +# -DBUILD_APPLE_HTTP=YES) here and append another `-library .../libmat.a +# -headers "$HDRS"` pair to the xcodebuild call below. Omitted from this first +# pass to keep the prototype focused on iOS. + +# --- 4. Assemble the xcframework --------------------------------------------- +rm -rf "$OUT/MATTelemetry.xcframework" +xcodebuild -create-xcframework \ + -library "$OUT/ios-arm64/$LIB" -headers "$HDRS" \ + -library "$OUT/ios-simulator/$LIB" -headers "$HDRS" \ + -output "$OUT/MATTelemetry.xcframework" +echo "Created $OUT/MATTelemetry.xcframework" + +# --- 5. Zip + checksum for release distribution ------------------------------ +( cd "$OUT" && rm -f MATTelemetry.xcframework.zip \ + && zip -qry MATTelemetry.xcframework.zip MATTelemetry.xcframework ) +echo "Zipped: $OUT/MATTelemetry.xcframework.zip" +echo -n "SPM checksum (for Package.swift binaryTarget url: form): " +swift package compute-checksum "$OUT/MATTelemetry.xcframework.zip" diff --git a/tools/apple/module.modulemap b/tools/apple/module.modulemap new file mode 100644 index 000000000..0a2c0167a --- /dev/null +++ b/tools/apple/module.modulemap @@ -0,0 +1,9 @@ +// Clang module vended by MATTelemetry.xcframework. Imported by the OneDSSwift +// Swift layer as `import ObjCModule` -- the module name matches what the +// existing wrappers/swift/Sources/OneDSSwift sources already import, so no Swift +// source changes are needed. + +module ObjCModule { + umbrella header "MATTelemetry-umbrella.h" + export * +} From 5e3687e2b016b8b1cbfdeb530748fb5d1a39854d Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 02:36:21 -0500 Subject: [PATCH 02/27] Prototype: SPM release workflow + parallel 3-component SemVer tag Adds .github/workflows/spm-release.yml: on a published 4-component release (vX.Y.Z.W), a macOS-runner job builds MATTelemetry.xcframework, uploads it to the Release, computes the SPM checksum, rewrites the Package.swift binaryTarget from path: to url:+checksum:, and pushes a 3-component SemVer tag (X.Y.Z) that Swift Package Manager can resolve (the SDK's own 4-component tags are not valid SemVer, so SPM ignores them). Also documents the parallel-tag consumption (`from: "3.10.161"`) and the release flow in tools/apple/README.md. Mirrors the vcpkg-release-bump pattern. NOT yet validated -- needs the prototype merged (so Package.swift exists at the release tag) and a macOS runner; the build script itself still needs a first run on a mac. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 141 ++++++++++++++++++++++++++++++ tools/apple/README.md | 37 +++++--- 2 files changed, 165 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/spm-release.yml diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml new file mode 100644 index 000000000..cd8c1c670 --- /dev/null +++ b/.github/workflows/spm-release.yml @@ -0,0 +1,141 @@ +name: SPM release (xcframework) + +# On a published SDK release, build MATTelemetry.xcframework, upload it to the +# GitHub Release, and publish a 3-component SemVer tag for Swift Package Manager +# whose Package.swift binaryTarget points at the uploaded artifact + checksum. +# +# Why a separate tag: the SDK's own release tags are 4-component (vX.Y.Z.W), +# which is NOT valid SemVer, so Swift Package Manager ignores them. This derives +# a 3-component tag (X.Y.Z) from the same release that SPM can resolve. +# +# Prerequisites: +# * The root Package.swift (the SPM manifest) must exist at the release tag +# (i.e. this prototype merged to main before the release is cut). +# * Uses the default GITHUB_TOKEN (needs contents: write). No extra secrets. + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "4-component release tag to publish for SPM (e.g. v3.10.161.1)" + required: true + type: string + +permissions: + contents: write + +concurrency: + group: spm-release-${{ github.event.release.tag_name || inputs.tag }} + cancel-in-progress: false + +jobs: + spm: + name: Publish SPM xcframework + tag + # Skip drafts/pre-releases; always allow manual dispatch. + if: >- + ${{ github.event_name == 'workflow_dispatch' || + (github.event.release.draft == false && github.event.release.prerelease == false) }} + runs-on: macos-14 # provides Xcode (xcodebuild, swift) + env: + ARTIFACT: MATTelemetry.xcframework.zip + steps: + - name: Resolve tag and derive SPM version + id: ver + env: + # Pass untrusted tag values through the environment rather than + # interpolating ${{ ... }} into the script body. + RELEASE_TAG: ${{ github.event.release.tag_name }} + INPUT_TAG: ${{ inputs.tag }} + run: | + set -euo pipefail + TAG="${RELEASE_TAG:-$INPUT_TAG}" + if [ -z "$TAG" ]; then echo "::error::No release tag could be resolved."; exit 1; fi + # Only act on 4-component version tags vX.Y.Z.W. + if ! printf '%s' "$TAG" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "::error::Tag '$TAG' is not a 4-component version tag (expected vX.Y.Z.W)." + exit 1 + fi + echo "::notice::Tag '$TAG' is not a 4-component version tag; nothing to publish." + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + VERSION="${TAG#v}" # X.Y.Z.W + SPM_VERSION="${VERSION%.*}" # X.Y.Z (drop the trailing build component) + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "spm_version=$SPM_VERSION" >> "$GITHUB_OUTPUT" + echo "Release $TAG -> SPM tag $SPM_VERSION" + + - name: Checkout the release tag + if: ${{ steps.ver.outputs.skip != 'true' }} + uses: actions/checkout@v4 + with: + ref: ${{ steps.ver.outputs.tag }} + fetch-depth: 0 + # The private lib/modules submodule is intentionally NOT fetched; the + # xcframework ships the core SDK + Obj-C wrappers, matching the vcpkg + # port (the optional modules are excluded there too). + submodules: false + + - name: Build MATTelemetry.xcframework + if: ${{ steps.ver.outputs.skip != 'true' }} + run: | + set -euo pipefail + chmod +x tools/apple/build-xcframework.sh + tools/apple/build-xcframework.sh release + test -f "build/apple/$ARTIFACT" + + - name: Compute SPM checksum + id: sum + if: ${{ steps.ver.outputs.skip != 'true' }} + run: | + set -euo pipefail + echo "checksum=$(swift package compute-checksum "build/apple/$ARTIFACT")" >> "$GITHUB_OUTPUT" + + - name: Upload xcframework to the release + if: ${{ steps.ver.outputs.skip != 'true' }} + env: + GH_TOKEN: ${{ github.token }} + run: gh release upload "${{ steps.ver.outputs.tag }}" "build/apple/$ARTIFACT" --clobber + + - name: Point Package.swift at the released artifact + if: ${{ steps.ver.outputs.skip != 'true' }} + env: + ASSET_URL: https://github.com/${{ github.repository }}/releases/download/${{ steps.ver.outputs.tag }}/MATTelemetry.xcframework.zip + CHECKSUM: ${{ steps.sum.outputs.checksum }} + run: | + set -euo pipefail + python3 - "$ASSET_URL" "$CHECKSUM" <<'PY' + import re, sys + url, checksum = sys.argv[1], sys.argv[2] + path = "Package.swift" + src = open(path).read() + repl = ( + '.binaryTarget(\n' + ' name: "MATTelemetry",\n' + f' url: "{url}",\n' + f' checksum: "{checksum}")' + ) + out = re.sub( + r'\.binaryTarget\(\s*name:\s*"MATTelemetry",\s*path:\s*"[^"]*"\s*\)', + repl, src, count=1) + assert out != src, "binaryTarget(path:) block not found in Package.swift" + open(path, "w").write(out) + PY + + - name: Commit manifest and push the 3-component SPM tag + if: ${{ steps.ver.outputs.skip != 'true' }} + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add Package.swift + git commit -m "[spm] ${{ steps.ver.outputs.spm_version }}: pin xcframework url + checksum" + # The SPM tag points at this commit (release source + resolved + # binaryTarget). It is published as a tag only, not merged to a branch. + git tag -a "${{ steps.ver.outputs.spm_version }}" \ + -m "Swift Package Manager release ${{ steps.ver.outputs.spm_version }} (from ${{ steps.ver.outputs.tag }})" + git push origin "refs/tags/${{ steps.ver.outputs.spm_version }}" + echo "Published SPM tag ${{ steps.ver.outputs.spm_version }}" diff --git a/tools/apple/README.md b/tools/apple/README.md index 5c5848e8f..90cb53452 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -41,19 +41,30 @@ swift build # resolves Package.swift against the local xcframework ## Consume - **Local:** point a sample app at this package directory (path dependency). -- **Released:** in Xcode, *File → Add Packages…* and enter the repo URL once the - release flow below is in place. - -## Release wiring (to make it URL-consumable) - -1. Build the xcframework and zip it (the script does both). -2. Attach `MATTelemetry.xcframework.zip` to the GitHub Release for the tag. -3. Switch the `.binaryTarget` in `Package.swift` from `path:` to - `url:`+`checksum:` (the `compute-checksum` value the script prints). - -This mirrors the `vcpkg-release-bump` workflow: a release-triggered job can build -the xcframework, upload it to the Release, and bump the `binaryTarget` URL + -checksum automatically. +- **Released:** in Xcode *File -> Add Package Dependencies...*, enter the repo + URL and pick a version. SPM only accepts **3-component SemVer**, and the SDK's + own `vX.Y.Z.W` tags are not valid SemVer, so consumers pin the **parallel + 3-component tag** the release workflow publishes: + + ```swift + .package(url: "https://github.com/microsoft/cpp_client_telemetry.git", from: "3.10.161") + ``` + +## Release wiring + +`.github/workflows/spm-release.yml` automates distribution on each published +release (a 4-component `vX.Y.Z.W` tag). On a macOS runner it: + +1. Builds `MATTelemetry.xcframework` and zips it. +2. Uploads the zip to the GitHub Release. +3. Computes the SPM checksum and rewrites the `Package.swift` `binaryTarget` + from `path:` to `url:`+`checksum:`. +4. Commits that manifest and pushes a **3-component SemVer tag** (`X.Y.Z`, + derived by dropping the trailing build component) that SPM can resolve. + +This mirrors the `vcpkg-release-bump` workflow. It requires the root +`Package.swift` to already exist at the release tag (i.e. this prototype merged +before the release is cut). ## Known gaps / TODO (validate on macOS) From ba7ba4cb3c0a1ac3b6d84b466030cfffd9c3a79a Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 13:38:08 -0500 Subject: [PATCH 03/27] Fix local SPM xcframework consumption Copy objc_begin/end support headers into the flattened xcframework Headers directory and mirror the existing Swift wrapper optional-module source exclusions in the root package manifest. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 39 +++++++++++++++++++++++++++++++- tools/apple/build-xcframework.sh | 2 ++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index ad4364e70..2e3a51ba7 100644 --- a/Package.swift +++ b/Package.swift @@ -27,6 +27,41 @@ // extended to automate steps 1-3 on each release tag. import PackageDescription +import Foundation + +let packageDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent() + +func moduleExists(_ relativePath: String) -> Bool { + FileManager.default.fileExists(atPath: packageDirectory.appendingPathComponent(relativePath).standardizedFileURL.path) +} + +let hasDiagnosticDataViewer = moduleExists("lib/modules/dataviewer") +let hasPrivacyGuard = moduleExists("lib/modules/privacyguard") +let hasSanitizer = moduleExists("lib/modules/sanitizer") + +var excludedSources: [String] = [] +var swiftSettings: [SwiftSetting] = [] + +if !hasDiagnosticDataViewer { + excludedSources.append("DiagnosticDataViewer.swift") +} + +if hasPrivacyGuard { + swiftSettings.append(.define("MATSDK_PRIVACYGUARD_AVAILABLE")) +} else { + excludedSources.append(contentsOf: [ + "CommonDataContext.swift", + "PrivacyGuard.swift", + "PrivacyGuardInitConfig.swift", + ]) +} + +if !hasSanitizer { + excludedSources.append(contentsOf: [ + "Sanitizer.swift", + "SanitizerInitConfig.swift", + ]) +} let package = Package( name: "OneDSSwift", @@ -60,6 +95,8 @@ let package = Package( .target( name: "OneDSSwift", dependencies: ["MATTelemetry"], - path: "wrappers/swift/Sources/OneDSSwift"), + path: "wrappers/swift/Sources/OneDSSwift", + exclude: excludedSources, + swiftSettings: swiftSettings), ] ) diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 728a90dae..342f9b29a 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -40,6 +40,8 @@ mkdir -p "$OUT" HDRS="$OUT/Headers" mkdir -p "$HDRS" cp "$ROOT"/wrappers/obj-c/ODW*.h "$HDRS/" +cp "$ROOT"/wrappers/obj-c/objc_begin.h "$HDRS/" +cp "$ROOT"/wrappers/obj-c/objc_end.h "$HDRS/" cp "$ROOT"/tools/apple/MATTelemetry-umbrella.h "$HDRS/" cp "$ROOT"/tools/apple/module.modulemap "$HDRS/" From 308e03110a3da3fc92d4af3da292e093c03701aa Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 13:51:49 -0500 Subject: [PATCH 04/27] Copy only public ObjC headers into xcframework Avoid vending private wrapper headers from the flattened MATTelemetry.xcframework Headers directory, which otherwise triggers incomplete umbrella warnings when ObjCModule is imported. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/build-xcframework.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 342f9b29a..5f4e39634 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -39,7 +39,7 @@ mkdir -p "$OUT" # module is named `ObjCModule` to match what wrappers/swift sources import. HDRS="$OUT/Headers" mkdir -p "$HDRS" -cp "$ROOT"/wrappers/obj-c/ODW*.h "$HDRS/" +find "$ROOT/wrappers/obj-c" -maxdepth 1 -name 'ODW*.h' ! -name '*_private.h' -exec cp {} "$HDRS/" \; cp "$ROOT"/wrappers/obj-c/objc_begin.h "$HDRS/" cp "$ROOT"/wrappers/obj-c/objc_end.h "$HDRS/" cp "$ROOT"/tools/apple/MATTelemetry-umbrella.h "$HDRS/" From dd972345632c70f9e1b919e8c99b45c51f38f850 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 17:47:06 -0500 Subject: [PATCH 05/27] Align SPM package with xcframework contents Generate the ObjC umbrella and availability manifest from the modules built into the xcframework, read that manifest from Package.swift, and guard Swift type aliases for optional modules. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 2 +- Package.swift | 25 ++++++++-- tools/apple/MATTelemetry-umbrella.h | 12 ++--- tools/apple/MATTelemetryAvailability.json | 5 ++ tools/apple/build-xcframework.sh | 46 ++++++++++++++++++- .../swift/Sources/OneDSSwift/ObjCTypes.swift | 2 + 6 files changed, 75 insertions(+), 17 deletions(-) create mode 100644 tools/apple/MATTelemetryAvailability.json diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml index cd8c1c670..28eb1f414 100644 --- a/.github/workflows/spm-release.yml +++ b/.github/workflows/spm-release.yml @@ -131,7 +131,7 @@ jobs: set -euo pipefail git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - git add Package.swift + git add Package.swift tools/apple/MATTelemetryAvailability.json git commit -m "[spm] ${{ steps.ver.outputs.spm_version }}: pin xcframework url + checksum" # The SPM tag points at this commit (release source + resolved # binaryTarget). It is published as a tag only, not merged to a branch. diff --git a/Package.swift b/Package.swift index 2e3a51ba7..773733ba2 100644 --- a/Package.swift +++ b/Package.swift @@ -31,13 +31,28 @@ import Foundation let packageDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent() -func moduleExists(_ relativePath: String) -> Bool { - FileManager.default.fileExists(atPath: packageDirectory.appendingPathComponent(relativePath).standardizedFileURL.path) +func readAvailability() -> [String: Bool] { + let candidates = [ + "build/apple/MATTelemetryAvailability.json", + "tools/apple/MATTelemetryAvailability.json", + ] + + for relativePath in candidates { + let url = packageDirectory.appendingPathComponent(relativePath).standardizedFileURL + guard let data = try? Data(contentsOf: url), + let object = try? JSONSerialization.jsonObject(with: data) as? [String: Bool] else { + continue + } + return object + } + + return [:] } -let hasDiagnosticDataViewer = moduleExists("lib/modules/dataviewer") -let hasPrivacyGuard = moduleExists("lib/modules/privacyguard") -let hasSanitizer = moduleExists("lib/modules/sanitizer") +let availability = readAvailability() +let hasDiagnosticDataViewer = availability["diagnosticDataViewer"] ?? false +let hasPrivacyGuard = availability["privacyGuard"] ?? false +let hasSanitizer = availability["sanitizer"] ?? false var excludedSources: [String] = [] var swiftSettings: [SwiftSetting] = [] diff --git a/tools/apple/MATTelemetry-umbrella.h b/tools/apple/MATTelemetry-umbrella.h index 562509962..734ceb07f 100644 --- a/tools/apple/MATTelemetry-umbrella.h +++ b/tools/apple/MATTelemetry-umbrella.h @@ -8,22 +8,16 @@ // Headers/ directory, so these are imported by bare name (not by the // ../../obj-c/ relative path the in-repo Swift bridging header uses). // -// Keep this list in sync with: -// * the ODW*.h headers copied by tools/apple/build-xcframework.sh, and -// * the Swift sources compiled in wrappers/swift/Sources/OneDSSwift. -// -// TODO(validate on macOS): confirm the ODW headers reference each other by bare -// name (or adjust the copy step) so the flattened layout compiles cleanly. +// build-xcframework.sh copies this template and appends imports for optional +// module headers only when those modules were actually built into the binary. #import -#import "ODWDiagnosticDataViewer.h" +#import "ODWCommonDataContext.h" #import "ODWEventProperties.h" #import "ODWLogConfiguration.h" #import "ODWLogger.h" #import "ODWLogManager.h" -#import "ODWPrivacyGuard.h" #import "ODWPrivacyGuardInitConfig.h" -#import "ODWSanitizer.h" #import "ODWSanitizerInitConfig.h" #import "ODWSemanticContext.h" diff --git a/tools/apple/MATTelemetryAvailability.json b/tools/apple/MATTelemetryAvailability.json new file mode 100644 index 000000000..34cb38831 --- /dev/null +++ b/tools/apple/MATTelemetryAvailability.json @@ -0,0 +1,5 @@ +{ + "diagnosticDataViewer": false, + "privacyGuard": false, + "sanitizer": false +} diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 5f4e39634..29e23edf0 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -39,12 +39,54 @@ mkdir -p "$OUT" # module is named `ObjCModule` to match what wrappers/swift sources import. HDRS="$OUT/Headers" mkdir -p "$HDRS" -find "$ROOT/wrappers/obj-c" -maxdepth 1 -name 'ODW*.h' ! -name '*_private.h' -exec cp {} "$HDRS/" \; + +has_dataviewer=false +has_privacyguard=false +has_sanitizer=false +[[ -d "$ROOT/lib/modules/dataviewer" ]] && has_dataviewer=true +[[ -d "$ROOT/lib/modules/privacyguard" ]] && has_privacyguard=true +[[ -d "$ROOT/lib/modules/sanitizer" ]] && has_sanitizer=true + +cat > "$OUT/MATTelemetryAvailability.json" <> "$HDRS/MATTelemetry-umbrella.h" + # --- 2. Build one static lib per (arch, platform) ---------------------------- build_slice() { # arch platform out-subdir local arch="$1" plat="$2" sub="$3" diff --git a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift index 1042a42e1..c0c1ad4e0 100644 --- a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift +++ b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift @@ -27,4 +27,6 @@ public typealias TransmissionProfile = ODWTransmissionProfile public typealias FlushStatus = ODWStatus // ODWPrivacyGuard.h +#if MATSDK_PRIVACYGUARD_AVAILABLE public typealias DataConcernType = ODWDataConcernType +#endif From accb600cb6f44c3d1f447e114127dda414bd55bc Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 18:00:27 -0500 Subject: [PATCH 06/27] Generate SPM availability from xcframework build Drive Swift source exclusions and ObjC umbrella optional imports from the modules actually built into MATTelemetry.xcframework, remove unsupported macOS package advertising, and add Apple system linker settings for the static binary target. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 14 ++++++++++++-- tools/apple/MATTelemetry-umbrella.h | 5 +++-- tools/apple/build-xcframework.sh | 12 +++++++----- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Package.swift b/Package.swift index 773733ba2..2ff67d8ee 100644 --- a/Package.swift +++ b/Package.swift @@ -82,7 +82,6 @@ let package = Package( name: "OneDSSwift", platforms: [ .iOS(.v12), - .macOS(.v10_15), ], products: [ .library(name: "OneDSSwift", targets: ["OneDSSwift"]), @@ -112,6 +111,17 @@ let package = Package( dependencies: ["MATTelemetry"], path: "wrappers/swift/Sources/OneDSSwift", exclude: excludedSources, - swiftSettings: swiftSettings), + swiftSettings: swiftSettings, + linkerSettings: [ + .linkedLibrary("sqlite3"), + .linkedLibrary("z"), + .linkedFramework("CFNetwork"), + .linkedFramework("CoreFoundation"), + .linkedFramework("Foundation"), + .linkedFramework("IOKit"), + .linkedFramework("Network"), + .linkedFramework("SystemConfiguration"), + .linkedFramework("UIKit"), + ]), ] ) diff --git a/tools/apple/MATTelemetry-umbrella.h b/tools/apple/MATTelemetry-umbrella.h index 734ceb07f..8d4aaf952 100644 --- a/tools/apple/MATTelemetry-umbrella.h +++ b/tools/apple/MATTelemetry-umbrella.h @@ -8,8 +8,9 @@ // Headers/ directory, so these are imported by bare name (not by the // ../../obj-c/ relative path the in-repo Swift bridging header uses). // -// build-xcframework.sh copies this template and appends imports for optional -// module headers only when those modules were actually built into the binary. +// This template intentionally lists only always-available Obj-C headers. +// build-xcframework.sh appends imports for optional module headers only when +// those modules were actually built into the binary. #import diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 29e23edf0..36bc5238f 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -88,17 +88,19 @@ cp "$ROOT"/tools/apple/MATTelemetry-umbrella.h "$HDRS/" } >> "$HDRS/MATTelemetry-umbrella.h" # --- 2. Build one static lib per (arch, platform) ---------------------------- -build_slice() { # arch platform out-subdir +build_slice() { # clean-arg arch platform out-subdir + local clean_arg="$1" + shift local arch="$1" plat="$2" sub="$3" echo "=== building $arch / $plat ($CONFIG) ===" - ( cd "$ROOT" && ./build-ios.sh clean "$CONFIG" "$arch" "$plat" ) + ( cd "$ROOT" && ./build-ios.sh $clean_arg "$CONFIG" "$arch" "$plat" ) mkdir -p "$OUT/$sub" cp "$ROOT/out/lib/$LIB" "$OUT/$sub/$LIB" } -build_slice arm64 iphoneos ios-arm64 -build_slice arm64 iphonesimulator ios-arm64-sim -build_slice x86_64 iphonesimulator ios-x86_64-sim +build_slice clean arm64 iphoneos ios-arm64 +build_slice "" arm64 iphonesimulator ios-arm64-sim +build_slice "" x86_64 iphonesimulator ios-x86_64-sim # Fat simulator archive (arm64 + x86_64) -- a single xcframework slice cannot # mix device and simulator, but it can contain multiple archs for one platform. From 4e03dff20e8da5ae54e5bdfca7ca5ea3591d631f Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 18:23:17 -0500 Subject: [PATCH 07/27] Address SPM prototype review refinements Remove macOS package advertising until a macOS slice exists, add iOS linker settings for the static xcframework, and avoid repeating build tool setup for each slice. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 14 +++++++------- tools/apple/build-xcframework.sh | 4 +++- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/Package.swift b/Package.swift index 2ff67d8ee..ace48f629 100644 --- a/Package.swift +++ b/Package.swift @@ -113,15 +113,15 @@ let package = Package( exclude: excludedSources, swiftSettings: swiftSettings, linkerSettings: [ + .linkedLibrary("c++"), .linkedLibrary("sqlite3"), .linkedLibrary("z"), - .linkedFramework("CFNetwork"), - .linkedFramework("CoreFoundation"), - .linkedFramework("Foundation"), - .linkedFramework("IOKit"), - .linkedFramework("Network"), - .linkedFramework("SystemConfiguration"), - .linkedFramework("UIKit"), + .linkedFramework("CFNetwork", .when(platforms: [.iOS])), + .linkedFramework("CoreFoundation", .when(platforms: [.iOS])), + .linkedFramework("Foundation", .when(platforms: [.iOS])), + .linkedFramework("Network", .when(platforms: [.iOS])), + .linkedFramework("SystemConfiguration", .when(platforms: [.iOS])), + .linkedFramework("UIKit", .when(platforms: [.iOS])), ]), ] ) diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 36bc5238f..9f531ce71 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -54,7 +54,9 @@ cat > "$OUT/MATTelemetryAvailability.json" < Date: Thu, 18 Jun 2026 18:45:27 -0500 Subject: [PATCH 08/27] Align Apple SPM docs and module availability Mark the prototype as validated, document remaining gaps, and make optional module availability respect explicit BUILD_PRIVACYGUARD/BUILD_SANITIZER settings passed through CMAKE_OPTS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/README.md | 29 +++++++++++++++++------------ tools/apple/build-xcframework.sh | 30 ++++++++++++++++++++++++++---- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/tools/apple/README.md b/tools/apple/README.md index 90cb53452..162043957 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -1,9 +1,9 @@ # Swift Package Manager (xcframework) — prototype -**Status: prototype / not yet validated on macOS.** This is a first-pass scaffold -for distributing the 1DS C++ SDK to Apple app developers via **Swift Package -Manager (SPM)**, the successor to CocoaPods (the CocoaPods trunk goes read-only -on 2 Dec 2026, and there is no official in-repo podspec today). +**Status: validated prototype.** This is a first-pass scaffold for distributing +the 1DS C++ SDK to Apple app developers via **Swift Package Manager (SPM)**, the +successor to CocoaPods (the CocoaPods trunk goes read-only on 2 Dec 2026, and +there is no official in-repo podspec today). ## Approach @@ -28,6 +28,7 @@ so the xcframework just needs to vend a Clang module named `ObjCModule` | `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice via `build-ios.sh`, lipo's the simulator archs, and assembles the xcframework with `xcodebuild -create-xcframework` | | `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module the Swift layer imports | | `tools/apple/MATTelemetry-umbrella.h` | Umbrella over the `ODW*.h` headers baked into the xcframework | +| `tools/apple/MATTelemetryAvailability.json` | Build-time optional-module manifest consumed by `Package.swift` so Swift sources match the xcframework contents | ## Build (on macOS) @@ -66,16 +67,20 @@ This mirrors the `vcpkg-release-bump` workflow. It requires the root `Package.swift` to already exist at the release tag (i.e. this prototype merged before the release is cut). -## Known gaps / TODO (validate on macOS) +## Validation performed + +- `tools/apple/build-xcframework.sh release` builds the iOS device and simulator + slices and prints the SPM checksum. +- `xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build` + validates local SwiftPM consumption. +- A small Obj-C module/static-link smoke test was built and run on an iOS + Simulator. + +## Known gaps / TODO - **macOS / Catalyst / visionOS slices** — only iOS device + simulator are wired up in this first pass; add the macOS slice (see section 3 of the script). -- **Conditional modules** — carry over the `moduleExists()` source exclusions - from `wrappers/swift/Package.swift` (PrivacyGuard / Sanitizer / DataViewer) and - keep the umbrella header in sync with the headers actually built. -- **Header flattening** — confirm the `ODW*.h` headers reference each other by - bare name in the flattened `Headers/` layout (adjust the copy step if not). -- **Static-lib path** — verify `out/lib/libmat.a` is the actual artifact name and - that `-DBUILD_SHARED_LIBS=OFF` yields a static archive for every slice. - **Code signing** — release xcframeworks are typically signed; add a signing step before zipping for distribution. +- **Release workflow validation** — exercise `.github/workflows/spm-release.yml` + end-to-end on an actual published release. diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 9f531ce71..37dfae1c4 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -17,8 +17,8 @@ # A macOS slice (and visionOS, Catalyst) can be folded in the same way -- see # the note in section 3. # -# NOTE: this is a first-pass scaffold. It has NOT been executed on macOS yet; -# validate on a mac and adjust the static-lib path / header flattening as needed. +# NOTE: this is a first-pass scaffold. It has been validated on macOS for iOS +# device + simulator slices; macOS/Catalyst/visionOS slices are still TODO. set -euo pipefail @@ -43,9 +43,31 @@ mkdir -p "$HDRS" has_dataviewer=false has_privacyguard=false has_sanitizer=false + +cmake_option_enabled() { # option-name default-value + local option="$1" + local value="$2" + local token + for token in $CMAKE_OPTS; do + case "$token" in + -D${option}=*) value="${token#*=}" ;; + -D${option}) value=ON ;; + esac + done + value="$(printf '%s' "$value" | tr '[:upper:]' '[:lower:]')" + case "$value" in + 0|false|no|off) return 1 ;; + *) return 0 ;; + esac +} + [[ -d "$ROOT/lib/modules/dataviewer" ]] && has_dataviewer=true -[[ -d "$ROOT/lib/modules/privacyguard" ]] && has_privacyguard=true -[[ -d "$ROOT/lib/modules/sanitizer" ]] && has_sanitizer=true +if [[ -d "$ROOT/lib/modules/privacyguard" ]] && cmake_option_enabled BUILD_PRIVACYGUARD ON; then + has_privacyguard=true +fi +if [[ -d "$ROOT/lib/modules/sanitizer" ]] && cmake_option_enabled BUILD_SANITIZER ON; then + has_sanitizer=true +fi cat > "$OUT/MATTelemetryAvailability.json" < Date: Thu, 18 Jun 2026 21:21:17 -0500 Subject: [PATCH 09/27] Add macOS slice to SPM xcframework Build a universal macOS libmat archive alongside the iOS device and simulator slices, advertise macOS in the root Swift package, and document the expanded validation story. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 15 ++++++----- tools/apple/README.md | 10 +++++--- tools/apple/build-xcframework.sh | 43 +++++++++++++++++++++++++------- 3 files changed, 49 insertions(+), 19 deletions(-) diff --git a/Package.swift b/Package.swift index ace48f629..fe7385189 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,8 @@ // Local development: // 1. Run `tools/apple/build-xcframework.sh release` on macOS with Xcode. // It produces ./build/apple/MATTelemetry.xcframework. -// 2. `swift build` (or add this package as a local dependency). +// 2. `swift build` validates macOS consumption; for iOS, add this package as a +// local dependency or build the package with an iOS Simulator destination. // // Release distribution (so consumers can add the repo by URL in Xcode): // 1. Build the xcframework, zip it, and attach it to the GitHub Release. @@ -82,6 +83,7 @@ let package = Package( name: "OneDSSwift", platforms: [ .iOS(.v12), + .macOS(.v10_15), ], products: [ .library(name: "OneDSSwift", targets: ["OneDSSwift"]), @@ -116,11 +118,12 @@ let package = Package( .linkedLibrary("c++"), .linkedLibrary("sqlite3"), .linkedLibrary("z"), - .linkedFramework("CFNetwork", .when(platforms: [.iOS])), - .linkedFramework("CoreFoundation", .when(platforms: [.iOS])), - .linkedFramework("Foundation", .when(platforms: [.iOS])), - .linkedFramework("Network", .when(platforms: [.iOS])), - .linkedFramework("SystemConfiguration", .when(platforms: [.iOS])), + .linkedFramework("CFNetwork", .when(platforms: [.iOS, .macOS])), + .linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macOS])), + .linkedFramework("Foundation", .when(platforms: [.iOS, .macOS])), + .linkedFramework("Network", .when(platforms: [.iOS, .macOS])), + .linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macOS])), + .linkedFramework("IOKit", .when(platforms: [.macOS])), .linkedFramework("UIKit", .when(platforms: [.iOS])), ]), ] diff --git a/tools/apple/README.md b/tools/apple/README.md index 162043957..c2d98771d 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -25,7 +25,7 @@ so the xcframework just needs to vend a Clang module named `ObjCModule` | File | Purpose | | --- | --- | | `Package.swift` (repo root) | Distributable SPM manifest: `binaryTarget` (xcframework) + `OneDSSwift` source target | -| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice via `build-ios.sh`, lipo's the simulator archs, and assembles the xcframework with `xcodebuild -create-xcframework` | +| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice, lipo's the simulator/macOS archs where needed, and assembles the xcframework with `xcodebuild -create-xcframework` | | `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module the Swift layer imports | | `tools/apple/MATTelemetry-umbrella.h` | Umbrella over the `ODW*.h` headers baked into the xcframework | | `tools/apple/MATTelemetryAvailability.json` | Build-time optional-module manifest consumed by `Package.swift` so Swift sources match the xcframework contents | @@ -70,7 +70,8 @@ before the release is cut). ## Validation performed - `tools/apple/build-xcframework.sh release` builds the iOS device and simulator - slices and prints the SPM checksum. + slices, plus a universal macOS slice, and prints the SPM checksum. +- `swift build` validates local macOS SwiftPM consumption. - `xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build` validates local SwiftPM consumption. - A small Obj-C module/static-link smoke test was built and run on an iOS @@ -78,8 +79,9 @@ before the release is cut). ## Known gaps / TODO -- **macOS / Catalyst / visionOS slices** — only iOS device + simulator are wired - up in this first pass; add the macOS slice (see section 3 of the script). +- **Catalyst / visionOS slices** — iOS device, iOS simulator, and macOS are wired + up in this first pass; Catalyst and visionOS still need separate slice wiring + and validation. - **Code signing** — release xcframeworks are typically signed; add a signing step before zipping for distribution. - **Release workflow validation** — exercise `.github/workflows/spm-release.yml` diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 37dfae1c4..85d272d3d 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -13,12 +13,11 @@ # build/apple/MATTelemetry.xcframework # build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) # -# Slices built here: iOS device (arm64) and iOS simulator (arm64 + x86_64 fat). -# A macOS slice (and visionOS, Catalyst) can be folded in the same way -- see -# the note in section 3. +# Slices built here: iOS device (arm64), iOS simulator (arm64 + x86_64 fat), +# and macOS (arm64 + x86_64 universal). # # NOTE: this is a first-pass scaffold. It has been validated on macOS for iOS -# device + simulator slices; macOS/Catalyst/visionOS slices are still TODO. +# device, simulator, and macOS slices; Catalyst/visionOS slices are still TODO. set -euo pipefail @@ -27,6 +26,15 @@ ROOT="$(cd "$(dirname "$0")/../.." && pwd)" OUT="$ROOT/build/apple" LIB="libmat.a" # mat target; the Obj-C wrappers compile into it (lib/CMakeLists.txt:217) +case "$CONFIG" in + release) CMAKE_BUILD_TYPE="Release" ;; + debug) CMAKE_BUILD_TYPE="Debug" ;; + *) + echo "Usage: $0 [release|debug]" >&2 + exit 1 + ;; +esac + # Force a STATIC libmat that includes the Obj-C wrappers, regardless of the # repo's default library type. export CMAKE_OPTS="-DBUILD_SHARED_LIBS=OFF -DBUILD_OBJC_WRAPPER=YES ${CMAKE_OPTS:-}" @@ -132,17 +140,34 @@ mkdir -p "$OUT/ios-simulator" lipo -create "$OUT/ios-arm64-sim/$LIB" "$OUT/ios-x86_64-sim/$LIB" \ -output "$OUT/ios-simulator/$LIB" -# --- 3. (Optional) macOS slice ------------------------------------------------ -# Add a native macOS build (e.g. cmake -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" -# -DBUILD_APPLE_HTTP=YES) here and append another `-library .../libmat.a -# -headers "$HDRS"` pair to the xcodebuild call below. Omitted from this first -# pass to keep the prototype focused on iOS. +# Native universal macOS archive. Build only the `mat` target in an isolated +# CMake build directory so switching away from the iOS toolchain does not +# disturb the already-copied iOS archives. +echo "=== building arm64+x86_64 / macosx ($CONFIG) ===" +MACOS_BUILD="$OUT/macos-build" +MACOS_DEPLOYMENT_TARGET="${MACOSX_DEPLOYMENT_TARGET:-10.15}" +cmake -S "$ROOT" -B "$MACOS_BUILD" \ + -DMAC_ARCH=universal \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="$MACOS_DEPLOYMENT_TARGET" \ + -DCMAKE_BUILD_TYPE="$CMAKE_BUILD_TYPE" \ + -DCMAKE_PACKAGE_TYPE=tgz \ + -DBUILD_TEST_TOOL=OFF \ + -DBUILD_UNIT_TESTS=OFF \ + -DBUILD_FUNC_TESTS=OFF \ + -DBUILD_SWIFT_WRAPPER=OFF \ + -DBUILD_PACKAGE=OFF \ + $CMAKE_OPTS +cmake --build "$MACOS_BUILD" --target mat +mkdir -p "$OUT/macos-universal" +cp "$MACOS_BUILD/lib/$LIB" "$OUT/macos-universal/$LIB" # --- 4. Assemble the xcframework --------------------------------------------- rm -rf "$OUT/MATTelemetry.xcframework" xcodebuild -create-xcframework \ -library "$OUT/ios-arm64/$LIB" -headers "$HDRS" \ -library "$OUT/ios-simulator/$LIB" -headers "$HDRS" \ + -library "$OUT/macos-universal/$LIB" -headers "$HDRS" \ -output "$OUT/MATTelemetry.xcframework" echo "Created $OUT/MATTelemetry.xcframework" From d242830e0c64d4e6177e303914a22629ce4bf763 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Thu, 18 Jun 2026 22:43:49 -0500 Subject: [PATCH 10/27] Add Mac Catalyst slice to SPM xcframework Teach the Apple build path to produce macabi archives, include a fat Catalyst variant in MATTelemetry.xcframework, advertise Mac Catalyst in the Swift package, and document the expanded validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CMakeLists.txt | 13 +++++++++++-- Package.swift | 18 ++++++++++-------- build-ios.sh | 16 +++++++++++++--- tools/apple/README.md | 19 ++++++++++--------- tools/apple/build-xcframework.sh | 14 ++++++++++++-- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a0ba0e82..6d117cb42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,13 +57,17 @@ if(APPLE) if (${IOS_PLAT} STREQUAL "iphonesimulator") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") - else() + elseif(NOT ${IOS_PLAT} STREQUAL "maccatalyst") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") endif() endif() - if((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator") OR (${IOS_PLAT} STREQUAL "xros") OR (${IOS_PLAT} STREQUAL "xrsimulator")) + if(${IOS_PLAT} STREQUAL "maccatalyst") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") + set(IOS_PLATFORM "macosx") + elseif((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator") OR (${IOS_PLAT} STREQUAL "xros") OR (${IOS_PLAT} STREQUAL "xrsimulator")) set(IOS_PLATFORM "${IOS_PLAT}") else() message(FATAL_ERROR "Unrecognized iOS platform '${IOS_PLAT}'") @@ -89,6 +93,11 @@ if(APPLE) OUTPUT_VARIABLE CMAKE_OSX_SYSROOT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(${IOS_PLAT} STREQUAL "maccatalyst") + set(IOS_SUPPORT_FRAMEWORKS "${CMAKE_OSX_SYSROOT}/System/iOSSupport/System/Library/Frameworks") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}") + endif() message(STATUS "CMAKE_OSX_SYSROOT ${CMAKE_OSX_SYSROOT}") message(STATUS "ARCHITECTURE: ${CMAKE_SYSTEM_PROCESSOR}") message(STATUS "PLATFORM: ${IOS_PLATFORM}") diff --git a/Package.swift b/Package.swift index fe7385189..6144610d0 100644 --- a/Package.swift +++ b/Package.swift @@ -17,8 +17,9 @@ // Local development: // 1. Run `tools/apple/build-xcframework.sh release` on macOS with Xcode. // It produces ./build/apple/MATTelemetry.xcframework. -// 2. `swift build` validates macOS consumption; for iOS, add this package as a -// local dependency or build the package with an iOS Simulator destination. +// 2. `swift build` validates macOS consumption; for iOS / Mac Catalyst, add +// this package as a local dependency or build the package with the desired +// Xcode destination. // // Release distribution (so consumers can add the repo by URL in Xcode): // 1. Build the xcframework, zip it, and attach it to the GitHub Release. @@ -83,6 +84,7 @@ let package = Package( name: "OneDSSwift", platforms: [ .iOS(.v12), + .macCatalyst(.v14), .macOS(.v10_15), ], products: [ @@ -118,13 +120,13 @@ let package = Package( .linkedLibrary("c++"), .linkedLibrary("sqlite3"), .linkedLibrary("z"), - .linkedFramework("CFNetwork", .when(platforms: [.iOS, .macOS])), - .linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macOS])), - .linkedFramework("Foundation", .when(platforms: [.iOS, .macOS])), - .linkedFramework("Network", .when(platforms: [.iOS, .macOS])), - .linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macOS])), + .linkedFramework("CFNetwork", .when(platforms: [.iOS, .macCatalyst, .macOS])), + .linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macCatalyst, .macOS])), + .linkedFramework("Foundation", .when(platforms: [.iOS, .macCatalyst, .macOS])), + .linkedFramework("Network", .when(platforms: [.iOS, .macCatalyst, .macOS])), + .linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macCatalyst, .macOS])), .linkedFramework("IOKit", .when(platforms: [.macOS])), - .linkedFramework("UIKit", .when(platforms: [.iOS])), + .linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst])), ]), ] ) diff --git a/build-ios.sh b/build-ios.sh index d316fe2fa..731572865 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -4,7 +4,7 @@ # build-ios.sh [clean] [release|debug] ${ARCH} ${PLATFORM} # where # ARCH = arm64|arm64e|x86_64 -# PLATFORM = iphoneos|iphonesimulator|xros|xrsimulator +# PLATFORM = iphoneos|iphonesimulator|maccatalyst|xros|xrsimulator if [ "$1" == "clean" ]; then echo "build-ios.sh: cleaning previous build artifacts" @@ -37,7 +37,7 @@ elif [ "$1" == "x86_64" ]; then shift fi -# the last param is expected to specify the platform name: iphoneos|iphonesimulator|xros|xrsimulator +# the last param is expected to specify the platform name: iphoneos|iphonesimulator|maccatalyst|xros|xrsimulator # so if it is non-empty and it is not "device", we take it as a valid platform name # otherwise we fall back to old iOS logic which only supported iphoneos|iphonesimulator IOS_PLAT="iphonesimulator" @@ -54,13 +54,23 @@ DEPLOYMENT_TARGET="" if [ "$IOS_PLAT" == "iphoneos" ] || [ "$IOS_PLAT" == "iphonesimulator" ]; then SYS_NAME="iOS" + IOS_SYSROOT="$IOS_PLAT" DEPLOYMENT_TARGET="$IOS_DEPLOYMENT_TARGET" if [ -z "$DEPLOYMENT_TARGET" ]; then DEPLOYMENT_TARGET="12.0" FORCE_RESET_DEPLOYMENT_TARGET=YES fi +elif [ "$IOS_PLAT" == "maccatalyst" ]; then + SYS_NAME="iOS" + IOS_SYSROOT="macosx" + DEPLOYMENT_TARGET="$MACCATALYST_DEPLOYMENT_TARGET" + if [ -z "$DEPLOYMENT_TARGET" ]; then + DEPLOYMENT_TARGET="14.0" + FORCE_RESET_DEPLOYMENT_TARGET=YES + fi elif [ "$IOS_PLAT" == "xros" ] || [ "$IOS_PLAT" == "xrsimulator" ]; then SYS_NAME="visionOS" + IOS_SYSROOT="$IOS_PLAT" DEPLOYMENT_TARGET="$XROS_DEPLOYMENT_TARGET" if [ -z "$DEPLOYMENT_TARGET" ]; then DEPLOYMENT_TARGET="1.0" @@ -92,7 +102,7 @@ cd out CMAKE_PACKAGE_TYPE=tgz -cmake_cmd="cmake -DCMAKE_OSX_SYSROOT=$IOS_PLAT -DCMAKE_SYSTEM_NAME=$SYS_NAME -DCMAKE_IOS_ARCH_ABI=$IOS_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DBUILD_IOS=YES -DIOS_ARCH=$IOS_ARCH -DIOS_PLAT=$IOS_PLAT -DIOS_DEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PACKAGE_TYPE=$CMAKE_PACKAGE_TYPE -DFORCE_RESET_DEPLOYMENT_TARGET=$FORCE_RESET_DEPLOYMENT_TARGET $CMAKE_OPTS .." +cmake_cmd="cmake -DCMAKE_OSX_SYSROOT=$IOS_SYSROOT -DCMAKE_SYSTEM_NAME=$SYS_NAME -DCMAKE_IOS_ARCH_ABI=$IOS_ARCH -DCMAKE_OSX_DEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DBUILD_IOS=YES -DIOS_ARCH=$IOS_ARCH -DIOS_PLAT=$IOS_PLAT -DIOS_DEPLOYMENT_TARGET=$DEPLOYMENT_TARGET -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PACKAGE_TYPE=$CMAKE_PACKAGE_TYPE -DFORCE_RESET_DEPLOYMENT_TARGET=$FORCE_RESET_DEPLOYMENT_TARGET $CMAKE_OPTS .." echo "${cmake_cmd}" eval $cmake_cmd diff --git a/tools/apple/README.md b/tools/apple/README.md index c2d98771d..ca13f40a3 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -25,7 +25,7 @@ so the xcframework just needs to vend a Clang module named `ObjCModule` | File | Purpose | | --- | --- | | `Package.swift` (repo root) | Distributable SPM manifest: `binaryTarget` (xcframework) + `OneDSSwift` source target | -| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice, lipo's the simulator/macOS archs where needed, and assembles the xcframework with `xcodebuild -create-xcframework` | +| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice, lipo's the simulator/Catalyst/macOS archs where needed, and assembles the xcframework with `xcodebuild -create-xcframework` | | `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module the Swift layer imports | | `tools/apple/MATTelemetry-umbrella.h` | Umbrella over the `ODW*.h` headers baked into the xcframework | | `tools/apple/MATTelemetryAvailability.json` | Build-time optional-module manifest consumed by `Package.swift` so Swift sources match the xcframework contents | @@ -69,19 +69,20 @@ before the release is cut). ## Validation performed -- `tools/apple/build-xcframework.sh release` builds the iOS device and simulator - slices, plus a universal macOS slice, and prints the SPM checksum. +- `tools/apple/build-xcframework.sh release` builds the iOS device, iOS + simulator, Mac Catalyst, and macOS slices, and prints the SPM checksum. - `swift build` validates local macOS SwiftPM consumption. - `xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build` - validates local SwiftPM consumption. -- A small Obj-C module/static-link smoke test was built and run on an iOS - Simulator. + validates iOS Simulator SwiftPM consumption. +- `xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build` + validates Mac Catalyst SwiftPM consumption. +- Small Obj-C module/static-link smoke tests validate binary module linkability. ## Known gaps / TODO -- **Catalyst / visionOS slices** — iOS device, iOS simulator, and macOS are wired - up in this first pass; Catalyst and visionOS still need separate slice wiring - and validation. +- **visionOS slices** — iOS device, iOS simulator, Mac Catalyst, and macOS are + wired up in this first pass; visionOS still needs separate slice wiring and + validation. - **Code signing** — release xcframeworks are typically signed; add a signing step before zipping for distribution. - **Release workflow validation** — exercise `.github/workflows/spm-release.yml` diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 85d272d3d..961e8a0e3 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -14,10 +14,11 @@ # build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) # # Slices built here: iOS device (arm64), iOS simulator (arm64 + x86_64 fat), -# and macOS (arm64 + x86_64 universal). +# Mac Catalyst (arm64 + x86_64 fat), and macOS (arm64 + x86_64 universal). # # NOTE: this is a first-pass scaffold. It has been validated on macOS for iOS -# device, simulator, and macOS slices; Catalyst/visionOS slices are still TODO. +# device, simulator, Mac Catalyst, and macOS slices; visionOS slices are still +# TODO. set -euo pipefail @@ -133,6 +134,8 @@ build_slice() { # clean-arg arch platform out-subdir build_slice clean arm64 iphoneos ios-arm64 build_slice "" arm64 iphonesimulator ios-arm64-sim build_slice "" x86_64 iphonesimulator ios-x86_64-sim +build_slice "" arm64 maccatalyst maccatalyst-arm64 +build_slice "" x86_64 maccatalyst maccatalyst-x86_64 # Fat simulator archive (arm64 + x86_64) -- a single xcframework slice cannot # mix device and simulator, but it can contain multiple archs for one platform. @@ -140,6 +143,12 @@ mkdir -p "$OUT/ios-simulator" lipo -create "$OUT/ios-arm64-sim/$LIB" "$OUT/ios-x86_64-sim/$LIB" \ -output "$OUT/ios-simulator/$LIB" +# Fat Catalyst archive (arm64 + x86_64), emitted as a separate platform variant +# from both iOS simulator and native macOS. +mkdir -p "$OUT/maccatalyst" +lipo -create "$OUT/maccatalyst-arm64/$LIB" "$OUT/maccatalyst-x86_64/$LIB" \ + -output "$OUT/maccatalyst/$LIB" + # Native universal macOS archive. Build only the `mat` target in an isolated # CMake build directory so switching away from the iOS toolchain does not # disturb the already-copied iOS archives. @@ -167,6 +176,7 @@ rm -rf "$OUT/MATTelemetry.xcframework" xcodebuild -create-xcframework \ -library "$OUT/ios-arm64/$LIB" -headers "$HDRS" \ -library "$OUT/ios-simulator/$LIB" -headers "$HDRS" \ + -library "$OUT/maccatalyst/$LIB" -headers "$HDRS" \ -library "$OUT/macos-universal/$LIB" -headers "$HDRS" \ -output "$OUT/MATTelemetry.xcframework" echo "Created $OUT/MATTelemetry.xcframework" From 1d89f6d463a9b80067cc706e94b64f4e0ef6a28e Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 01:28:10 -0500 Subject: [PATCH 11/27] Add visionOS slices to SPM xcframework Build visionOS device and simulator archives, advertise visionOS in the Swift package, and document the expanded validation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CMakeLists.txt | 12 ++++++++++-- Package.swift | 19 ++++++++++--------- tools/apple/README.md | 8 ++++---- tools/apple/build-xcframework.sh | 12 +++++++++--- 4 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d117cb42..b67461ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,7 +57,7 @@ if(APPLE) if (${IOS_PLAT} STREQUAL "iphonesimulator") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") - elseif(NOT ${IOS_PLAT} STREQUAL "maccatalyst") + elseif(${IOS_PLAT} STREQUAL "iphoneos") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") endif() @@ -67,7 +67,15 @@ if(APPLE) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") set(IOS_PLATFORM "macosx") - elseif((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator") OR (${IOS_PLAT} STREQUAL "xros") OR (${IOS_PLAT} STREQUAL "xrsimulator")) + elseif(${IOS_PLAT} STREQUAL "xros") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}") + set(IOS_PLATFORM "${IOS_PLAT}") + elseif(${IOS_PLAT} STREQUAL "xrsimulator") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator") + set(IOS_PLATFORM "${IOS_PLAT}") + elseif((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator")) set(IOS_PLATFORM "${IOS_PLAT}") else() message(FATAL_ERROR "Unrecognized iOS platform '${IOS_PLAT}'") diff --git a/Package.swift b/Package.swift index 6144610d0..e7b4e56bd 100644 --- a/Package.swift +++ b/Package.swift @@ -17,9 +17,9 @@ // Local development: // 1. Run `tools/apple/build-xcframework.sh release` on macOS with Xcode. // It produces ./build/apple/MATTelemetry.xcframework. -// 2. `swift build` validates macOS consumption; for iOS / Mac Catalyst, add -// this package as a local dependency or build the package with the desired -// Xcode destination. +// 2. `swift build` validates macOS consumption; for iOS / Mac Catalyst / +// visionOS, add this package as a local dependency or build the package +// with the desired Xcode destination. // // Release distribution (so consumers can add the repo by URL in Xcode): // 1. Build the xcframework, zip it, and attach it to the GitHub Release. @@ -86,6 +86,7 @@ let package = Package( .iOS(.v12), .macCatalyst(.v14), .macOS(.v10_15), + .visionOS(.v1), ], products: [ .library(name: "OneDSSwift", targets: ["OneDSSwift"]), @@ -120,13 +121,13 @@ let package = Package( .linkedLibrary("c++"), .linkedLibrary("sqlite3"), .linkedLibrary("z"), - .linkedFramework("CFNetwork", .when(platforms: [.iOS, .macCatalyst, .macOS])), - .linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macCatalyst, .macOS])), - .linkedFramework("Foundation", .when(platforms: [.iOS, .macCatalyst, .macOS])), - .linkedFramework("Network", .when(platforms: [.iOS, .macCatalyst, .macOS])), - .linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macCatalyst, .macOS])), + .linkedFramework("CFNetwork", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])), + .linkedFramework("CoreFoundation", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])), + .linkedFramework("Foundation", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])), + .linkedFramework("Network", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])), + .linkedFramework("SystemConfiguration", .when(platforms: [.iOS, .macCatalyst, .macOS, .visionOS])), .linkedFramework("IOKit", .when(platforms: [.macOS])), - .linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst])), + .linkedFramework("UIKit", .when(platforms: [.iOS, .macCatalyst, .visionOS])), ]), ] ) diff --git a/tools/apple/README.md b/tools/apple/README.md index ca13f40a3..2fed02012 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -70,19 +70,19 @@ before the release is cut). ## Validation performed - `tools/apple/build-xcframework.sh release` builds the iOS device, iOS - simulator, Mac Catalyst, and macOS slices, and prints the SPM checksum. + simulator, Mac Catalyst, visionOS device, visionOS simulator, and macOS + slices, and prints the SPM checksum. - `swift build` validates local macOS SwiftPM consumption. - `xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build` validates iOS Simulator SwiftPM consumption. - `xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build` validates Mac Catalyst SwiftPM consumption. +- `xcodebuild -scheme OneDSSwift -destination 'generic/platform=visionOS Simulator' build` + validates visionOS Simulator SwiftPM consumption. - Small Obj-C module/static-link smoke tests validate binary module linkability. ## Known gaps / TODO -- **visionOS slices** — iOS device, iOS simulator, Mac Catalyst, and macOS are - wired up in this first pass; visionOS still needs separate slice wiring and - validation. - **Code signing** — release xcframeworks are typically signed; add a signing step before zipping for distribution. - **Release workflow validation** — exercise `.github/workflows/spm-release.yml` diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 961e8a0e3..023fe7d3f 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -14,11 +14,11 @@ # build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) # # Slices built here: iOS device (arm64), iOS simulator (arm64 + x86_64 fat), -# Mac Catalyst (arm64 + x86_64 fat), and macOS (arm64 + x86_64 universal). +# Mac Catalyst (arm64 + x86_64 fat), visionOS device/simulator (arm64), and +# macOS (arm64 + x86_64 universal). # # NOTE: this is a first-pass scaffold. It has been validated on macOS for iOS -# device, simulator, Mac Catalyst, and macOS slices; visionOS slices are still -# TODO. +# device, simulator, Mac Catalyst, visionOS, and macOS slices. set -euo pipefail @@ -137,6 +137,10 @@ build_slice "" x86_64 iphonesimulator ios-x86_64-sim build_slice "" arm64 maccatalyst maccatalyst-arm64 build_slice "" x86_64 maccatalyst maccatalyst-x86_64 +# visionOS uses a different CMake system name, so start it from a fresh cache. +build_slice clean arm64 xros visionos-arm64 +build_slice "" arm64 xrsimulator visionos-arm64-sim + # Fat simulator archive (arm64 + x86_64) -- a single xcframework slice cannot # mix device and simulator, but it can contain multiple archs for one platform. mkdir -p "$OUT/ios-simulator" @@ -177,6 +181,8 @@ xcodebuild -create-xcframework \ -library "$OUT/ios-arm64/$LIB" -headers "$HDRS" \ -library "$OUT/ios-simulator/$LIB" -headers "$HDRS" \ -library "$OUT/maccatalyst/$LIB" -headers "$HDRS" \ + -library "$OUT/visionos-arm64/$LIB" -headers "$HDRS" \ + -library "$OUT/visionos-arm64-sim/$LIB" -headers "$HDRS" \ -library "$OUT/macos-universal/$LIB" -headers "$HDRS" \ -output "$OUT/MATTelemetry.xcframework" echo "Created $OUT/MATTelemetry.xcframework" From 024684724006f8be8d835e104bbd54ca00c3afe7 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 01:36:56 -0500 Subject: [PATCH 12/27] Address SPM release review comments Clarify release automation comments, make the checksum workflow step unambiguous, and ensure forced xcframework CMake flags cannot be overridden by caller-provided CMAKE_OPTS. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 3 ++- Package.swift | 9 ++++----- tools/apple/build-xcframework.sh | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml index 28eb1f414..6c129b4bd 100644 --- a/.github/workflows/spm-release.yml +++ b/.github/workflows/spm-release.yml @@ -92,7 +92,8 @@ jobs: if: ${{ steps.ver.outputs.skip != 'true' }} run: | set -euo pipefail - echo "checksum=$(swift package compute-checksum "build/apple/$ARTIFACT")" >> "$GITHUB_OUTPUT" + checksum="$(swift package compute-checksum "build/apple/$ARTIFACT")" + echo "checksum=$checksum" >> "$GITHUB_OUTPUT" - name: Upload xcframework to the release if: ${{ steps.ver.outputs.skip != 'true' }} diff --git a/Package.swift b/Package.swift index e7b4e56bd..1a1806ccb 100644 --- a/Package.swift +++ b/Package.swift @@ -22,11 +22,10 @@ // with the desired Xcode destination. // // Release distribution (so consumers can add the repo by URL in Xcode): -// 1. Build the xcframework, zip it, and attach it to the GitHub Release. -// 2. Run `swift package compute-checksum MATTelemetry.xcframework.zip`. -// 3. Replace the `.binaryTarget(... path:)` below with the `url:`+`checksum:` -// form shown in the comment. The vcpkg-release-bump workflow pattern can be -// extended to automate steps 1-3 on each release tag. +// .github/workflows/spm-release.yml builds and uploads the xcframework, +// computes the checksum, rewrites the local `.binaryTarget(... path:)` below +// to `url:`+`checksum:`, and pushes the 3-component SemVer tag that SPM can +// resolve. import PackageDescription import Foundation diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 023fe7d3f..406ed7513 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -38,7 +38,7 @@ esac # Force a STATIC libmat that includes the Obj-C wrappers, regardless of the # repo's default library type. -export CMAKE_OPTS="-DBUILD_SHARED_LIBS=OFF -DBUILD_OBJC_WRAPPER=YES ${CMAKE_OPTS:-}" +export CMAKE_OPTS="${CMAKE_OPTS:-} -DBUILD_SHARED_LIBS=OFF -DBUILD_OBJC_WRAPPER=YES" rm -rf "$OUT" mkdir -p "$OUT" From 9634894be8bc3e2b216b683e86a3decd5db5d4e4 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 01:46:12 -0500 Subject: [PATCH 13/27] Address follow-up SPM review comments Update the package comment for optional Swift sources, fail early for unsupported Apple platforms, and skip SPM tag publishing when the release tag already exists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 4 ++++ Package.swift | 6 ++---- build-ios.sh | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml index 6c129b4bd..bd485b04f 100644 --- a/.github/workflows/spm-release.yml +++ b/.github/workflows/spm-release.yml @@ -130,6 +130,10 @@ jobs: if: ${{ steps.ver.outputs.skip != 'true' }} run: | set -euo pipefail + if git ls-remote --exit-code --tags origin "refs/tags/${{ steps.ver.outputs.spm_version }}" >/dev/null; then + echo "::notice::SPM tag ${{ steps.ver.outputs.spm_version }} already exists; skipping tag publish." + exit 0 + fi git config user.name "github-actions[bot]" git config user.email "41898282+github-actions[bot]@users.noreply.github.com" git add Package.swift tools/apple/MATTelemetryAvailability.json diff --git a/Package.swift b/Package.swift index 1a1806ccb..c7dc5346d 100644 --- a/Package.swift +++ b/Package.swift @@ -106,10 +106,8 @@ let package = Package( path: "build/apple/MATTelemetry.xcframework"), // Thin Swift API layer (source). Depends on the Obj-C module from the - // xcframework. NOTE: the conditional source exclusions in - // wrappers/swift/Package.swift (PrivacyGuard / Sanitizer / DataViewer - // when those private modules aren't built) should be carried over here - // and kept in sync with the headers baked into the xcframework. + // xcframework. The conditional source exclusions above must stay in sync + // with the headers baked into the xcframework. .target( name: "OneDSSwift", dependencies: ["MATTelemetry"], diff --git a/build-ios.sh b/build-ios.sh index 731572865..8fb50094e 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -76,6 +76,9 @@ elif [ "$IOS_PLAT" == "xros" ] || [ "$IOS_PLAT" == "xrsimulator" ]; then DEPLOYMENT_TARGET="1.0" FORCE_RESET_DEPLOYMENT_TARGET=YES fi +else + echo "ERROR: unsupported Apple platform '$IOS_PLAT'. Expected iphoneos, iphonesimulator, maccatalyst, xros, or xrsimulator." 1>&2 + exit 1 fi echo "deployment target = $DEPLOYMENT_TARGET" From 5923e5cfed97e943fdeeacca6caf970041ca7bad Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 01:53:42 -0500 Subject: [PATCH 14/27] Validate SPM Apple platforms in release workflow Run SwiftPM and Xcode package builds for macOS, iOS Simulator, Mac Catalyst, and visionOS Simulator after producing the xcframework artifact. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml index bd485b04f..ac06c2649 100644 --- a/.github/workflows/spm-release.yml +++ b/.github/workflows/spm-release.yml @@ -37,7 +37,7 @@ jobs: if: >- ${{ github.event_name == 'workflow_dispatch' || (github.event.release.draft == false && github.event.release.prerelease == false) }} - runs-on: macos-14 # provides Xcode (xcodebuild, swift) + runs-on: macos-15 # provides Xcode with Apple platform SDKs (xcodebuild, swift) env: ARTIFACT: MATTelemetry.xcframework.zip steps: @@ -87,6 +87,15 @@ jobs: tools/apple/build-xcframework.sh release test -f "build/apple/$ARTIFACT" + - name: Validate SwiftPM package consumption + if: ${{ steps.ver.outputs.skip != 'true' }} + run: | + set -euo pipefail + swift build + xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build + xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build + xcodebuild -scheme OneDSSwift -destination 'generic/platform=visionOS Simulator' build + - name: Compute SPM checksum id: sum if: ${{ steps.ver.outputs.skip != 'true' }} From ccceaa5fed519193ba4d56376c80803ee19d9e49 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 02:09:05 -0500 Subject: [PATCH 15/27] Skip package creation for xcframework slices Allow build-ios.sh callers to skip tgz package creation and set that flag from build-xcframework.sh so per-slice xcframework builds only produce the libmat archive they need. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build-ios.sh | 6 +++++- tools/apple/build-xcframework.sh | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/build-ios.sh b/build-ios.sh index 8fb50094e..c3da652e7 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -111,4 +111,8 @@ eval $cmake_cmd make -make package +if [ "${MATTELEMETRY_SKIP_PACKAGE:-}" == "1" ]; then + echo "MATTELEMETRY_SKIP_PACKAGE=1: skipping package creation" +else + make package +fi diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 406ed7513..036c29c53 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -126,7 +126,7 @@ build_slice() { # clean-arg arch platform out-subdir shift local arch="$1" plat="$2" sub="$3" echo "=== building $arch / $plat ($CONFIG) ===" - ( cd "$ROOT" && ./build-ios.sh $clean_arg "$CONFIG" "$arch" "$plat" ) + ( cd "$ROOT" && MATTELEMETRY_SKIP_PACKAGE=1 ./build-ios.sh $clean_arg "$CONFIG" "$arch" "$plat" ) mkdir -p "$OUT/$sub" cp "$ROOT/out/lib/$LIB" "$OUT/$sub/$LIB" } From 5c6f57414336b6b28c7bf4f69aedacb41b2d310a Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 02:22:13 -0500 Subject: [PATCH 16/27] Clean up Apple SPM README Refresh the prototype documentation to match the current multi-platform xcframework, release workflow, validation coverage, and remaining gaps. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/README.md | 143 +++++++++++++++++++++++------------------- 1 file changed, 80 insertions(+), 63 deletions(-) diff --git a/tools/apple/README.md b/tools/apple/README.md index 2fed02012..9c791e48b 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -1,89 +1,106 @@ -# Swift Package Manager (xcframework) — prototype +# Swift Package Manager xcframework prototype -**Status: validated prototype.** This is a first-pass scaffold for distributing -the 1DS C++ SDK to Apple app developers via **Swift Package Manager (SPM)**, the -successor to CocoaPods (the CocoaPods trunk goes read-only on 2 Dec 2026, and -there is no official in-repo podspec today). +**Status: validated prototype.** This packages the 1DS C++ SDK for Apple +developers through Swift Package Manager (SPM), using a prebuilt xcframework for +the C++ core plus Obj-C wrappers and compiling the Swift API from source. -## Approach +## Package shape -SPM cannot practically compile this SDK's C++ tree from source (CMake build, -Bond codegen, vendored sqlite3/zlib, heavy platform conditionals). So: +SPM is not a good fit for compiling this SDK's full C++ tree directly because +the SDK depends on CMake, Bond codegen, vendored sqlite3/zlib, and platform +conditionals. Instead, the package is split into: -| Layer | How it ships | +| Layer | Packaging | | --- | --- | -| C++ core + Obj-C wrappers (`ODW*`) | **Prebuilt binary** — `MATTelemetry.xcframework` (`.binaryTarget`) | -| Swift API (`OneDSSwift`) | **Source** — `wrappers/swift/Sources/OneDSSwift`, depends on the Obj-C module from the xcframework | +| C++ core + Obj-C wrappers (`ODW*`) | `MATTelemetry.xcframework` binary target | +| Swift API (`OneDSSwift`) | Source target in `wrappers/swift/Sources/OneDSSwift` | -The Obj-C wrappers already compile into `libmat.a` on Apple -(`lib/CMakeLists.txt:217`), and the Swift sources already `import ObjCModule`, -so the xcframework just needs to vend a Clang module named `ObjCModule` -(`tools/apple/module.modulemap` + `MATTelemetry-umbrella.h`). +The xcframework vendors a Clang module named `ObjCModule` through +`tools/apple/module.modulemap` and `MATTelemetry-umbrella.h`, matching the +existing Swift sources' `import ObjCModule`. -## Files +## Supported slices + +`tools/apple/build-xcframework.sh release` builds: + +| Platform | Slice | +| --- | --- | +| iOS device | `ios-arm64` | +| iOS Simulator | `ios-arm64_x86_64-simulator` | +| Mac Catalyst | `ios-arm64_x86_64-maccatalyst` | +| macOS | `macos-arm64_x86_64` | +| visionOS device | `xros-arm64` | +| visionOS Simulator | `xros-arm64-simulator` | + +## Important files | File | Purpose | | --- | --- | -| `Package.swift` (repo root) | Distributable SPM manifest: `binaryTarget` (xcframework) + `OneDSSwift` source target | -| `tools/apple/build-xcframework.sh` | Builds a static `libmat.a` per Apple slice, lipo's the simulator/Catalyst/macOS archs where needed, and assembles the xcframework with `xcodebuild -create-xcframework` | -| `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module the Swift layer imports | -| `tools/apple/MATTelemetry-umbrella.h` | Umbrella over the `ODW*.h` headers baked into the xcframework | -| `tools/apple/MATTelemetryAvailability.json` | Build-time optional-module manifest consumed by `Package.swift` so Swift sources match the xcframework contents | +| `Package.swift` | Root SPM manifest: binary target + Swift source target | +| `tools/apple/build-xcframework.sh` | Builds static `libmat.a` slices and assembles `MATTelemetry.xcframework` | +| `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module | +| `tools/apple/MATTelemetry-umbrella.h` | Base umbrella for always-available Obj-C wrapper headers | +| `tools/apple/MATTelemetryAvailability.json` | Optional-module manifest consumed by `Package.swift` | +| `.github/workflows/spm-release.yml` | Release automation for the hosted xcframework and SPM tag | + +## Local build -## Build (on macOS) +Run on macOS with Xcode and CMake: ```bash tools/apple/build-xcframework.sh release # -> build/apple/MATTelemetry.xcframework -# -> build/apple/MATTelemetry.xcframework.zip (+ prints the SPM checksum) -swift build # resolves Package.swift against the local xcframework +# -> build/apple/MATTelemetry.xcframework.zip +# -> prints the SwiftPM checksum ``` -## Consume +For local development, `Package.swift` points at +`build/apple/MATTelemetry.xcframework`. `swift build` validates macOS +consumption; use Xcode destinations for iOS Simulator, Mac Catalyst, and +visionOS Simulator. -- **Local:** point a sample app at this package directory (path dependency). -- **Released:** in Xcode *File -> Add Package Dependencies...*, enter the repo - URL and pick a version. SPM only accepts **3-component SemVer**, and the SDK's - own `vX.Y.Z.W` tags are not valid SemVer, so consumers pin the **parallel - 3-component tag** the release workflow publishes: +## Consumption - ```swift - .package(url: "https://github.com/microsoft/cpp_client_telemetry.git", from: "3.10.161") - ``` +- **Local:** add this repository as a local package dependency after building + `build/apple/MATTelemetry.xcframework`. +- **Released:** add the repository URL in Xcode and pin the parallel + 3-component SemVer tag published by the release workflow: -## Release wiring +```swift +.package(url: "https://github.com/microsoft/cpp_client_telemetry.git", from: "3.10.161") +``` + +The SDK's native release tags are 4-component (`vX.Y.Z.W`), which SPM does not +accept as SemVer. The release workflow publishes the corresponding `X.Y.Z` tag. + +## Release workflow -`.github/workflows/spm-release.yml` automates distribution on each published -release (a 4-component `vX.Y.Z.W` tag). On a macOS runner it: +`.github/workflows/spm-release.yml` runs for published SDK releases and manual +dispatch. It: -1. Builds `MATTelemetry.xcframework` and zips it. -2. Uploads the zip to the GitHub Release. -3. Computes the SPM checksum and rewrites the `Package.swift` `binaryTarget` - from `path:` to `url:`+`checksum:`. -4. Commits that manifest and pushes a **3-component SemVer tag** (`X.Y.Z`, - derived by dropping the trailing build component) that SPM can resolve. +1. Builds and zips `MATTelemetry.xcframework`. +2. Validates package consumption for macOS, iOS Simulator, Mac Catalyst, and + visionOS Simulator. +3. Uploads the zip to the GitHub Release. +4. Rewrites `Package.swift` from local `path:` to hosted `url:` + `checksum:`. +5. Commits the resolved manifest and pushes the 3-component SPM tag. -This mirrors the `vcpkg-release-bump` workflow. It requires the root -`Package.swift` to already exist at the release tag (i.e. this prototype merged -before the release is cut). +The private `lib/modules` submodule is intentionally not fetched by the release +workflow, so optional module headers and Swift sources are gated by +`MATTelemetryAvailability.json`. ## Validation performed -- `tools/apple/build-xcframework.sh release` builds the iOS device, iOS - simulator, Mac Catalyst, visionOS device, visionOS simulator, and macOS - slices, and prints the SPM checksum. -- `swift build` validates local macOS SwiftPM consumption. -- `xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build` - validates iOS Simulator SwiftPM consumption. -- `xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build` - validates Mac Catalyst SwiftPM consumption. -- `xcodebuild -scheme OneDSSwift -destination 'generic/platform=visionOS Simulator' build` - validates visionOS Simulator SwiftPM consumption. -- Small Obj-C module/static-link smoke tests validate binary module linkability. - -## Known gaps / TODO - -- **Code signing** — release xcframeworks are typically signed; add a signing - step before zipping for distribution. -- **Release workflow validation** — exercise `.github/workflows/spm-release.yml` - end-to-end on an actual published release. +- Full xcframework build for iOS, iOS Simulator, Mac Catalyst, macOS, visionOS, + and visionOS Simulator. +- SwiftPM/Xcode builds for macOS, iOS Simulator, Mac Catalyst, visionOS + Simulator, and visionOS device. +- External TelemetryTest package consumer builds, including visionOS. +- Obj-C module/static-link smoke tests for representative binary slices. +- Apple Vision Pro simulator runtime installation and boot. + +## Known gaps + +- Release xcframework signing/notarization. +- End-to-end execution of `.github/workflows/spm-release.yml` on a real + published release. From 79f55faaff49f829ced5becefdda8c1a89037eae Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 02:24:19 -0500 Subject: [PATCH 17/27] Remove status label from Apple SPM README Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/apple/README.md b/tools/apple/README.md index 9c791e48b..a5a7eeddf 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -1,8 +1,8 @@ # Swift Package Manager xcframework prototype -**Status: validated prototype.** This packages the 1DS C++ SDK for Apple -developers through Swift Package Manager (SPM), using a prebuilt xcframework for -the C++ core plus Obj-C wrappers and compiling the Swift API from source. +This packages the 1DS C++ SDK for Apple developers through Swift Package Manager +(SPM), using a prebuilt xcframework for the C++ core plus Obj-C wrappers and +compiling the Swift API from source. ## Package shape From 27049fe003c5603ca9dcf2b9f64530cace354215 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 02:25:33 -0500 Subject: [PATCH 18/27] Assert SPM platforms in release workflow Validate the Package.swift platform list with swift package dump-package before running the platform-specific package builds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/spm-release.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/spm-release.yml b/.github/workflows/spm-release.yml index ac06c2649..2fbd94ba1 100644 --- a/.github/workflows/spm-release.yml +++ b/.github/workflows/spm-release.yml @@ -91,6 +91,23 @@ jobs: if: ${{ steps.ver.outputs.skip != 'true' }} run: | set -euo pipefail + swift package dump-package > package.json + python3 - <<'PY' + import json + expected = { + "ios": "12.0", + "maccatalyst": "14.0", + "macos": "10.15", + "visionos": "1.0", + } + with open("package.json", encoding="utf-8") as f: + platforms = { + item["platformName"]: item["version"] + for item in json.load(f)["platforms"] + } + if platforms != expected: + raise SystemExit(f"Unexpected Package.swift platforms: {platforms}") + PY swift build xcodebuild -scheme OneDSSwift -destination 'generic/platform=iOS Simulator' build xcodebuild -scheme OneDSSwift -destination 'platform=macOS,variant=Mac Catalyst' build From 55c4ce5dc7f5e010551839ac90a6dd49788fce08 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 02:49:37 -0500 Subject: [PATCH 19/27] Clean up xcframework build script comments Remove a drifting line-number reference and keep section headings sequential. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/build-xcframework.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 036c29c53..30dd95c1c 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -25,7 +25,7 @@ set -euo pipefail CONFIG="${1:-release}" ROOT="$(cd "$(dirname "$0")/../.." && pwd)" OUT="$ROOT/build/apple" -LIB="libmat.a" # mat target; the Obj-C wrappers compile into it (lib/CMakeLists.txt:217) +LIB="libmat.a" # mat target; the Obj-C wrappers compile into it. case "$CONFIG" in release) CMAKE_BUILD_TYPE="Release" ;; @@ -175,7 +175,7 @@ cmake --build "$MACOS_BUILD" --target mat mkdir -p "$OUT/macos-universal" cp "$MACOS_BUILD/lib/$LIB" "$OUT/macos-universal/$LIB" -# --- 4. Assemble the xcframework --------------------------------------------- +# --- 3. Assemble the xcframework --------------------------------------------- rm -rf "$OUT/MATTelemetry.xcframework" xcodebuild -create-xcframework \ -library "$OUT/ios-arm64/$LIB" -headers "$HDRS" \ @@ -187,7 +187,7 @@ xcodebuild -create-xcframework \ -output "$OUT/MATTelemetry.xcframework" echo "Created $OUT/MATTelemetry.xcframework" -# --- 5. Zip + checksum for release distribution ------------------------------ +# --- 4. Zip + checksum for release distribution ------------------------------ ( cd "$OUT" && rm -f MATTelemetry.xcframework.zip \ && zip -qry MATTelemetry.xcframework.zip MATTelemetry.xcframework ) echo "Zipped: $OUT/MATTelemetry.xcframework.zip" From 9a18330a8fa5ca536070edb9f603fac9a0fdaa53 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 03:21:34 -0500 Subject: [PATCH 20/27] Use fresh build cache for each xcframework slice Build each Apple slice from a clean CMake out directory, while preserving the build-tools marker, and restrict xcframework slice builds to the libmat archive target inputs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/build-xcframework.sh | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 30dd95c1c..00af27b1d 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -36,9 +36,17 @@ case "$CONFIG" in ;; esac -# Force a STATIC libmat that includes the Obj-C wrappers, regardless of the -# repo's default library type. -export CMAKE_OPTS="${CMAKE_OPTS:-} -DBUILD_SHARED_LIBS=OFF -DBUILD_OBJC_WRAPPER=YES" +# Build only the static libmat archive with Obj-C wrappers; slice builds do not +# need the repo's test, Swift wrapper, or package targets. +CMAKE_OPTS="${CMAKE_OPTS:-}" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_SHARED_LIBS=OFF" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_OBJC_WRAPPER=YES" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_TEST_TOOL=OFF" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_UNIT_TESTS=OFF" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_FUNC_TESTS=OFF" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_SWIFT_WRAPPER=OFF" +CMAKE_OPTS="$CMAKE_OPTS -DBUILD_PACKAGE=OFF" +export CMAKE_OPTS rm -rf "$OUT" mkdir -p "$OUT" @@ -121,25 +129,26 @@ cp "$ROOT"/tools/apple/MATTelemetry-umbrella.h "$HDRS/" } >> "$HDRS/MATTelemetry-umbrella.h" # --- 2. Build one static lib per (arch, platform) ---------------------------- -build_slice() { # clean-arg arch platform out-subdir - local clean_arg="$1" - shift +build_slice() { # arch platform out-subdir local arch="$1" plat="$2" sub="$3" echo "=== building $arch / $plat ($CONFIG) ===" - ( cd "$ROOT" && MATTELEMETRY_SKIP_PACKAGE=1 ./build-ios.sh $clean_arg "$CONFIG" "$arch" "$plat" ) + ( + cd "$ROOT" + rm -f CMakeCache.txt *.cmake + rm -rf out + MATTELEMETRY_SKIP_PACKAGE=1 ./build-ios.sh "$CONFIG" "$arch" "$plat" + ) mkdir -p "$OUT/$sub" cp "$ROOT/out/lib/$LIB" "$OUT/$sub/$LIB" } -build_slice clean arm64 iphoneos ios-arm64 -build_slice "" arm64 iphonesimulator ios-arm64-sim -build_slice "" x86_64 iphonesimulator ios-x86_64-sim -build_slice "" arm64 maccatalyst maccatalyst-arm64 -build_slice "" x86_64 maccatalyst maccatalyst-x86_64 - -# visionOS uses a different CMake system name, so start it from a fresh cache. -build_slice clean arm64 xros visionos-arm64 -build_slice "" arm64 xrsimulator visionos-arm64-sim +build_slice arm64 iphoneos ios-arm64 +build_slice arm64 iphonesimulator ios-arm64-sim +build_slice x86_64 iphonesimulator ios-x86_64-sim +build_slice arm64 maccatalyst maccatalyst-arm64 +build_slice x86_64 maccatalyst maccatalyst-x86_64 +build_slice arm64 xros visionos-arm64 +build_slice arm64 xrsimulator visionos-arm64-sim # Fat simulator archive (arm64 + x86_64) -- a single xcframework slice cannot # mix device and simulator, but it can contain multiple archs for one platform. @@ -169,7 +178,6 @@ cmake -S "$ROOT" -B "$MACOS_BUILD" \ -DBUILD_UNIT_TESTS=OFF \ -DBUILD_FUNC_TESTS=OFF \ -DBUILD_SWIFT_WRAPPER=OFF \ - -DBUILD_PACKAGE=OFF \ $CMAKE_OPTS cmake --build "$MACOS_BUILD" --target mat mkdir -p "$OUT/macos-universal" From 279ffd39a15e28b1645c83395842e99a1e403e8d Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 03:47:18 -0500 Subject: [PATCH 21/27] Use portable shell comparison in iOS build Use POSIX '=' for the MATTELEMETRY_SKIP_PACKAGE check in build-ios.sh. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- build-ios.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-ios.sh b/build-ios.sh index c3da652e7..d96b93bc4 100755 --- a/build-ios.sh +++ b/build-ios.sh @@ -111,7 +111,7 @@ eval $cmake_cmd make -if [ "${MATTELEMETRY_SKIP_PACKAGE:-}" == "1" ]; then +if [ "${MATTELEMETRY_SKIP_PACKAGE:-}" = "1" ]; then echo "MATTELEMETRY_SKIP_PACKAGE=1: skipping package creation" else make package From 7bc7415c175addd5953992e7ad05421af01087dc Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 11:26:25 -0500 Subject: [PATCH 22/27] Quote ${IOS_PLAT}/${IOS_ARCH} in Apple if() conditions Addresses Copilot review on #1486 (CMakeLists.txt:70, :108): the new Apple-slice platform conditionals used unquoted ${IOS_PLAT} (and the adjacent ${IOS_ARCH}) inside if()/elseif(). If the variable is ever empty/undefined, `if(${IOS_PLAT} STREQUAL "...")` expands to `if( STREQUAL "...")`, which is a hard CMake parse error that aborts configuration. Quoting the expansion ("${IOS_PLAT}") keeps the compare well-formed (empty -> false) and matches the existing `if("${MAC_ARCH}" STREQUAL ...)` idiom already used in this file. Quoted all 7 ${IOS_PLAT} comparisons (lines 57/60/66/70/74/78/104) and the 3 ${IOS_ARCH} comparisons (84/88/92). ${CMAKE_SYSTEM_NAME} (line 313) is left as-is: it is always defined by CMake, so it cannot trigger the empty-expansion parse error. Verified with a minimal CMake repro: the quoted form configures cleanly with the variable undefined and still matches when set, whereas the old unquoted form errors out at configure time. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- CMakeLists.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b67461ba2..28d43fb7e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,42 +54,42 @@ if(APPLE) if(FORCE_RESET_OSX_DEPLOYMENT_TARGET) set(CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE) - if (${IOS_PLAT} STREQUAL "iphonesimulator") + if ("${IOS_PLAT}" STREQUAL "iphonesimulator") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mios-simulator-version-min=${IOS_DEPLOYMENT_TARGET}") - elseif(${IOS_PLAT} STREQUAL "iphoneos") + elseif("${IOS_PLAT}" STREQUAL "iphoneos") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -miphoneos-version-min=${IOS_DEPLOYMENT_TARGET}") endif() endif() - if(${IOS_PLAT} STREQUAL "maccatalyst") + if("${IOS_PLAT}" STREQUAL "maccatalyst") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-ios${IOS_DEPLOYMENT_TARGET}-macabi") set(IOS_PLATFORM "macosx") - elseif(${IOS_PLAT} STREQUAL "xros") + elseif("${IOS_PLAT}" STREQUAL "xros") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}") set(IOS_PLATFORM "${IOS_PLAT}") - elseif(${IOS_PLAT} STREQUAL "xrsimulator") + elseif("${IOS_PLAT}" STREQUAL "xrsimulator") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -target ${IOS_ARCH}-apple-xros${IOS_DEPLOYMENT_TARGET}-simulator") set(IOS_PLATFORM "${IOS_PLAT}") - elseif((${IOS_PLAT} STREQUAL "iphoneos") OR (${IOS_PLAT} STREQUAL "iphonesimulator")) + elseif(("${IOS_PLAT}" STREQUAL "iphoneos") OR ("${IOS_PLAT}" STREQUAL "iphonesimulator")) set(IOS_PLATFORM "${IOS_PLAT}") else() message(FATAL_ERROR "Unrecognized iOS platform '${IOS_PLAT}'") endif() - if(${IOS_ARCH} STREQUAL "x86_64") + if("${IOS_ARCH}" STREQUAL "x86_64") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch x86_64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch x86_64") set(CMAKE_SYSTEM_PROCESSOR x86_64) - elseif(${IOS_ARCH} STREQUAL "arm64") + elseif("${IOS_ARCH}" STREQUAL "arm64") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64") set(CMAKE_SYSTEM_PROCESSOR arm64) - elseif(${IOS_ARCH} STREQUAL "arm64e") + elseif("${IOS_ARCH}" STREQUAL "arm64e") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -arch arm64e") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -arch arm64e") set(CMAKE_SYSTEM_PROCESSOR arm64e) @@ -101,7 +101,7 @@ if(APPLE) OUTPUT_VARIABLE CMAKE_OSX_SYSROOT ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if(${IOS_PLAT} STREQUAL "maccatalyst") + if("${IOS_PLAT}" STREQUAL "maccatalyst") set(IOS_SUPPORT_FRAMEWORKS "${CMAKE_OSX_SYSROOT}/System/iOSSupport/System/Library/Frameworks") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -iframework ${IOS_SUPPORT_FRAMEWORKS}") From 9d522660d0051b0b6454965be749961c0b5721be Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 12:10:01 -0500 Subject: [PATCH 23/27] Rename public Clang module ObjCModule -> MATTelemetryObjC The Obj-C wrapper module vended by MATTelemetry.xcframework (and the local wrappers/swift package) was named `ObjCModule` -- a generic name that consumers `import`. Once #1486 makes this module public via SPM, a generic name risks colliding with another binary/SPM package that also vends an `ObjCModule`, and pollutes the consumer's module namespace. Rename it to the namespaced `MATTelemetryObjC` while the name is still internal (the wrappers/swift package was never distributed), so it is collision-safe before first release. Renamed consistently across both build paths so the same Swift sources compile against both modulemaps: - tools/apple/module.modulemap (xcframework) and wrappers/swift/Modules/module.modulemap (local): `module MATTelemetryObjC`. - All 13 `import ObjCModule` -> `import MATTelemetryObjC` in wrappers/swift/Sources/OneDSSwift/*.swift. - Bridging header file renamed ObjCModule-Bridging-Header.h -> MATTelemetryObjC-Bridging-Header.h and its modulemap reference updated. - Comments/docs (Package.swift, build-xcframework.sh, tools/apple/README.md, examples/swift/README.md) updated. Pure rename; no behavioral change. Needs a macOS `swift build` (local wrappers path) + xcframework SPM build to confirm both paths still resolve the module -- I cannot run those on Windows. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 2 +- examples/swift/README.md | 2 +- tools/apple/README.md | 6 +++--- tools/apple/build-xcframework.sh | 2 +- tools/apple/module.modulemap | 8 ++++---- ...idging-Header.h => MATTelemetryObjC-Bridging-Header.h} | 0 wrappers/swift/Modules/module.modulemap | 4 ++-- wrappers/swift/Sources/OneDSSwift/CommonDataContext.swift | 2 +- .../swift/Sources/OneDSSwift/DiagnosticDataViewer.swift | 2 +- wrappers/swift/Sources/OneDSSwift/EventProperties.swift | 2 +- wrappers/swift/Sources/OneDSSwift/LogConfiguration.swift | 2 +- wrappers/swift/Sources/OneDSSwift/LogManager.swift | 2 +- wrappers/swift/Sources/OneDSSwift/Logger.swift | 2 +- wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift | 4 ++-- wrappers/swift/Sources/OneDSSwift/PrivacyGuard.swift | 2 +- .../swift/Sources/OneDSSwift/PrivacyGuardInitConfig.swift | 2 +- wrappers/swift/Sources/OneDSSwift/Sanitizer.swift | 2 +- .../swift/Sources/OneDSSwift/SanitizerInitConfig.swift | 2 +- wrappers/swift/Sources/OneDSSwift/SemanticContext.swift | 2 +- 19 files changed, 25 insertions(+), 25 deletions(-) rename wrappers/swift/Headers/{ObjCModule-Bridging-Header.h => MATTelemetryObjC-Bridging-Header.h} (100%) diff --git a/Package.swift b/Package.swift index c7dc5346d..bf8313af8 100644 --- a/Package.swift +++ b/Package.swift @@ -92,7 +92,7 @@ let package = Package( ], targets: [ // Prebuilt C++ core + Obj-C wrappers. The xcframework's bundled - // module map vends the Clang module `ObjCModule` (see + // module map vends the Clang module `MATTelemetryObjC` (see // tools/apple/module.modulemap), which the Swift layer imports. // // For a tagged release, swap the local path for the hosted artifact: diff --git a/examples/swift/README.md b/examples/swift/README.md index 2c7db6c3a..3f66816da 100644 --- a/examples/swift/README.md +++ b/examples/swift/README.md @@ -30,7 +30,7 @@ Details: - OneDSSwift: Package containing swift wrappers - Modules Included - - ObjCModule: Module exposing ObjC headers via module.modulemap file. + - MATTelemetryObjC: Module exposing ObjC headers via module.modulemap file. - Libraries and Frameworks to link to Target - [Same as mentioned in the SampleXcodeApp section](#to-be-linked) \ No newline at end of file diff --git a/tools/apple/README.md b/tools/apple/README.md index a5a7eeddf..c08dd4ac3 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -15,9 +15,9 @@ conditionals. Instead, the package is split into: | C++ core + Obj-C wrappers (`ODW*`) | `MATTelemetry.xcframework` binary target | | Swift API (`OneDSSwift`) | Source target in `wrappers/swift/Sources/OneDSSwift` | -The xcframework vendors a Clang module named `ObjCModule` through +The xcframework vendors a Clang module named `MATTelemetryObjC` through `tools/apple/module.modulemap` and `MATTelemetry-umbrella.h`, matching the -existing Swift sources' `import ObjCModule`. +existing Swift sources' `import MATTelemetryObjC`. ## Supported slices @@ -38,7 +38,7 @@ existing Swift sources' `import ObjCModule`. | --- | --- | | `Package.swift` | Root SPM manifest: binary target + Swift source target | | `tools/apple/build-xcframework.sh` | Builds static `libmat.a` slices and assembles `MATTelemetry.xcframework` | -| `tools/apple/module.modulemap` | Defines the `ObjCModule` Clang module | +| `tools/apple/module.modulemap` | Defines the `MATTelemetryObjC` Clang module | | `tools/apple/MATTelemetry-umbrella.h` | Base umbrella for always-available Obj-C wrapper headers | | `tools/apple/MATTelemetryAvailability.json` | Optional-module manifest consumed by `Package.swift` | | `.github/workflows/spm-release.yml` | Release automation for the hosted xcframework and SPM tag | diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 00af27b1d..21bb9add1 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -53,7 +53,7 @@ mkdir -p "$OUT" # --- 1. Public Obj-C headers + module map (vended by the xcframework) -------- # Flatten the ODW*.h headers + umbrella + modulemap into one Headers dir. The -# module is named `ObjCModule` to match what wrappers/swift sources import. +# module is named `MATTelemetryObjC` to match what wrappers/swift sources import. HDRS="$OUT/Headers" mkdir -p "$HDRS" diff --git a/tools/apple/module.modulemap b/tools/apple/module.modulemap index 0a2c0167a..ec2e84f22 100644 --- a/tools/apple/module.modulemap +++ b/tools/apple/module.modulemap @@ -1,9 +1,9 @@ // Clang module vended by MATTelemetry.xcframework. Imported by the OneDSSwift -// Swift layer as `import ObjCModule` -- the module name matches what the -// existing wrappers/swift/Sources/OneDSSwift sources already import, so no Swift -// source changes are needed. +// Swift layer as `import MATTelemetryObjC`. The module name is kept identical +// to the one in wrappers/swift/Modules/module.modulemap so the same Swift +// sources compile against both the local modulemap and this xcframework. -module ObjCModule { +module MATTelemetryObjC { umbrella header "MATTelemetry-umbrella.h" export * } diff --git a/wrappers/swift/Headers/ObjCModule-Bridging-Header.h b/wrappers/swift/Headers/MATTelemetryObjC-Bridging-Header.h similarity index 100% rename from wrappers/swift/Headers/ObjCModule-Bridging-Header.h rename to wrappers/swift/Headers/MATTelemetryObjC-Bridging-Header.h diff --git a/wrappers/swift/Modules/module.modulemap b/wrappers/swift/Modules/module.modulemap index 2e1856bae..f4fbec71d 100644 --- a/wrappers/swift/Modules/module.modulemap +++ b/wrappers/swift/Modules/module.modulemap @@ -1,6 +1,6 @@ /// Module exporting headers declared in ObjC. Imported by Swift package to have access to the ObjC types. -module ObjCModule { - header "../Headers/ObjCModule-Bridging-Header.h" +module MATTelemetryObjC { + header "../Headers/MATTelemetryObjC-Bridging-Header.h" export * } diff --git a/wrappers/swift/Sources/OneDSSwift/CommonDataContext.swift b/wrappers/swift/Sources/OneDSSwift/CommonDataContext.swift index 88786ac95..74c5bf0d0 100644 --- a/wrappers/swift/Sources/OneDSSwift/CommonDataContext.swift +++ b/wrappers/swift/Sources/OneDSSwift/CommonDataContext.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper over ODWCommonDataContext class. public final class CommonDataContext { diff --git a/wrappers/swift/Sources/OneDSSwift/DiagnosticDataViewer.swift b/wrappers/swift/Sources/OneDSSwift/DiagnosticDataViewer.swift index 46d3011e9..dc136b076 100644 --- a/wrappers/swift/Sources/OneDSSwift/DiagnosticDataViewer.swift +++ b/wrappers/swift/Sources/OneDSSwift/DiagnosticDataViewer.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper class over `ODWDiagnosticDataViewer` representing Diagnostic Data Viewer Hook. public final class DiagnosticDataViewer { diff --git a/wrappers/swift/Sources/OneDSSwift/EventProperties.swift b/wrappers/swift/Sources/OneDSSwift/EventProperties.swift index eb346a84e..bee46e573 100644 --- a/wrappers/swift/Sources/OneDSSwift/EventProperties.swift +++ b/wrappers/swift/Sources/OneDSSwift/EventProperties.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /** Represents Event's properties. diff --git a/wrappers/swift/Sources/OneDSSwift/LogConfiguration.swift b/wrappers/swift/Sources/OneDSSwift/LogConfiguration.swift index 0f6bd18bb..1a5812770 100644 --- a/wrappers/swift/Sources/OneDSSwift/LogConfiguration.swift +++ b/wrappers/swift/Sources/OneDSSwift/LogConfiguration.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Class wrapping `ODWLogConfiguration` ObjC class object, representing configuration related to events. public final class LogConfiguration { diff --git a/wrappers/swift/Sources/OneDSSwift/LogManager.swift b/wrappers/swift/Sources/OneDSSwift/LogManager.swift index 11a0af415..5a659795f 100644 --- a/wrappers/swift/Sources/OneDSSwift/LogManager.swift +++ b/wrappers/swift/Sources/OneDSSwift/LogManager.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper over ODWLogManager which manages the telemetry logging system. public final class LogManager { diff --git a/wrappers/swift/Sources/OneDSSwift/Logger.swift b/wrappers/swift/Sources/OneDSSwift/Logger.swift index 04a9c1497..b901cfa4b 100644 --- a/wrappers/swift/Sources/OneDSSwift/Logger.swift +++ b/wrappers/swift/Sources/OneDSSwift/Logger.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper class around ObjC Logger class `ODWLogger` used to events. public final class Logger { diff --git a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift index c0c1ad4e0..0f0483eee 100644 --- a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift +++ b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift @@ -5,12 +5,12 @@ /// Contains alias for the types declared in the ObjC header files to make them available /// as part of the swift package module. -/// To avoid clients not have to import ObjCModule explicitly. +/// To avoid clients not have to import MATTelemetryObjC explicitly. /// Important: Due to objc->swift conventions, Type name is removed, so ODWPiiKindGenericData would be accessed as .genericData in swift. /// Check corresponding header file for the doc of each type. -import ObjCModule +import MATTelemetryObjC // ODWEventProperties.h public typealias EventPriority = ODWEventPriority diff --git a/wrappers/swift/Sources/OneDSSwift/PrivacyGuard.swift b/wrappers/swift/Sources/OneDSSwift/PrivacyGuard.swift index 19ad4787a..b589b82bc 100644 --- a/wrappers/swift/Sources/OneDSSwift/PrivacyGuard.swift +++ b/wrappers/swift/Sources/OneDSSwift/PrivacyGuard.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper to `ODWPrivacyGuard` representing Privacy Guard Hook. public final class PrivacyGuard { diff --git a/wrappers/swift/Sources/OneDSSwift/PrivacyGuardInitConfig.swift b/wrappers/swift/Sources/OneDSSwift/PrivacyGuardInitConfig.swift index 7f660d9f6..2459c7f08 100644 --- a/wrappers/swift/Sources/OneDSSwift/PrivacyGuardInitConfig.swift +++ b/wrappers/swift/Sources/OneDSSwift/PrivacyGuardInitConfig.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC public final class PrivacyGuardInitConfig { let odwPrivacyGuardInitConfig: ODWPrivacyGuardInitConfig diff --git a/wrappers/swift/Sources/OneDSSwift/Sanitizer.swift b/wrappers/swift/Sources/OneDSSwift/Sanitizer.swift index 4b5035f3c..004166f16 100644 --- a/wrappers/swift/Sources/OneDSSwift/Sanitizer.swift +++ b/wrappers/swift/Sources/OneDSSwift/Sanitizer.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper to `ODWSanitizer` representing the Sanitizer. public final class Sanitizer { diff --git a/wrappers/swift/Sources/OneDSSwift/SanitizerInitConfig.swift b/wrappers/swift/Sources/OneDSSwift/SanitizerInitConfig.swift index 8eb70fd37..81f23d75b 100644 --- a/wrappers/swift/Sources/OneDSSwift/SanitizerInitConfig.swift +++ b/wrappers/swift/Sources/OneDSSwift/SanitizerInitConfig.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC public final class SanitizerInitConfig { let odwSanitizerInitConfig: ODWSanitizerInitConfig diff --git a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift index 184d19bc4..211983097 100644 --- a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift +++ b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // -import ObjCModule +import MATTelemetryObjC /// Wrapper over `ODWSemanticContext` class that manages the inclusion of semantic context values on logged events. public class SemanticContext { From 2b5bdd50a555617931ff092273e39e698f172045 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 14:09:26 -0500 Subject: [PATCH 24/27] docs(apple): note xcframework expects system sqlite3/zlib The SPM xcframework links the platform's libsqlite3/libz (Package.swift .linkedLibrary) rather than bundling them. Document why: bundling a private static sqlite3 would collide with any consumer that also uses SQLite (Core Data/GRDB/FMDB) -> duplicate symbols / two-instance state; system linking yields one shared copy. Notes the contrast with vcpkg (uses vcpkg packages) and Android (bundles, since the NDK has no system copy). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tools/apple/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tools/apple/README.md b/tools/apple/README.md index c08dd4ac3..1c97c31b1 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -19,6 +19,21 @@ The xcframework vendors a Clang module named `MATTelemetryObjC` through `tools/apple/module.modulemap` and `MATTelemetry-umbrella.h`, matching the existing Swift sources' `import MATTelemetryObjC`. +## Runtime dependencies (sqlite3 / zlib) + +The xcframework does **not** bundle sqlite3 or zlib. `Package.swift` links the +**system** `libsqlite3` and `libz` that Apple ships on every iOS / macOS / Mac +Catalyst / visionOS target (`.linkedLibrary("sqlite3")` / `.linkedLibrary("z")`), +so consumers do not need to add them. + +This is deliberate. Embedding a private static copy of sqlite3 into the +xcframework would give any app that also uses SQLite (Core Data, GRDB, FMDB, +etc.) two copies of the library in one process — risking duplicate-symbol link +errors or divergent SQLite state. Linking the OS-provided libraries guarantees a +single shared instance. For comparison, the vcpkg build consumes vcpkg's own +sqlite3/zlib packages, and only the Android build bundles them (the NDK ships no +system copy). + ## Supported slices `tools/apple/build-xcframework.sh release` builds: From f2c5fc946ced4b51fa06b67cdcde42f8ff8fca53 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 14:29:31 -0500 Subject: [PATCH 25/27] Use PIIKind alias in SemanticContext.setUserID signature Every other PII-tagged public API in OneDSSwift (EventProperties, Logger, LogManager) already uses the `PIIKind` typealias; only SemanticContext.setUserID exposed the raw Obj-C `ODWPiiKind` in its signature/default. Switch it to `PIIKind` (same underlying type via the ObjCTypes.swift typealias) so consumers of the Swift API never need to reference the MATTelemetryObjC module name. Pure source-level alias swap; no behavioral change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- wrappers/swift/Sources/OneDSSwift/SemanticContext.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift index 211983097..a95d53992 100644 --- a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift +++ b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift @@ -50,9 +50,9 @@ public class SemanticContext { - Parameters: - userID: A `String` that contains the unique user identifier. - withPiiKind: A PIIKind of the userID. Set it to PiiKind_None t odenote it as non-PII. - - Note: Default value is `ODWPiiKind.identity`. + - Note: Default value is `PIIKind.identity`. */ - public func setUserID(_ userID: String, withPiiKind piiKind: ODWPiiKind = ODWPiiKind.identity) { + public func setUserID(_ userID: String, withPiiKind piiKind: PIIKind = PIIKind.identity) { odwSemanticContext.setUserId(userID) } From 06caea2aaafd11d5e9965743c7708b1e480458ec Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 15:17:39 -0500 Subject: [PATCH 26/27] Fix SemanticContext.setUserID dropping the caller's piiKind setUserID accepted a piiKind argument but called the no-piiKind Obj-C overload `setUserId(_:)`, silently discarding the caller's PII classification for the user id. ODWSemanticContext exposes a `setUserId:piiKind:` overload (wrappers/obj-c/ODWSemanticContext.h:47-48) for exactly this. Route the argument through so the requested PII tag is actually applied. Privacy-relevant behavioral fix. Needs a macOS `swift build` to confirm the bridged selector (`setUserId(_:piiKind:)`); cannot run Swift on the Windows session. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- wrappers/swift/Sources/OneDSSwift/SemanticContext.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift index a95d53992..03d731102 100644 --- a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift +++ b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift @@ -53,7 +53,7 @@ public class SemanticContext { - Note: Default value is `PIIKind.identity`. */ public func setUserID(_ userID: String, withPiiKind piiKind: PIIKind = PIIKind.identity) { - odwSemanticContext.setUserId(userID) + odwSemanticContext.setUserId(userID, piiKind: piiKind) } /** From a57c2a1008a64dbdf8f98060e29c48ddcde68998 Mon Sep 17 00:00:00 2001 From: Bhagirath Mehta Date: Fri, 19 Jun 2026 15:42:52 -0500 Subject: [PATCH 27/27] Address Swift package review refinements Keep CommonDataContext available without PrivacyGuard, tighten Swift docs, and scope xcframework slice cleanup to the output directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- Package.swift | 1 - tools/apple/README.md | 6 +++--- tools/apple/build-xcframework.sh | 1 - wrappers/swift/Package.swift | 1 - wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift | 2 +- wrappers/swift/Sources/OneDSSwift/SemanticContext.swift | 2 +- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/Package.swift b/Package.swift index bf8313af8..18af3547c 100644 --- a/Package.swift +++ b/Package.swift @@ -66,7 +66,6 @@ if hasPrivacyGuard { swiftSettings.append(.define("MATSDK_PRIVACYGUARD_AVAILABLE")) } else { excludedSources.append(contentsOf: [ - "CommonDataContext.swift", "PrivacyGuard.swift", "PrivacyGuardInitConfig.swift", ]) diff --git a/tools/apple/README.md b/tools/apple/README.md index 1c97c31b1..79d706fd1 100644 --- a/tools/apple/README.md +++ b/tools/apple/README.md @@ -1,8 +1,8 @@ # Swift Package Manager xcframework prototype -This packages the 1DS C++ SDK for Apple developers through Swift Package Manager -(SPM), using a prebuilt xcframework for the C++ core plus Obj-C wrappers and -compiling the Swift API from source. +This package distributes the 1DS C++ SDK to Apple developers through Swift +Package Manager (SPM), using a prebuilt xcframework for the C++ core plus Obj-C +wrappers and compiling the Swift API from source. ## Package shape diff --git a/tools/apple/build-xcframework.sh b/tools/apple/build-xcframework.sh index 21bb9add1..7fed4ccf9 100755 --- a/tools/apple/build-xcframework.sh +++ b/tools/apple/build-xcframework.sh @@ -134,7 +134,6 @@ build_slice() { # arch platform out-subdir echo "=== building $arch / $plat ($CONFIG) ===" ( cd "$ROOT" - rm -f CMakeCache.txt *.cmake rm -rf out MATTELEMETRY_SKIP_PACKAGE=1 ./build-ios.sh "$CONFIG" "$arch" "$plat" ) diff --git a/wrappers/swift/Package.swift b/wrappers/swift/Package.swift index 879943354..5547c5c4d 100644 --- a/wrappers/swift/Package.swift +++ b/wrappers/swift/Package.swift @@ -25,7 +25,6 @@ if hasPrivacyGuard { swiftSettings.append(.define("MATSDK_PRIVACYGUARD_AVAILABLE")) } else { excludedSources.append(contentsOf: [ - "CommonDataContext.swift", "PrivacyGuard.swift", "PrivacyGuardInitConfig.swift", ]) diff --git a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift index 0f0483eee..e0e446b00 100644 --- a/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift +++ b/wrappers/swift/Sources/OneDSSwift/ObjCTypes.swift @@ -5,7 +5,7 @@ /// Contains alias for the types declared in the ObjC header files to make them available /// as part of the swift package module. -/// To avoid clients not have to import MATTelemetryObjC explicitly. +/// This lets clients use the Swift package module without importing MATTelemetryObjC explicitly. /// Important: Due to objc->swift conventions, Type name is removed, so ODWPiiKindGenericData would be accessed as .genericData in swift. /// Check corresponding header file for the doc of each type. diff --git a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift index 03d731102..3bf485fdf 100644 --- a/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift +++ b/wrappers/swift/Sources/OneDSSwift/SemanticContext.swift @@ -49,7 +49,7 @@ public class SemanticContext { - Parameters: - userID: A `String` that contains the unique user identifier. - - withPiiKind: A PIIKind of the userID. Set it to PiiKind_None t odenote it as non-PII. + - withPiiKind: A `PIIKind` for the userID. Set it to `PIIKind.none` to denote it as non-PII. - Note: Default value is `PIIKind.identity`. */ public func setUserID(_ userID: String, withPiiKind piiKind: PIIKind = PIIKind.identity) {