// // AppDelegate.swift // USB Meter // // Created by Bogdan Timofte on 01/03/2020. // Copyright © 2020 Bogdan Timofte. All rights reserved. // import UIKit import CoreData import CloudKit //let btSerial = BluetoothSerial(delegate: BSD()) let appData = AppData() enum Constants { static let chartUnderscan: CGFloat = 0.5 static let chartOverscan: CGFloat = 1 - chartUnderscan } // MARK: Clock // MARK: Debug public func track(_ message: String = "", file: String = #file, function: String = #function, line: Int = #line ) { let date = Date() let calendar = Calendar.current let hour = calendar.component(.hour, from: date) let minutes = calendar.component(.minute, from: date) let seconds = calendar.component(.second, from: date) print("\(hour):\(minutes):\(seconds) - \(file):\(line) - \(function) \(message)") } @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { private let cloudKitContainerIdentifier = "iCloud.ro.xdev.USB-Meter" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. logCloudKitStatus() return true } private func logCloudKitStatus() { let container = CKContainer(identifier: cloudKitContainerIdentifier) container.accountStatus { status, error in if let error { track("CloudKit account status error: \(error.localizedDescription)") return } let statusDescription: String switch status { case .available: statusDescription = "available" case .noAccount: statusDescription = "noAccount" case .restricted: statusDescription = "restricted" case .couldNotDetermine: statusDescription = "couldNotDetermine" case .temporarilyUnavailable: statusDescription = "temporarilyUnavailable" @unknown default: statusDescription = "unknown" } track("CloudKit account status for \(self.cloudKitContainerIdentifier): \(statusDescription)") } } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } // MARK: - Core Data stack lazy var persistentContainer: NSPersistentCloudKitContainer = { /* The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail. */ let container = NSPersistentCloudKitContainer(name: "CKModel") if let description = container.persistentStoreDescriptions.first { description.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.ro.xdev.USB-Meter") description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) } container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // Log error and attempt recovery instead of crashing the app immediately. NSLog("Core Data store load failed: %s", error.localizedDescription) // Attempt lightweight migration and fallback by resetting the store when migration fails. if let storeURL = storeDescription.url { let coordinator = container.persistentStoreCoordinator let storeType = storeDescription.type do { try coordinator.destroyPersistentStore(at: storeURL, ofType: storeType, options: nil) try coordinator.addPersistentStore(ofType: storeType, configurationName: nil, at: storeURL, options: storeDescription.options) NSLog("Core Data store recovered by destroying and recreating store at %@", storeURL.path) return } catch { NSLog("Core Data recovery attempt failed: %s", (error as NSError).localizedDescription) } } // As a last resort, keep running but note that persistent store is unavailable. // In debug environment this should be investigated further. #if DEBUG fatalError("Unresolved error \(error), \(error.userInfo)") #endif } }) container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy return container }() // MARK: - Core Data Saving support func saveContext () { let context = persistentContainer.viewContext if context.hasChanges { do { try context.save() } catch { let nserror = error as NSError NSLog("Core Data save failed: %@", nserror.localizedDescription) #if DEBUG fatalError("Unresolved error \(nserror), \(nserror.userInfo)") #endif } } } }