Posts

Post not yet marked as solved
0 Replies
403 Views
I work on a MacOS app (which has a companion iOS app) that uses Core Data with NSPersistentCloudKitContainer. The app also supports widgets and hence there is a need to migrate the persistent store within the core data stack using replacePersistentStore( at:.... During development I recently created a new model version and added a new entity, replaced some attributes etc... Working on the iOS app is fine because deleting the app clears all the data allowing me to work with a clean slate. On MacOS, I initially thought that I could simply navigate to the app group file location, delete the .sqlite file, along with the sqlite-shm and sqlite-wal. I also went and deleted the CloudKit related files. I did all of this out of pure ignorance - my expectation was that it would give me a clean slate, but it did not. This instead gave me some unpredictable behaviour, but the app was always in a bad state. the issues I saw were; • migration failure, • sqlite errors highlighting no such column: t0 - where all the new entity details were missing in sqlite completely After finding a post in the forums about how to reset macOS correctly, I did this instead - do { try container.persistentStoreCoordinator.destroyPersistentStore(at: container.persistentStoreDescriptions.first!.url!, type: .sqlite, options: nil) try container.persistentStoreCoordinator.destroyPersistentStore(at: storeURL, type: .sqlite, options: nil) } catch { print(String(describing: error)) } And now I am back to the ongoing error of This NSPersistentStoreCoordinator has no persistent stores (schema mismatch or migration failure). It cannot perform a save operation. Another thing to note - whenever running the destroyPersistentStore( I have tried this on both the URLs of the old store location and the new one (in the app group). This still doesn't seem to help. AND I noticed that while destroyPersistentStore does get rid of the .sqlite file, it does not delete the sqlite-shm and sqlite-wal - could this be the problem? and do I need to delete these manually? public class CoreDataManager { public static let shared = CoreDataManager() private enum Constants { #if os(macOS) static let appGroupName = "2MM2V2959F.wabitime.group" #elseif os(iOS) static let appGroupName = "group.wabitime" #endif static let containerName = "WabiTimeDataModel" /// The name of the sql database file static let databaseName = "wabitime_database" /// The identifier for the container static let containerIdentifier = "iCloud.com.jslomowitz.WabiTime" } public lazy var context = persistentContainer.viewContext lazy var managedObjectModel: NSManagedObjectModel = { guard let wabiDataBundle = Bundle.module.url( forResource: Constants.containerName, withExtension: "momd" ), let managedObjectModel = NSManagedObjectModel(contentsOf: wabiDataBundle) else { assertionFailure("cannot find managedObjectModel") return NSManagedObjectModel() } return managedObjectModel }() lazy var persistentContainer: NSPersistentCloudKitContainer = { let container = NSPersistentCloudKitContainer( name: Constants.containerName, managedObjectModel: managedObjectModel ) /// URL of the old sql database that has not been relocated to the app group var oldStoreURL: URL? { guard let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url, FileManager.default.fileExists(atPath: url.path) else { return nil } return url } /// URL of the sql database in the app group var storeURL: URL { guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: Constants.appGroupName) else { fatalError("Shared file container could not be created") } return fileContainer.appendingPathComponent("\(Constants.databaseName).sqlite") } // assign the shared container if the old store has been deleted if oldStoreURL == nil { let description = NSPersistentStoreDescription(url: storeURL) description.shouldInferMappingModelAutomatically = true description.shouldMigrateStoreAutomatically = true container.persistentStoreDescriptions = [description] } // perform store migration if necessary if let url = oldStoreURL, url.absoluteString != storeURL.absoluteString { let coordinator = container.persistentStoreCoordinator do { let storeOptions = [ NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true ] try coordinator.replacePersistentStore( at: url, withPersistentStoreFrom: storeURL, sourceOptions: storeOptions, type: .sqlite ) } catch { print(error.localizedDescription) } self.deleteOldStore(with: url) } let options = NSPersistentCloudKitContainerOptions(containerIdentifier: Constants.containerIdentifier) guard let description = container.persistentStoreDescriptions.first else { fatalError("Could not retrieve a persistent store description.") } description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) description.cloudKitContainerOptions = options container.loadPersistentStores(completionHandler: { [weak self] (_, error) in guard let self, error == nil else { assertionFailure("Unresolved error: \(String(describing: error))") return } }) container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType) return container }() private func deleteOldStore(with url: URL) { let fileCoordinator = NSFileCoordinator() fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil) { url in do { try FileManager.default.removeItem(at: url) } catch { print(error.localizedDescription) } } } // MARK: - Core Data Saving and Undo support func saveContext(completion: (() -> Void)? = nil) { #if os(macOS) if !context.commitEditing() { NSLog("AppDelegate unable to commit editing before saving") } #endif if context.hasChanges { do { try context.save() print("SAVED") completion?() } catch { let nserror = error as NSError #if os(macOS) NSApplication.shared.presentError(nserror) #endif } } } }
Posted Last updated
.
Post not yet marked as solved
1 Replies
1.4k Views
I am using a preview with my view that looks like this - #Preview { PaywallTwo(store: MyStore()) } In this scenario, MyStore is a MainActor, but has an initialiser doing some async work inside a Task from the initialiser. I am not sure if this is an implementation error, or perhaps something wrong with the new #Preview, as this was working fine with PreviewProvider I currently have complete strict concurrency, and the error I get is: call to main actor-isolated initializer 'init()' in a synchronous nonisolated context
Posted Last updated
.
Post not yet marked as solved
2 Replies
852 Views
I have created the following views inside a playground. I want to set views within a ZStack to appear and disappear over time. I have debugged the attributes being passed over and have found everything seems to be perfectly fine there. Seems to be a matter of the View itself. This works fine in a playground, but will not display at all in a live activity. Is there an issue doing this with Widgets or is it not supported? import SwiftUI import WidgetKit import PlaygroundSupport struct CyclesView: View {   let dateRange: ClosedRange<Date>   @State private var shouldShow = false   var body: some View {     viewBody()       .padding()       .opacity(shouldShow ? 1.0 : 0.0)   }      @ViewBuilder   private func viewBody() -> some View {     VStack(alignment: .trailing) {       Text(         timerInterval: dateRange)     }     .task {       await hideAfterDelay()     }     .task {       await showAfterDelay()     }   }   private func showAfterDelay() async {     try? await Task.sleep(for: .seconds(dateRange.lowerBound.timeIntervalSinceNow))     shouldShow = true   }      private func hideAfterDelay() async {     try? await Task.sleep(for: .seconds(dateRange.upperBound.timeIntervalSinceNow))     shouldShow = false   } } struct CyclesStack: View {   static let referenceDate = Date()   let timelines: [ClosedRange<Date>] = [     referenceDate...referenceDate.addingTimeInterval(10),     referenceDate.addingTimeInterval(10)...referenceDate.addingTimeInterval(20),     referenceDate.addingTimeInterval(20)...referenceDate.addingTimeInterval(30),     referenceDate.addingTimeInterval(30)...referenceDate.addingTimeInterval(40)   ]      var body: some View {     ZStack {       ForEach(timelines, id: \.lowerBound) { range in         CyclesView(           dateRange: range         )       }     }     .padding()   } } PlaygroundPage.current.setLiveView(CyclesStack()   .frame(width: 200, height: 100))
Posted Last updated
.
Post not yet marked as solved
1 Replies
950 Views
I have the following model in place for navigating to various pages: enum Destination: Hashable {   case today   case activity   case settings(path: SettingsPath? = nil)   case project(project: ProjectItem? = nil)      enum SettingsPath: Hashable {     case cycles     case donations   } } In an ObservableObject, I'm using @Published var sidebarDestination: Destination? = .today And then in various NavigationLink buttons, I'm using the following initializer -  NavigationLink(value: NavigationModel.Destination.activity... In the detail section of my NavigationSplitView I'm using a switch like so detail: {       if let destination = navigationModel.sidebarDestination {         switch destination {         case .today: TodayView()         case .project(let project):           // FIXME: Why is the detail view not updating?             if let selectedProject = project {               ProjectDetailView(project: selectedProject)             } else {               EmptyView()             } ... I've noticed that the pages with an enum case with an associated value are not updating correctly - the title on the page will update, but none of the other content. The pages with an enum case without an associated type seem to work just fine. All of this is using an iPad - the larger screen sizes Is this a bug?
Posted Last updated
.