Files
InvestmentTrackerApp/PortfolioJournal/App/PortfolioJournalApp.swift
T
alexandrev-tibco 94ed4d17eb Release 1.3.0: onboarding UX, charts paywall banner, re-engagement notifications, Crashlytics
- Crashlytics: add FirebaseCrashlytics framework + dSYM upload build phase
- Onboarding: useSampleData off by default, Add First Investment as primary CTA
- AddSourceView: contextual placeholder and footer explaining what a source is
- Charts: CompactPaywallBanner visible to free users on every visit
- Notifications: re-engagement (7d) and monthly check-in local notifications
- Paywall: decorative chart preview replacing static crown icon
- Localization: all new strings in en + es-ES
2026-05-01 09:26:37 +02:00

57 lines
2.5 KiB
Swift

import SwiftUI
@main
struct PortfolioJournalApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject private var iapService: IAPService
@StateObject private var adMobService: AdMobService
@StateObject private var accountStore: AccountStore
@StateObject private var tabSelection = TabSelectionStore()
@Environment(\.scenePhase) private var scenePhase
let coreDataStack = CoreDataStack.shared
init() {
// Clean up any duplicate objects from previous bugs before initializing stores
CoreDataStack.shared.cleanupDuplicateObjects()
let iap = IAPService()
_iapService = StateObject(wrappedValue: iap)
_adMobService = StateObject(wrappedValue: AdMobService())
_accountStore = StateObject(wrappedValue: AccountStore(iapService: iap))
}
var body: some Scene {
WindowGroup {
ContentView()
.environment(\.managedObjectContext, coreDataStack.viewContext)
.environmentObject(coreDataStack)
.environmentObject(iapService)
.environmentObject(adMobService)
.environmentObject(accountStore)
.environmentObject(tabSelection)
}
.onChange(of: scenePhase) { _, newPhase in
if newPhase == .active {
coreDataStack.refreshWidgetData()
// Re-read all Core Data objects from the persistent store so that
// iCloud changes made on other devices while this device was inactive
// are reflected immediately without waiting for a remote-change notification.
coreDataStack.refreshFromCloudKit()
NotificationService.shared.scheduleReEngagementNotification()
NotificationService.shared.scheduleMonthlyCheckIn()
} else if newPhase == .background {
guard iapService.isPremium else { return }
guard UserDefaults.standard.bool(forKey: "backupsEnabled") else { return }
let retention = UserDefaults.standard.integer(forKey: "backupRetentionCount")
let keepCount = [5, 10, 20].contains(retention) ? retention : 10
let includeICloud = UserDefaults.standard.bool(forKey: "cloudSyncEnabled")
_ = BackupService.shared.createBackup(
retentionCount: keepCount,
includeICloud: includeICloud
)
}
}
}
}