Post

Replies

Boosts

Views

Activity

Reply to Syncing Local Notifications using CloudKit
I implement this using (CloudCore for sync, and) an Observer object that reacts to changes in CoreData. Whether the change comes locally or synced, the notifications get queued/presented locally on all the users' devices. For example final class RemindersObserver: NSObject, NSFetchedResultsControllerDelegate {     private let persistentContainer: NSPersistentContainer     private let frc: NSFetchedResultsController<Reminder>              init(persistentContainer: NSPersistentContainer) {         self.persistentContainer = persistentContainer                  let moc = persistentContainer.newBackgroundContext()         moc.automaticallyMergesChangesFromParent = true         let fetchRequest: NSFetchRequest<Reminder> = Reminder.fetchRequest()         fetchRequest.sortDescriptors = [NSSortDescriptor(key: "moveBy", ascending: false)]                  frc = NSFetchedResultsController(fetchRequest: fetchRequest,                                          managedObjectContext: moc,                                          sectionNameKeyPath: nil,                                          cacheName: "RemindersObserver")                  super.init()                  frc.delegate = self         try? frc.performFetch()     }          func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         let noteCenter = UNUserNotificationCenter.current()         noteCenter.removeAllPendingNotificationRequests()                  guard let reminders = frc.fetchedObjects else { return }                  for reminder in reminders {             guard let car = reminder.car, let uuidString = car.uuid else { continue }                          for countdown in Countdown.allCases {                 let content = UNMutableNotificationContent()                 content.categoryIdentifier = "reminderNotification"                 content.title = "Move the " + car.name!                 if countdown == .expired {                     content.subtitle = "Parking has expired!"                 } else {                     content.subtitle = "Parking expires in " + countdown.timeInterval().toString()                 }                 let notificationDate = reminder.moveBy! - countdown.timeInterval()                 let components: Set<Calendar.Component> = [.second, .minute, .hour, .day]                 let dateComponents = Calendar.current.dateComponents(components, from: notificationDate)                 let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)                 let request = UNNotificationRequest(identifier: uuidString + countdown.rawValue, content: content, trigger: trigger)                 noteCenter.add(request) { error in                     //                 }             }         }         WidgetCenter.shared.reloadTimelines(ofKind: Identifiers.carWidget)     } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
May ’21
Reply to CoreData, @FetchRequest, @ObservedObject and View Updating
fwiw, my experiences with DiffableDataSources and CoreData is that they are great at detecting additions and deletions (because the managed object IDs in the fetched results are different) but don't work for updates (because the ids stay the same, only the properties change). I thought I saw a very brief comment in a video about updating DiffableDataSources due to property changes, but damn if I can remember which video that was.
Topic: App & System Services SubTopic: Core OS Tags:
Jun ’21
Reply to CoreData, @FetchRequest, @ObservedObject and View Updating
also fwiw, in UIKit, I solve for this by observing the changed records of the FetchedRsultsController, and specifically reloading them in the diffable data source. …     func updateSnapshot() {         var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<String, Car>()         frc.sections?.forEach { section in             diffableDataSourceSnapshot.appendSections([section.name])             diffableDataSourceSnapshot.appendItems(section.objects as! [Car], toSection: section.name)         }         diffableDataSourceSnapshot.reloadItems(changedCars)                  diffableDataSource?.apply(diffableDataSourceSnapshot, animatingDifferences: true)     }      } extension CarsListViewController: NSFetchedResultsControllerDelegate {          func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {                  if type == .update, let car = anObject as? Car {             changedCars.append(car)         }     }        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         self.updateSnapshot()         changedCars.removeAll()     } } Note this means I am not using the fancy new FRC delegate callbacks with snapshots, because they only have adds/deletes, no updates.
Topic: App & System Services SubTopic: Core OS Tags:
Jun ’21
Reply to CloudKit and CoreData synchronization
In CoreData, NSFetchRequest is used to perform a single fetch, whereas NSFetchedResultsController provides a continuous view of data matching your predicates. Furthermore, for SwiftUI, you should look at the property wrapper @FetchRequest, which in fact implements a NSFetchResultsController, and can be used instead of your ItemsModel class. Hope this helps.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Jun ’21
Reply to Why an invisible same cell is being updated when update is performing using NSFetchedResultsController and Diffable Data Source?
sigh… for reasons that boggle my mind, the snapshot returned by the FRC let snapshot = snapshotReference as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID> DOES NOT properly handle object updates, only inserts, deletions, and reorder. Its infuriating, frankly. So I have resorted to implementing my FRC delegate like this…     var changedCars: [Car] = []     func updateSnapshot() {         var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<String, Car>()         frc.sections?.forEach { section in             diffableDataSourceSnapshot.appendSections([section.name])             diffableDataSourceSnapshot.appendItems(section.objects as! [Car], toSection: section.name)         }         diffableDataSourceSnapshot.reloadItems(changedCars)                  diffableDataSource?.apply(diffableDataSourceSnapshot, animatingDifferences: true)     }          func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {                  if type == .update, let car = anObject as? Car {             changedCars.append(car)         }     }          func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         self.updateSnapshot()                  changedCars.removeAll()     }
Jun ’21
Reply to Is NSAsynchronousFetchRequest suitable for production app?
There are several issues here. CoreData is very specifically designed to manage memory efficiently, especially with large data sets. But you are correct that some queries can take a long time, and if performed on the main thread, can cause a hitch. When a query is performed, however, not all the data is loaded. https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html When configured with NSManageObjects, DiffableDataSource uses only the object IDs to detect changes, and the snapshots currently returned by FRC (as of iOS 15 beta 1) only include .inserts, .deletes, and .moves, but not .updates. If you want to handle updates, you have to specifically call snapshot.reload or snapshot.refresh for the changes objects. You can configure an NSFetchedResultsController to use a background NSManagedObjectContext. Any access to properties of the found objects must be done within a moc.perform { } block, or use the objectIDs to retrieve the objects on the main thread viewContext. Another idea is to re-create the behavior of FRCs by listening to CoreData notifications directly. See https://developer.apple.com/documentation/foundation/nsnotification/name/1506884-nsmanagedobjectcontextobjectsdid
Jun ’21
Reply to CloudKit public database subscription doesn't reach all users
It appears that subscribing to the public database is deprecated. CoreData's support for CloudKit public database does polling. See https://developer.apple.com/videos/play/wwdc2020/10650/ with a reference at the 7:30 mark, and a more-detailed discussion starting at 10:20. I would recommend the entire video, even if you're rolling your own CloudKit implementation, as it has lots of insights into how achieve sync without the tombstone magic in CKFetchRecordsZoneChangesOperation.
Jul ’21
Reply to Syncing Local Notifications using CloudKit
I implement this using (CloudCore for sync, and) an Observer object that reacts to changes in CoreData. Whether the change comes locally or synced, the notifications get queued/presented locally on all the users' devices. For example final class RemindersObserver: NSObject, NSFetchedResultsControllerDelegate {     private let persistentContainer: NSPersistentContainer     private let frc: NSFetchedResultsController<Reminder>              init(persistentContainer: NSPersistentContainer) {         self.persistentContainer = persistentContainer                  let moc = persistentContainer.newBackgroundContext()         moc.automaticallyMergesChangesFromParent = true         let fetchRequest: NSFetchRequest<Reminder> = Reminder.fetchRequest()         fetchRequest.sortDescriptors = [NSSortDescriptor(key: "moveBy", ascending: false)]                  frc = NSFetchedResultsController(fetchRequest: fetchRequest,                                          managedObjectContext: moc,                                          sectionNameKeyPath: nil,                                          cacheName: "RemindersObserver")                  super.init()                  frc.delegate = self         try? frc.performFetch()     }          func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         let noteCenter = UNUserNotificationCenter.current()         noteCenter.removeAllPendingNotificationRequests()                  guard let reminders = frc.fetchedObjects else { return }                  for reminder in reminders {             guard let car = reminder.car, let uuidString = car.uuid else { continue }                          for countdown in Countdown.allCases {                 let content = UNMutableNotificationContent()                 content.categoryIdentifier = "reminderNotification"                 content.title = "Move the " + car.name!                 if countdown == .expired {                     content.subtitle = "Parking has expired!"                 } else {                     content.subtitle = "Parking expires in " + countdown.timeInterval().toString()                 }                 let notificationDate = reminder.moveBy! - countdown.timeInterval()                 let components: Set<Calendar.Component> = [.second, .minute, .hour, .day]                 let dateComponents = Calendar.current.dateComponents(components, from: notificationDate)                 let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)                 let request = UNNotificationRequest(identifier: uuidString + countdown.rawValue, content: content, trigger: trigger)                 noteCenter.add(request) { error in                     //                 }             }         }         WidgetCenter.shared.reloadTimelines(ofKind: Identifiers.carWidget)     } }
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
May ’21
Reply to CoreData, @FetchRequest, @ObservedObject and View Updating
fwiw, my experiences with DiffableDataSources and CoreData is that they are great at detecting additions and deletions (because the managed object IDs in the fetched results are different) but don't work for updates (because the ids stay the same, only the properties change). I thought I saw a very brief comment in a video about updating DiffableDataSources due to property changes, but damn if I can remember which video that was.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jun ’21
Reply to CoreData, @FetchRequest, @ObservedObject and View Updating
also fwiw, in UIKit, I solve for this by observing the changed records of the FetchedRsultsController, and specifically reloading them in the diffable data source. …     func updateSnapshot() {         var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<String, Car>()         frc.sections?.forEach { section in             diffableDataSourceSnapshot.appendSections([section.name])             diffableDataSourceSnapshot.appendItems(section.objects as! [Car], toSection: section.name)         }         diffableDataSourceSnapshot.reloadItems(changedCars)                  diffableDataSource?.apply(diffableDataSourceSnapshot, animatingDifferences: true)     }      } extension CarsListViewController: NSFetchedResultsControllerDelegate {          func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {                  if type == .update, let car = anObject as? Car {             changedCars.append(car)         }     }        func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         self.updateSnapshot()         changedCars.removeAll()     } } Note this means I am not using the fancy new FRC delegate callbacks with snapshots, because they only have adds/deletes, no updates.
Topic: App & System Services SubTopic: Core OS Tags:
Replies
Boosts
Views
Activity
Jun ’21
Reply to CloudKit and CoreData synchronization
In CoreData, NSFetchRequest is used to perform a single fetch, whereas NSFetchedResultsController provides a continuous view of data matching your predicates. Furthermore, for SwiftUI, you should look at the property wrapper @FetchRequest, which in fact implements a NSFetchResultsController, and can be used instead of your ItemsModel class. Hope this helps.
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jun ’21
Reply to Core Data TableView Display Problem
change let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "MyNumberDataRegistration") to let fetchRequest = NSFetchRequest<MyNumberDataRegistration>(entityName: "MyNumberDataRegistration")
Replies
Boosts
Views
Activity
Jun ’21
Reply to CloudKit - 503 THROTTLED "public/users/discover"
I've had similar issues using the iOS-side discoverAllUsers API for users with large address books. For me, its one call (operation) but underneath the hood, CloudKit is taking chunks of contacts and fetching identities. Would be great is Apple special-cased the discoverAllUsers API to not hit these throttling limits per client-call.
Replies
Boosts
Views
Activity
Jun ’21
Reply to (Swift + CloudKit) or (SwiftUi + Core Data + CloudKit)
CoreData is definitely not "falling off" :-) Use SwiftUI+CoreData and sync with CloudKit, using either NSPersistentCloudKitContainer, an open-source sync engine like CloudCore, or a third-party sync engine like Ensembles.io
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Replies
Boosts
Views
Activity
Jun ’21
Reply to cloud kit query record dont working
did you add indexes to the fields you're querying on? This has to be done in the console.
Replies
Boosts
Views
Activity
Jun ’21
Reply to Why an invisible same cell is being updated when update is performing using NSFetchedResultsController and Diffable Data Source?
sigh… for reasons that boggle my mind, the snapshot returned by the FRC let snapshot = snapshotReference as NSDiffableDataSourceSnapshot<Int, NSManagedObjectID> DOES NOT properly handle object updates, only inserts, deletions, and reorder. Its infuriating, frankly. So I have resorted to implementing my FRC delegate like this…     var changedCars: [Car] = []     func updateSnapshot() {         var diffableDataSourceSnapshot = NSDiffableDataSourceSnapshot<String, Car>()         frc.sections?.forEach { section in             diffableDataSourceSnapshot.appendSections([section.name])             diffableDataSourceSnapshot.appendItems(section.objects as! [Car], toSection: section.name)         }         diffableDataSourceSnapshot.reloadItems(changedCars)                  diffableDataSource?.apply(diffableDataSourceSnapshot, animatingDifferences: true)     }          func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) {                  if type == .update, let car = anObject as? Car {             changedCars.append(car)         }     }          func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {         self.updateSnapshot()                  changedCars.removeAll()     }
Replies
Boosts
Views
Activity
Jun ’21
Reply to Should I use weak to avoid from interferencing with CoreData memory management?
It is safe to use strong references to managedObjects in table/collection view cells. There is a limited pool of cells that get reused as the table/collection scrolls, and CoreData does far more to manage memory behind the managedObject (i.e. faulting).
Replies
Boosts
Views
Activity
Jun ’21
Reply to Is NSAsynchronousFetchRequest suitable for production app?
There are several issues here. CoreData is very specifically designed to manage memory efficiently, especially with large data sets. But you are correct that some queries can take a long time, and if performed on the main thread, can cause a hitch. When a query is performed, however, not all the data is loaded. https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html When configured with NSManageObjects, DiffableDataSource uses only the object IDs to detect changes, and the snapshots currently returned by FRC (as of iOS 15 beta 1) only include .inserts, .deletes, and .moves, but not .updates. If you want to handle updates, you have to specifically call snapshot.reload or snapshot.refresh for the changes objects. You can configure an NSFetchedResultsController to use a background NSManagedObjectContext. Any access to properties of the found objects must be done within a moc.perform { } block, or use the objectIDs to retrieve the objects on the main thread viewContext. Another idea is to re-create the behavior of FRCs by listening to CoreData notifications directly. See https://developer.apple.com/documentation/foundation/nsnotification/name/1506884-nsmanagedobjectcontextobjectsdid
Replies
Boosts
Views
Activity
Jun ’21
Reply to icloud on = automatic hoover up of your photos
Create a test account to do your development and testing.
Replies
Boosts
Views
Activity
Jun ’21
Reply to CloudKit public database subscription doesn't reach all users
It appears that subscribing to the public database is deprecated. CoreData's support for CloudKit public database does polling. See https://developer.apple.com/videos/play/wwdc2020/10650/ with a reference at the 7:30 mark, and a more-detailed discussion starting at 10:20. I would recommend the entire video, even if you're rolling your own CloudKit implementation, as it has lots of insights into how achieve sync without the tombstone magic in CKFetchRecordsZoneChangesOperation.
Replies
Boosts
Views
Activity
Jul ’21
Reply to CKQuery predicate fails with ANY-BEGINSWITH
Do you have a Searchable index on favoriteColors?
Replies
Boosts
Views
Activity
Jul ’21
Reply to How to clean up old NSPersistentStore files after migrating Core Data with CloudKit to App Group container
Hmm
Replies
Boosts
Views
Activity
Jul ’21