spottedcat 1.0.1

Rusty SpottedCat simple game engine
Documentation
#[cfg(target_os = "ios")]
use block2::RcBlock;
#[cfg(target_os = "ios")]
use objc2::{msg_send, rc::Retained};
#[cfg(target_os = "ios")]
use objc2_core_motion::CMPedometer;
#[cfg(target_os = "ios")]
use objc2_foundation::{MainThreadMarker, NSDate};

#[cfg(target_os = "ios")]
const STEP_PERMISSION_EVENT: &str = "step_permission";
#[cfg(target_os = "ios")]
const MOTION_PERMISSION: &str = "motion";
#[cfg(target_os = "ios")]
const CM_AUTHORIZED: isize = 3;

#[cfg(target_os = "ios")]
fn motion_authorization_status() -> isize {
    unsafe { msg_send![objc2::class!(CMPedometer), authorizationStatus] }
}

#[cfg(target_os = "ios")]
pub fn has_runtime_permission(permission: &str) -> Result<bool, String> {
    match permission {
        MOTION_PERMISSION => Ok(motion_authorization_status() == CM_AUTHORIZED),
        _ => Err(format!("unsupported iOS permission: {permission}")),
    }
}

#[cfg(target_os = "ios")]
pub fn request_runtime_permission(permission: &str) -> Result<(), String> {
    if permission != MOTION_PERMISSION {
        return Err(format!("unsupported iOS permission: {permission}"));
    }

    let _mtm = MainThreadMarker::new()
        .ok_or_else(|| "motion permission request must run on the iOS main thread".to_string())?;

    unsafe {
        if !CMPedometer::isStepCountingAvailable() {
            return Err("step counting is unavailable on this device".to_string());
        }

        let pedometer = CMPedometer::new();
        let pedometer_for_handler: Retained<CMPedometer> = pedometer.clone();
        let start = NSDate::date();
        let handler = RcBlock::new(
            move |_data: *mut objc2_core_motion::CMPedometerData,
                  _error: *mut objc2_foundation::NSError| {
                let granted = motion_authorization_status() == CM_AUTHORIZED;
                pedometer_for_handler.stopPedometerUpdates();
                crate::push_platform_event(crate::PlatformEvent::Event(
                    STEP_PERMISSION_EVENT.to_string(),
                    if granted { "granted" } else { "denied" }.to_string(),
                ));
            },
        );

        pedometer.startPedometerUpdatesFromDate_withHandler(&start, RcBlock::as_ptr(&handler));
    }

    Ok(())
}