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 |
haptics, safe-area insets, outbound links, share sheet, thermal/low-power state |
Status: experimental (0.3, pre-release). APIs will move. Live behaviour needs a real device, the relevant Apple/Google setup, and the matching Swift shim linked from the companion SPM package — see "iOS integration". Everything is fully exercisable on desktop first via the built-in fakes.
How it works
Every module shares one native contract:
- 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 (an SPM product, see "iOS integration") 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 SPM product must be linked into your app — so
enabling a feature you haven't wired natively fails loudly at link time instead
of misbehaving at runtime.
= { = "0.3", = ["storekit", "ads", "att"] }
Quick start
use *;
use *;
// Show an interstitial once it's loaded.
// Gate features on ownership (covers purchase, restore, relaunch).
// Keep this action visible only when UMP requires it. Send
// `PresentPrivacyOptions` from the user's tap to reopen their choices.
See demo/ for a button-per-feature app that runs on desktop and iOS.
iOS integration
The Swift shims ship as a Swift package in this same repo, co-versioned with
the crate — one git tag pins both halves, which keeps the @_cdecl ↔ extern "C"
contract in lockstep. You link only the products for the features you ship; there
are no files to vendor or keep in sync by hand.
-
Add this crate with the features you ship.
-
Add this repo as a Swift package dependency and link the matching products. Each product links its own system frameworks;
Adsbrings the Google Mobile Ads + UMP SDKs transitively. The symbol prefixes (store_,platform_,admob_,att_,gamekit_,review_) won't collide with your own bridge.cargo feature SPM product platformPlatformstorekitStoreadsAdsattAttgamekitGameCenterreviewReview -
Per-feature native setup (stays in your app — the package ships none of it):
- ads — set
GADApplicationIdentifierinInfo.plist(useTEST_APP_IDin dev), add theSKAdNetworkItemsGoogle ships, and provide a visible action that sendsPresentPrivacyOptionswhenever thePrivacyOptionsRequirementresource isRequired. - att — add
NSUserTrackingUsageDescriptiontoInfo.plist. - gamekit — enable the Game Center capability.
- storekit — define products in App Store Connect (or a StoreKit config).
- ads — set
-
The
demo/ios/XcodeGen project shows the whole wiring end to end — it consumes the package by relative path.
Testing
The fakes are env-tunable (force no-fill, show-failures, consent prompts,
privacy-options requirements, ATT outcomes, Game Center sign-out) — each module
documents its knobs. An explicit UmpTestConfig makes geography testing
deterministic only while AdmobConfig::use_test_ads is enabled; production
configurations ignore its geography and reset fields. Simulators are test
devices automatically. For a physical device, include UMP's logged hashed test
device id in AdmobConfig::test_device_ids. On-device behaviour must still be
validated in Xcode with the SDKs linked.
Compatibility
bevy_ios_toolkit |
bevy |
iOS | AdMob SDK |
|---|---|---|---|
| 0.3 | 0.19 | 16+ | 12.3–12.x (+ UMP 3.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
Released under the MIT License.