bevy_ios_toolkit 0.3.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
// Share-sheet shim for bevy_ios_toolkit's `platform` feature.
//
// @_cdecl C-ABI called FROM Rust; fire-and-forget (no callbacks, no polled
// result). Behind #if canImport(UIKit) with a linking stub otherwise. Symbol
// prefix `platform_`.

import Foundation

#if canImport(UIKit)
import UIKit

/// The view controller actually on screen. Presenting from the window's root
/// while a modal is up silently does nothing, so walk the presentation chain.
@MainActor private func topmostViewController() -> UIViewController? {
    var top = UIApplication.shared.connectedScenes
        .compactMap { $0 as? UIWindowScene }
        .flatMap { $0.windows }
        .first { $0.isKeyWindow }?.rootViewController
    while let presented = top?.presentedViewController {
        top = presented
    }
    return top
}

@_cdecl("platform_share_text")
public func platform_share_text(_ text: UnsafePointer<CChar>) {
    let s = String(cString: text)
    DispatchQueue.main.async {
        guard let top = topmostViewController() else { return }
        // A second sheet over the first is a no-op on iOS; drop it rather than
        // queue it, so a double-tap can't strand a sheet.
        guard !(top is UIActivityViewController) else { return }

        let vc = UIActivityViewController(activityItems: [s], applicationActivities: nil)
        // iPad presents this as a popover and traps on a nil source view; anchor
        // it to the centre of the presenting view, which reads as a modal sheet.
        if let popover = vc.popoverPresentationController {
            popover.sourceView = top.view
            popover.sourceRect = CGRect(
                x: top.view.bounds.midX, y: top.view.bounds.midY, width: 0, height: 0
            )
            popover.permittedArrowDirections = []
        }
        top.present(vc, animated: true)
    }
}

#else
@_cdecl("platform_share_text") public func platform_share_text(_ text: UnsafePointer<CChar>) {}
#endif