1 contributor
import SwiftUI
import SwiftData
@main
struct HealthProbeApp: App {
@State private var appSettings = AppSettings()
var sharedModelContainer: ModelContainer = {
destroyLegacyStoreIfNeeded()
do {
return try createModelContainer()
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
.environment(appSettings)
}
.modelContainer(sharedModelContainer)
}
// Removes the legacy store to avoid schema incompatibility.
// The old store used TypeDistributionBin which is no longer in the schema.
private static func destroyLegacyStoreIfNeeded() {
let storeURL = URL.applicationSupportDirectory.appending(path: "HealthProbe.store")
guard FileManager.default.fileExists(atPath: storeURL.path()) else { return }
let candidates = [
storeURL,
storeURL.appendingPathExtension("shm"),
storeURL.appendingPathExtension("wal"),
]
for url in candidates { try? FileManager.default.removeItem(at: url) }
}
// Two separate ModelConfiguration instances:
// cloudKitConfig — CloudKit-enabled for audit data; OperationLog intentionally excluded
// localConfig — local-only for OperationLog audit trail; never synced
//
// ⚠️ DeviceProfile is kept local-only (not synced to CloudKit) since it's device-specific
// cosmetic data (name, color tag) that should not cross devices.
private static func createModelContainer() throws -> ModelContainer {
let cloudKitModels = Schema([
HealthSnapshot.self,
TypeCount.self,
YearlyCount.self,
SnapshotDelta.self,
TypeDelta.self,
AnomalyRecord.self,
])
let localModels = Schema([
OperationLog.self,
DeviceProfile.self,
])
let appSupportURL = URL.applicationSupportDirectory
try FileManager.default.createDirectory(at: appSupportURL, withIntermediateDirectories: true)
let cloudConfig: ModelConfiguration
let localConfig = ModelConfiguration(
"local",
schema: localModels,
url: appSupportURL.appending(path: "HealthProbeLocal.store"),
cloudKitDatabase: .none
)
#if targetEnvironment(simulator)
cloudConfig = ModelConfiguration(
"cloud",
schema: cloudKitModels,
url: appSupportURL.appending(path: "HealthProbeCloud.store"),
cloudKitDatabase: .none
)
#else
// For production, set up the CloudKit container in Apple Developer Portal.
// Container must be: "iCloud." + PRODUCT_BUNDLE_IDENTIFIER
cloudConfig = ModelConfiguration(
"cloud",
schema: cloudKitModels,
cloudKitDatabase: .private("iCloud.ro.xdev.HealthProbe")
)
#endif
let fullSchema = Schema([
HealthSnapshot.self, TypeCount.self, YearlyCount.self,
SnapshotDelta.self, TypeDelta.self, AnomalyRecord.self,
OperationLog.self, DeviceProfile.self,
])
do {
return try ModelContainer(for: fullSchema, configurations: [cloudConfig, localConfig])
} catch {
// Recover from schema migration failures by removing the stores and retrying once
let candidates: [URL] = [
appSupportURL.appending(path: "HealthProbeCloud.store"),
appSupportURL.appending(path: "HealthProbeCloud.store.shm"),
appSupportURL.appending(path: "HealthProbeCloud.store.wal"),
appSupportURL.appending(path: "HealthProbeLocal.store"),
appSupportURL.appending(path: "HealthProbeLocal.store.shm"),
appSupportURL.appending(path: "HealthProbeLocal.store.wal"),
]
for url in candidates { try? FileManager.default.removeItem(at: url) }
return try ModelContainer(for: fullSchema, configurations: [cloudConfig, localConfig])
}
}
}