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
@_cdeclC-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.
= { = "0.1", = ["storekit", "ads", "att"] }
Quick start
use *;
use *;
// Show an interstitial once it's loaded.
// Gate features on ownership (covers purchase, restore, relaunch).
See demo/ for a button-per-feature app that runs on desktop and iOS.
iOS integration
- Add this crate with the features you ship.
- Copy the matching
swift/*.swiftshims 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. - Per-feature native setup:
- ads — add the GoogleMobileAds + UserMessagingPlatform SPM packages; set
GADApplicationIdentifierinInfo.plist(useTEST_APP_IDin dev). - att — add
NSUserTrackingUsageDescriptiontoInfo.plist. - gamekit — enable the Game Center capability.
- storekit — define products in App Store Connect (or a StoreKit config).
- ads — add the GoogleMobileAds + UserMessagingPlatform SPM packages; set
- The
demo/ios/XcodeGen project shows the whole wiring end to end.
Testing
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.