Files
Reflect/Shared/Persisence/SharedModelContainer.swift
Trey t 356ce9ea62 Fix build errors, resolve all warnings, and improve code quality
Widget Extension Fixes:
- Create standalone WidgetDataProvider for widget data isolation
- Add WIDGET_EXTENSION compiler flag for conditional compilation
- Fix DataController references in widget-shared files
- Sync widget version numbers with main app (23, 1.0.2)
- Add WidgetBackground color to asset catalog

Warning Resolutions:
- Fix UIScreen.main deprecation in BGView and SharingListView
- Fix Text '+' concatenation deprecation in PurchaseButtonView and SettingsTabView
- Fix exhaustive switch in BiometricAuthManager (add .none case)
- Fix var to let in ExportService (3 instances)
- Fix unused result warning in NoteEditorView
- Fix ForEach duplicate ID warnings in MonthView and YearView

Code Quality Improvements:
- Wrap bypassSubscription in #if DEBUG for security
- Rename StupidAssCustomWidgetObservableObject to CustomWidgetStateViewModel
- Add @MainActor to IconViewModel
- Replace fatalError with graceful fallback in SharedModelContainer
- Add [weak self] to closures in DayViewViewModel
- Add OSLog-based AppLogger for production logging
- Add ImageCache with NSCache for memory efficiency
- Add AccessibilityHelpers with Reduce Motion support
- Create DataControllerProtocol for dependency injection
- Update .gitignore with secrets exclusions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 00:48:35 -06:00

125 lines
4.5 KiB
Swift

//
// SharedModelContainer.swift
// Feels
//
// Factory for creating ModelContainer shared between main app and widget extension.
//
import Foundation
import SwiftData
import os.log
/// Errors that can occur when creating the shared model container
enum SharedModelContainerError: LocalizedError {
case appGroupNotAvailable(String)
case modelContainerCreationFailed(Error)
var errorDescription: String? {
switch self {
case .appGroupNotAvailable(let groupID):
return "App Group container not available for: \(groupID)"
case .modelContainerCreationFailed(let error):
return "Failed to create ModelContainer: \(error.localizedDescription)"
}
}
}
enum SharedModelContainer {
private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "com.tt.ifeel", category: "SharedModelContainer")
/// Creates a ModelContainer with the appropriate configuration for app group sharing
/// - Parameter useCloudKit: Whether to enable CloudKit sync
/// - Returns: Configured ModelContainer
/// - Throws: SharedModelContainerError if creation fails
static func create(useCloudKit: Bool = false) throws -> ModelContainer {
let schema = Schema([MoodEntryModel.self])
let storeURL = try Self.storeURL
let configuration: ModelConfiguration
if useCloudKit {
// CloudKit-enabled configuration
configuration = ModelConfiguration(
schema: schema,
url: storeURL,
cloudKitDatabase: .private(cloudKitContainerID)
)
} else {
// Local-only configuration
configuration = ModelConfiguration(
schema: schema,
url: storeURL,
cloudKitDatabase: .none
)
}
do {
return try ModelContainer(for: schema, configurations: [configuration])
} catch {
logger.error("Failed to create ModelContainer: \(error.localizedDescription)")
throw SharedModelContainerError.modelContainerCreationFailed(error)
}
}
/// Creates a ModelContainer, falling back to in-memory storage if shared container fails
/// - Parameter useCloudKit: Whether to enable CloudKit sync
/// - Returns: Configured ModelContainer (shared or in-memory fallback)
static func createWithFallback(useCloudKit: Bool = false) -> ModelContainer {
do {
return try create(useCloudKit: useCloudKit)
} catch {
logger.warning("Falling back to in-memory storage due to: \(error.localizedDescription)")
// Fall back to in-memory storage
let schema = Schema([MoodEntryModel.self])
let config = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
do {
return try ModelContainer(for: schema, configurations: [config])
} catch {
// This should never happen with in-memory storage, but handle it gracefully
logger.critical("Failed to create even in-memory ModelContainer: \(error.localizedDescription)")
preconditionFailure("Unable to create ModelContainer: \(error)")
}
}
}
/// The URL for the SwiftData store in the shared app group container
/// - Throws: SharedModelContainerError if app group is not available
static var storeURL: URL {
get throws {
guard let containerURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: appGroupID
) else {
logger.error("App Group container not available for: \(appGroupID)")
throw SharedModelContainerError.appGroupNotAvailable(appGroupID)
}
return containerURL.appendingPathComponent(storeFileName)
}
}
/// App Group identifier based on build configuration
static var appGroupID: String {
#if DEBUG
return Constants.groupShareIdDebug
#else
return Constants.groupShareId
#endif
}
/// CloudKit container identifier based on build configuration
static var cloudKitContainerID: String {
#if DEBUG
return "iCloud.com.tt.ifeelDebug"
#else
return "iCloud.com.tt.ifeel"
#endif
}
/// Store file name based on build configuration
static var storeFileName: String {
#if DEBUG
return "Feels-Debug.store"
#else
return "Feels.store"
#endif
}
}