bevy_ios_toolkit 0.1.0

Native iOS integrations for Bevy as ECS resources and messages: StoreKit IAP, Google AdMob ads, App Tracking Transparency, Game Center, review prompts, haptics and safe-area — each behind a feature, over a polled C-ABI Swift bridge with a testable desktop fake.
Documentation

bevy_ios_toolkit

Native iOS integrations for Bevy, exposed as ordinary ECS resources and messages. One crate, one plugin, a feature per integration:

feature module what it bridges
storekit store StoreKit 2 in-app purchases
ads ads Google AdMob ads + UMP (GDPR) consent
att att App Tracking Transparency prompt
gamekit gamekit Game Center auth, leaderboards, achievements
review review StoreKit review prompt
platform platform impact haptics, safe-area inset, outbound links

Status: experimental (0.1, pre-release). APIs will move. Live behaviour needs a real device, the relevant Apple/Google setup, and the matching Swift shim in your Xcode target — see "iOS integration". Everything is fully exercisable on desktop first via the built-in fakes.

How it works

Every module shares one native contract (the proven NativeBridge.swift pattern):

  • Each native entry point is @_cdecl C-ABI, called from Rust.
  • The SDKs' async, delegate-driven work surfaces as polled state (or a drained event queue) read once per frame — never callbacks into Rust, because re-entrancy against winit's event loop is not safe.
  • Each Swift shim (swift/*.swift) sits behind #if canImport(...) with linking stubs, so the staticlib links on any target.

Off iOS every module is a stateful, env-tunable fake, so the whole app flow — purchases, ads, rewards, consent, ATT, Game Center — runs on cargo run desktop builds with no device.

Features are opt-in for a reason

No feature is enabled by default. A module's extern "C" block only exists when its feature is on, and the matching Swift shim must be in your Xcode target — so enabling a feature you haven't wired natively fails loudly at link time instead of misbehaving at runtime.

bevy_ios_toolkit = { version = "0.1", features = ["storekit", "ads", "att"] }

Quick start

use bevy::prelude::*;
use bevy_ios_toolkit::prelude::*;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, IosPlugin))
        .insert_resource(AdmobConfig::test_ads())          // `ads`
        .insert_resource(StoreConfig {                     // `storekit`
            product_ids: vec!["com.example.app.removeads".into()],
        })
        .run();
}

// Show an interstitial once it's loaded.
fn show(inv: Res<AdInventory>, mut shows: MessageWriter<ShowAd>) {
    if inv.is_loaded(AdFormat::Interstitial) {
        shows.write(ShowAd(AdFormat::Interstitial));
    }
}

// Gate features on ownership (covers purchase, restore, relaunch).
fn gate(entitlements: Res<Entitlements>) {
    if entitlements.owns("com.example.app.removeads") { /* hide ads */ }
}

See demo/ for a button-per-feature app that runs on desktop and iOS.

iOS integration

  1. Add this crate with the features you ship.
  2. Copy the matching swift/*.swift shims into your app and add them to the Xcode target. The symbol prefixes (cupertino_, admob_, att_, gamekit_, review_) won't collide with your own bridge.
  3. Per-feature native setup:
    • ads — add the GoogleMobileAds + UserMessagingPlatform SPM packages; set GADApplicationIdentifier in Info.plist (use TEST_APP_ID in dev).
    • att — add NSUserTrackingUsageDescription to Info.plist.
    • gamekit — enable the Game Center capability.
    • storekit — define products in App Store Connect (or a StoreKit config).
  4. The demo/ios/ XcodeGen project shows the whole wiring end to end.

Testing

cargo test --features all
cargo run --example store --features storekit
cargo run --example ads   --features ads

The fakes are env-tunable (force no-fill, show-failures, consent prompts, ATT outcomes, Game Center sign-out) — each module documents its knobs. On-device behaviour must be validated in Xcode with the SDKs linked; this crate can't simulate Apple's or Google's servers.

Compatibility

bevy_ios_toolkit bevy iOS AdMob SDK
0.1 0.19 16+ 12.x (+ UMP 2.x)

Authorship

Much of this project — the Rust crate, the Swift bridges, its tests, and these docs — was written by Claude Opus 4.8 (Anthropic) under human direction and review. It ships with a passing test suite and a runnable demo, but it's young (0.x): read the code, run your own tests, and validate the native iOS paths on a real device before relying on it in production. Bug reports and PRs welcome.

License

Licensed under either of Apache-2.0 or MIT at your option.