diff --git a/Targets/App/Sources/Main/Dependencies.swift b/Targets/App/Sources/Main/Dependencies.swift new file mode 100644 index 0000000..c061a05 --- /dev/null +++ b/Targets/App/Sources/Main/Dependencies.swift @@ -0,0 +1,21 @@ +import FlinkyCore +import SwiftData + +@MainActor +struct Dependencies { + let toastManager = ToastManager() + let appHealthObserver = AppHealthObserver() + let metricKitManager = MetricKitManager() + + private let _modelContainer: LazyThrowingValue + + var modelContainer: ModelContainer { + get throws { try _modelContainer.resolve() } + } + + init(isTestingEnabled: Bool) { + _modelContainer = LazyThrowingValue { + try SharedModelContainerFactory.make(isStoredInMemoryOnly: isTestingEnabled) + } + } +} diff --git a/Targets/App/Sources/Main/FlinkyApp.swift b/Targets/App/Sources/Main/FlinkyApp.swift index 0e389ef..512f393 100644 --- a/Targets/App/Sources/Main/FlinkyApp.swift +++ b/Targets/App/Sources/Main/FlinkyApp.swift @@ -9,33 +9,32 @@ import os.log struct FlinkyApp: App { private static let logger = Logger.forType(Self.self) - private static let sharedToastManager = ToastManager() - @StateObject private var toastManager = FlinkyApp.sharedToastManager - private let sharedModelContainer: ModelContainer + private static let isTestingEnabled = ProcessInfo.processInfo.isTestingEnabled + private static let deps = Dependencies(isTestingEnabled: Self.isTestingEnabled) + + @StateObject private var toastManager = Self.deps.toastManager + private let modelContainer: ModelContainer init() { - let isTestingEnabled = ProcessInfo.processInfo.isTestingEnabled SentrySDK.start { options in - Self.configureSentry(options: options, toastManager: Self.sharedToastManager, isTestingEnabled: isTestingEnabled) + Self.configureSentry(options: options, toastManager: Self.deps.toastManager, isTestingEnabled: Self.isTestingEnabled) } // Start app health observation for system-level metrics // (thermal state, network reachability, app state transitions) - AppHealthObserver.shared.startObserving() + Self.deps.appHealthObserver.startObserving() // Subscribe to MetricKit payloads and report all values as Sentry metrics - MetricKitManager.shared.startReceiving() + Self.deps.metricKitManager.startReceiving() do { - sharedModelContainer = try SharedModelContainerFactory.make( - isStoredInMemoryOnly: isTestingEnabled - ) + modelContainer = try Self.deps.modelContainer } catch { + SentrySDK.capture(error: error) fatalError("Failed to create shared ModelContainer: \(error)") } - // Seed if needed on first app launch - DataSeedingService.seedDataIfNeeded(modelContext: sharedModelContainer.mainContext) + DataSeedingService.seedDataIfNeeded(modelContext: modelContainer.mainContext) } /// Configures the Sentry SDK options. @@ -63,11 +62,11 @@ struct FlinkyApp: App { options.releaseName = "\(bundleId ?? "unknown")@\(version ?? "unknown")+\(build ?? "unknown")" func pickEnvValue(production: T, develop: T) -> T { - #if DEBUG - return develop - #else - return production - #endif +#if DEBUG + return develop +#else + return production +#endif } options.environment = pickEnvValue(production: "production", develop: "development") @@ -247,6 +246,6 @@ struct FlinkyApp: App { options.publicKey = "30d2f7cc2fa469eaf8e4bdf958ad9d66bce491a7da1fb08ff0a7156a8e15a47d" } } - .modelContainer(sharedModelContainer) + .modelContainer(modelContainer) } } diff --git a/Targets/App/Sources/Services/AppHealthObserver.swift b/Targets/App/Sources/Services/AppHealthObserver.swift index 49979e0..6c1328b 100644 --- a/Targets/App/Sources/Services/AppHealthObserver.swift +++ b/Targets/App/Sources/Services/AppHealthObserver.swift @@ -17,11 +17,6 @@ import UIKit /// Reference: https://github.com/getsentry/sentry-cocoa/issues/7000 final class AppHealthObserver { - // MARK: - Singleton - - /// Shared instance of the app health observer. - static let shared = AppHealthObserver() - // MARK: - Properties private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "Flinky", category: "AppHealthObserver") @@ -36,10 +31,6 @@ final class AppHealthObserver { private var isObserving = false - // MARK: - Initialization - - private init() {} - // MARK: - Public Methods /// Starts observing app health signals. diff --git a/Targets/App/Sources/Services/MetricKitManager.swift b/Targets/App/Sources/Services/MetricKitManager.swift index 9e992e7..0063018 100644 --- a/Targets/App/Sources/Services/MetricKitManager.swift +++ b/Targets/App/Sources/Services/MetricKitManager.swift @@ -6,22 +6,12 @@ import SentrySwift final class MetricKitManager: NSObject, MXMetricManagerSubscriber { - // MARK: - Singleton - - static let shared = MetricKitManager() - // MARK: - Properties private static let logger = Logger(subsystem: Bundle.main.bundleIdentifier ?? "Flinky", category: "MetricKitManager") private var isReceiving = false - // MARK: - Initialization - - private override init() { - super.init() - } - // MARK: - Public Methods func startReceiving() { diff --git a/Targets/App/Sources/Utils/LazyThrowingValue.swift b/Targets/App/Sources/Utils/LazyThrowingValue.swift new file mode 100644 index 0000000..dd16277 --- /dev/null +++ b/Targets/App/Sources/Utils/LazyThrowingValue.swift @@ -0,0 +1,15 @@ +final class LazyThrowingValue { + private var cached: T? + private let factory: () throws -> T + + init(_ factory: @escaping () throws -> T) { + self.factory = factory + } + + func resolve() throws -> T { + if let cached { return cached } + let value = try factory() + cached = value + return value + } +}