Files
marathon/crates/lib/src/persistence/lifecycle.rs
Sienna Meridian Satterwhite 1bd664fd2a initial persistence commit
Signed-off-by: Sienna Meridian Satterwhite <sienna@r3t.io>
2025-11-16 11:50:16 +00:00

159 lines
5.9 KiB
Rust

//! iOS lifecycle event handling for persistence
//!
//! This module provides event types and handlers for iOS application lifecycle
//! events that require immediate persistence (e.g., background suspension).
//!
//! # iOS Integration
//!
//! To integrate with iOS, wire up these handlers in your app delegate:
//!
//! ```swift
//! // In your iOS app delegate:
//! func applicationWillResignActive(_ application: UIApplication) {
//! // Send AppLifecycleEvent::WillResignActive to Bevy
//! }
//!
//! func applicationDidEnterBackground(_ application: UIApplication) {
//! // Send AppLifecycleEvent::DidEnterBackground to Bevy
//! }
//! ```
use crate::persistence::*;
use bevy::prelude::*;
/// Application lifecycle events that require persistence handling
///
/// These events are critical moments where data must be flushed immediately
/// to avoid data loss.
#[derive(Debug, Clone, Message)]
pub enum AppLifecycleEvent {
/// Application will resign active (iOS: `applicationWillResignActive`)
///
/// Sent when the app is about to move from active to inactive state.
/// Example: incoming phone call, user switches to another app
WillResignActive,
/// Application did enter background (iOS: `applicationDidEnterBackground`)
///
/// Sent when the app has moved to the background. The app has approximately
/// 5 seconds to complete critical tasks before suspension.
DidEnterBackground,
/// Application will enter foreground (iOS: `applicationWillEnterForeground`)
///
/// Sent when the app is about to enter the foreground (user returning to app).
WillEnterForeground,
/// Application did become active (iOS: `applicationDidBecomeActive`)
///
/// Sent when the app has become active and is ready to receive user input.
DidBecomeActive,
/// Application will terminate (iOS: `applicationWillTerminate`)
///
/// Sent when the app is about to terminate. Similar to shutdown but from OS.
WillTerminate,
}
/// System to handle iOS lifecycle events and trigger immediate persistence
///
/// This system listens for lifecycle events and performs immediate flushes
/// when the app is backgrounding or terminating.
pub fn lifecycle_event_system(
mut events: MessageReader<AppLifecycleEvent>,
mut write_buffer: ResMut<WriteBufferResource>,
db: Res<PersistenceDb>,
mut metrics: ResMut<PersistenceMetrics>,
mut health: ResMut<PersistenceHealth>,
mut pending_tasks: ResMut<PendingFlushTasks>,
) {
for event in events.read() {
match event {
AppLifecycleEvent::WillResignActive => {
// App is becoming inactive - perform immediate flush
info!("App will resign active - performing immediate flush");
if let Err(e) = force_flush(&mut write_buffer, &db, &mut metrics) {
error!("Failed to flush on resign active: {}", e);
health.record_flush_failure();
} else {
health.record_flush_success();
}
}
AppLifecycleEvent::DidEnterBackground => {
// App entered background - perform immediate flush and checkpoint
info!("App entered background - performing immediate flush and checkpoint");
// Force immediate flush
if let Err(e) = force_flush(&mut write_buffer, &db, &mut metrics) {
error!("Failed to flush on background: {}", e);
health.record_flush_failure();
} else {
health.record_flush_success();
}
// Also checkpoint the WAL to ensure durability
let start = std::time::Instant::now();
match db.lock() {
Ok(mut conn) => {
match checkpoint_wal(&mut conn, CheckpointMode::Passive) {
Ok(_) => {
let duration = start.elapsed();
metrics.record_checkpoint(duration);
health.record_checkpoint_success();
info!("Background checkpoint completed successfully");
}
Err(e) => {
error!("Failed to checkpoint on background: {}", e);
health.record_checkpoint_failure();
}
}
}
Err(e) => {
error!("Failed to acquire database lock for checkpoint: {}", e);
health.record_checkpoint_failure();
}
}
}
AppLifecycleEvent::WillTerminate => {
// App will terminate - perform shutdown sequence
warn!("App will terminate - performing shutdown sequence");
if let Err(e) = shutdown_system(&mut write_buffer, &db, &mut metrics, Some(&mut pending_tasks)) {
error!("Failed to perform shutdown on terminate: {}", e);
} else {
info!("Clean shutdown completed on terminate");
}
}
AppLifecycleEvent::WillEnterForeground => {
// App returning from background - no immediate action needed
info!("App will enter foreground");
}
AppLifecycleEvent::DidBecomeActive => {
// App became active - no immediate action needed
info!("App did become active");
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lifecycle_event_creation() {
let event = AppLifecycleEvent::WillResignActive;
match event {
AppLifecycleEvent::WillResignActive => {
// Success
}
_ => panic!("Event type mismatch"),
}
}
}