Post not yet marked as solved
I am trying to deduplicate data created by NSPersistentCloudKitContainer in my app.
I have a universal app, with Share Extensions on both macOS and iOS. On each platform I share a store between the app and the extension with an App Group. The app and the extensions are both configured to sync to CloudKit. (This means local sharing is handled when offline, and a remote share extension will sync to CloudKit work when the main app is closed)
This configuration is causing duplicates to be generated. I believe this is because when the macOS app is open, both it and the macOS share extension will try and (almost simultaneously) sync a newly shared object, resulting in two copies in CloudKit.
On the macOS app, I can look through the persistent history and see the insertion 'author'. The first insertion is made by the extension "macOSShareExtension", the second is made by "NSCloudKitMirroringDelegate.import". I could easily make a choice to delete the second object.
However, at the same time, on the iOS app, I will get two insertions, both with the author "NSCloudKitMirroringDelegate.import".
I don't want to deduplicate only on the macOS app in this case. That would mean the the iOS app has duplicate objects until the deduplication propagates.
If I use CKRecord's creationDate to keep the first syncd Object, can I guarantee that if one Object has an associated CKRecord and the other doesn't, the one with out will subsequently gain a record with a later creationDate?
Should I be taking a different approach? Thank you.
Post not yet marked as solved
I'm currently trying to save a selected image in core data as a type data, but I'm getting an error in the persistence file marked with a comment. I've included my code and any questions I will gladly answer. Thanks for any help!
Content View:
import SwiftUI
import PhotosUI
struct ContentView: View {
@ObservedObject var persistence = PersistenceController.shared
@State var selectedItems: [PhotosPickerItem] = []
@State var data: Data?
var body: some View {
NavigationView{
VStack{
Spacer()
VStack{
Spacer()
if let data = data, let uiimage = UIImage(data: data) {
Image(uiImage: uiimage)
.resizable()
.scaledToFit()
.frame(width: 250, height: 500)
}
Spacer()
}
Spacer()
PhotosPicker(selection: $selectedItems, maxSelectionCount: 1, matching: .images){
Text("Pick Photo")
}
.onChange(of: selectedItems){ newValue in
guard let item = selectedItems.first else{
return
}
item.loadTransferable(type: Data.self){ result in
switch result {
case .success(let data):
if let data = data{
self.data = data
} else {
print("Data is nil")
}
case .failure(let failure):
fatalError("\(failure)")
}
}
}
Spacer()
}
.navigationBarItems(trailing: addButton)
}
}
var addButton: some View {
Button(action: {
guard let item = selectedItems.first else{
return
}
item.loadTransferable(type: Data.self){ result in
switch result {
case .success(let data):
if let data = data{
persistence.addObject(image: data)
} else {
print("Data is nil")
}
case .failure(let failure):
fatalError("\(failure)")
}
}
}){
Text("Add Image").bold()
}
}
}
Persistence:
import Foundation
import CoreData
class PersistenceController: ObservableObject {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "ReciPlanner")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
func addObject(image: Data){
let context = container.viewContext
let object = Object(context: context) //Error: Thread 7: "An NSManagedObject of class 'Object' must have a valid NSEntityDescription."
object.item = image
}
func contextSave() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
print("**** ERROR: Unable to save context \(error)")
}
}
}
}
Data Model:
Post not yet marked as solved
I have an IOS application who uses coredata
As I want to develop and App extension which needs to share coredata with my app(Yes, I made a group App), I thought a good solution would be to create a framework with the, coredata model.
I did it, but after that the coredata of the main app is no longer necessary.
and I think I made a mistake when removing the file xcdatamodel from my app.
when I refer to an entity in the main application, it doesn't use the entity of the framework.
when I right click on an entity instance in my main application, I see a file which is: entity+CoreDataClass and of course I don't find it in the finder.
Did I made a mistake by simply remove the xcdatamodel from my App?
Do I have something to clean?
Post not yet marked as solved
I'm currently trying to save a selected image in core data as a type data, but I'm getting an error in the persistence file marked with a comment. I've included my code and any questions I will gladly answer. Thanks for any help!
Content View:
import SwiftUI
import PhotosUI
struct ContentView: View {
@ObservedObject var persistence = PersistenceController.shared
@State var selectedItems: [PhotosPickerItem] = []
@State var data: Data?
var body: some View {
NavigationView{
VStack{
Spacer()
VStack{
Spacer()
if let data = data, let uiimage = UIImage(data: data) {
Image(uiImage: uiimage)
.resizable()
.scaledToFit()
.frame(width: 250, height: 500)
}
Spacer()
}
Spacer()
PhotosPicker(selection: $selectedItems, maxSelectionCount: 1, matching: .images){
Text("Pick Photo")
}
.onChange(of: selectedItems){ newValue in
guard let item = selectedItems.first else{
return
}
item.loadTransferable(type: Data.self){ result in
switch result {
case .success(let data):
if let data = data{
self.data = data
} else {
print("Data is nil")
}
case .failure(let failure):
fatalError("\(failure)")
}
}
}
Spacer()
}
.navigationBarItems(trailing: addButton)
}
}
var addButton: some View {
Button(action: {
guard let item = selectedItems.first else{
return
}
item.loadTransferable(type: Data.self){ result in
switch result {
case .success(let data):
if let data = data{
persistence.addObject(image: data)
} else {
print("Data is nil")
}
case .failure(let failure):
fatalError("\(failure)")
}
}
}){
Text("Add Image").bold()
}
}
}
Persistence:
import Foundation
import CoreData
class PersistenceController: ObservableObject {
static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "ReciPlanner")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.automaticallyMergesChangesFromParent = true
}
func addObject(image: Data){
let context = container.viewContext
let object = Object(context: context) //Error: Thread 7: "An NSManagedObject of class 'Object' must have a valid NSEntityDescription."
object.item = image
}
func contextSave() {
let context = container.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
print("**** ERROR: Unable to save context \(error)")
}
}
}
}
Data Model:
Post not yet marked as solved
Yesterday I had issues with my CoreData database for my Multiplatform app which resulted in changes to the schema. I reset the CloudKit version and, on the iOS simulator, had to delete my app and run the code again to get it to work. My issue is with the macOS target. I'm getting the same fatalError (Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134140 "Persistent store migration failed, missing mapping model.") when running the macOS app but I can't figure out how to delete it so I can run it fresh like I did with the iOS version.
I've found where the application is created (in the Debug directory ultimately within my app's Build directory) and removed them all but when run the newly created application has the same error. Should I move up the directory structure and remove the entire app directory within Xcode/DerivedData or is there another way? Wanted an answer before I do something I can't go back from :)
Thanks,
Kyra
I'm new to iOS development. I've created an app in SwiftUI that fetches data from firebase and saves it coredata. I've used Task{}, async and await for my JSON conversions, coredata fetch/save, etc.
The app is working fine and has been uploaded to app store too. Only in some devices with memory issues, the performance degrades. Though the app doesn't crash at any time in any devices, the UI blocks for a fraction of second while some data is being processed.
After a few hours of research regarding background threads, concurrency, managed objects, etc., I found it's not a good practice to use the viewContext for all the purposes.
So, here is why this happens: All the changes, fetch requests, batchdelete etc, are performed on the viewContect from the persistanceController. This file is auto generated by xcode while creating the project.
We need to use different context for saving and reading. We need to perform the CPU intensive operations in a background thread.
I did NOT find any codelabs/tutorials concerning coredata. The apple document explains building coredata stack etc with appDelegate, controller etc kinds of files which are not SwiftUI related.
Being new to iOS, I've learnt only SwiftUI and not the others. Please provide any links or documentation for recommended/best practices for coredata in SwiftUI.
Post not yet marked as solved
I have a DataManager class as follows:
enum DataManagerType {
case normal, preview, testing
}
class DataManager: NSObject, ObservableObject {
static let shared = DataManager(type: .normal)
static let preview = DataManager(type: .preview)
static let testing = DataManager(type: .testing)
@Published var todos = [Todo]()
fileprivate var managedObjectContext: NSManagedObjectContext
private let todosFRC: NSFetchedResultsController<TodoMO>
private init(type: DataManagerType) {
switch type {
case .normal:
let persistentStore = PersistentStore()
self.managedObjectContext = persistentStore.context
case .preview:
let persistentStore = PersistentStore(inMemory: true)
self.managedObjectContext = persistentStore.context
for i in 0..<10 {
let newTodo = TodoMO(context: managedObjectContext)
newTodo.title = "Todo \(i)"
newTodo.isComplete = false
newTodo.date = Date()
newTodo.id = UUID()
}
try? self.managedObjectContext.save()
case .testing:
let persistentStore = PersistentStore(inMemory: true)
self.managedObjectContext = persistentStore.context
}
let todoFR: NSFetchRequest<TodoMO> = TodoMO.fetchRequest()
todoFR.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)]
todosFRC = NSFetchedResultsController(fetchRequest: todoFR,
managedObjectContext: managedObjectContext,
sectionNameKeyPath: nil,
cacheName: nil)
super.init()
// Initial fetch to populate todos array
todosFRC.delegate = self
try? todosFRC.performFetch()
if let newTodos = todosFRC.fetchedObjects {
self.todos = newTodos.map({todo(from: $0)})
}
}
func saveData() {
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch let error as NSError {
NSLog("Unresolved error saving context: \(error), \(error.userInfo)")
}
}
}
private func fetchFirst<T: NSManagedObject>(_ objectType: T.Type, predicate: NSPredicate?) -> Result<T?, Error> {
let request = objectType.fetchRequest()
request.predicate = predicate
request.fetchLimit = 1
do {
let result = try managedObjectContext.fetch(request) as? [T]
return .success(result?.first)
} catch {
return .failure(error)
}
}
}
My persistence store is as such:
struct PersistentStore {
let container: NSPersistentContainer
init(inMemory: Bool = false) {
container = NSPersistentContainer(name: "CoreDataModel")
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
var context: NSManagedObjectContext { container.viewContext }
func saveContext () {
if context.hasChanges {
do {
try context.save()
} catch let error as NSError {
NSLog("Unresolved error saving context: \(error), \(error.userInfo)")
}
}
}
}
I get an error when calling:
let predicate = NSPredicate(format: "id = %@", todo.id as CVarArg) //todo.id is just some UUID() //irrelevant here
let result = fetchFirst(TodoMO.self, predicate: predicate)
This is the error:
2022-07-09 21:36:17.425709-0400 CoreDataExample[73965:7495035] [error] error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'CoreDataExampleTests.TodoMO' so +entity is confused. Have you loaded your NSManagedObjectModel yet ?
CoreData: error: No NSEntityDescriptions in any model claim the NSManagedObject subclass 'CoreDataExampleTests.TodoMO' so +entity is confused. Have you loaded your NSManagedObjectModel yet ?
2022-07-09 21:36:17.425780-0400 CoreDataExample[73965:7495035] [error] error: +[CoreDataExampleTests.TodoMO entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
CoreData: error: +[CoreDataExampleTests.TodoMO entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
/Users/santiagogarciasantos/Documents/Xcode Projects/CoreDataExample/CoreDataExample/DataManager/DataManager.swift:87: error: -[CoreDataExampleTests.CoreDataExampleTests test_Add_Todo] : executeFetchRequest:error: A fetch request must have an entity. (NSInvalidArgumentException)
I've checked the target, made sure my Entity is in "Current Product Module" but it still won't work.
Important: This only occurs when I'm testing (using my DataManager.testing)
Post not yet marked as solved
After using CloudKit enabled CoreData app for a while, I have the following observation.
I have been actively use a CloudKit enabled CoreData app, for 2 weeks.
Then, I get another fresh device. Once I hook up the fresh device to internet, based on the UI updating sequence, it seems like CoreData is replaying the entire transaction history, starting from 2 weeks ago till now.
So, my questions is, if we are using the CloudKit enabled CoreData app for 5 years.
Then, when I get a fresh new device, will the CoreData replaying the entire transaction history starting from 5 years ago?
Isn't that is highly inefficient, and cause the new device sync extremely slow? Is that is so, how can we avoid such slowness, when user is using the app long time enough, and then decide to sync to a newly bought device?
Thank you
Post not yet marked as solved
So I know this is probably a stretch, but is there any way to airdrop an object from core data? From what I understand airdrop is only used with links, but is there a way to convert the data of a core data object into a file, convert the file into a link, send it via airdrop, and convert it back into a file, and then add that object from a file to another user's core data storage?
Any help would be greatly appreciated. Thanks!!
Post not yet marked as solved
Given an image selected in a UIImagePickerController and saved as a UIImage, how can I save that image in a core data model using XCode 14 beta? I’ve only found videos from 2 years ago where they make their own persistence file which is outdated.
Any help would be greatly appreciated! Thanks.
Post not yet marked as solved
Since the release of iOS 14, I keep getting reports of this crash via Crashlytics and I have no idea how to figure out what is going wrong, because the stack trace doesn't touch my code anywhere. What can I do? How do I find the source of the problem?
EXC_BAD_ACCESS KERN_INVALID_ADDRESS 0x0000000000000004
Crashed: com.apple.root.user-initiated-qos
0	CoreData											 0x1b85a87ac _PFObjectIDFastHash64 + 40
1	CoreFoundation								 0x1b254e4f8 __CFBasicHashRehash + 992
2	CoreFoundation								 0x1b2552414 CFBasicHashRemoveValue + 2384
3	CoreFoundation								 0x1b2469ec0 CFDictionaryRemoveValue + 236
4	CoreData											 0x1b84f51c0 -[NSManagedObjectContext(_NSInternalAdditions) _forgetObject:propagateToObjectStore:removeFromRegistry:] + 124
5	CoreData											 0x1b84d46ec -[_PFManagedObjectReferenceQueue _processReferenceQueue:] + 860
6	CoreData											 0x1b85a0734 -[_PFAutoreleasePoolThunk dealloc] + 48
7	libobjc.A.dylib								0x1c65bb81c AutoreleasePoolPage::releaseUntil(objc_object**) + 204
8	libobjc.A.dylib								0x1c65bb6e8 objc_autoreleasePoolPop + 212
9	libdispatch.dylib							0x1b2120aa4 _dispatch_last_resort_autorelease_pool_pop + 44
10 libdispatch.dylib							0x1b21313c8 _dispatch_root_queue_drain + 1064
11 libdispatch.dylib							0x1b21318e8 _dispatch_worker_thread2 + 116
12 libsystem_pthread.dylib				0x1f9a748cc _pthread_wqthread + 216
13 libsystem_pthread.dylib				0x1f9a7b77c start_wqthread + 8
Post not yet marked as solved
I followed along apples Article for relevant store changes, mainly for data deduplication.
https://developer.apple.com/documentation/coredata/consuming_relevant_store_changes
I also downloaded the Core Data / CloudKit Demo App which already has a deduplication process.
https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud
In the Demo project I observed that more often than not, Posts loose their relationship to Tags.
After some investigation I assume that this happens, when a Tag which has a relationship to a Post, gets deleted during the deduplication process, before the relevant Post was synced to the device.
When the Post now arrives on the device, its related Tag Object does no longer exist. Therefore it's also not possible to find the retained, deduped Tag-Object which should be connected to the Post.
I'm wondering why this was implemented that way in the Demo Project, as this really causes critical data loss.
I have also no Idea how to avoid it.
In the Article, Apple recommends to use Core Datas tombstone to preserve some values of deleted objects. However, there is no further explaination.
It's also not implemented in the Demo project.
How do I restore lost relationships and how does the tombstone help with it?
Example:
Before it synced:
After it synced:
Post not yet marked as solved
I'm restarting my app using CoreData and CloudKit and want to implement testing before I get too far and it becomes unmanageable. Is there a good tutorial anyone has found for testing when using SwiftUI (macOS and iOS but not storyboards).
Thanks. It seems overwhelming right now and I don't know where to start. :)
Post not yet marked as solved
Hi,
I made a mistake as I was trying to manage the storage on my iPhone.
In Settings / myName / manage storage / swiftUI, I selected “erase data…”
I realized afterwards that this was linked to the CloudKit container from an app I had developed for myself.
This app keeps records of my purchases and consumption of wine bottles. All the content of this app has therefore vanished. it contained several years of data.
This app uses CoreData and CloudKit.
Does anyone know if there is any chance to recover my data ?
On iCloud.com, I see no way to do such a thing.
On CloudKit console, I don’t see any possibility.
I have a backup of my iPhone on my computer; I guess that if I restore the iPhone, it will have some “CoreData” data, but not everything (images for example). And in that case, how to transfer back the data to the CloudKit container ?
Thanks for your help !
Best regards
Nicolas
I have a view that is pulling the context from the environment using the latest SwiftUI CoreData template (i.e. there is a preview/in-memory context vs the persistent context).
I also have an @ObservableObject class that is fetching objects based on the predicates passed in (think dynamic filtering). A random element from these fetched results are then displayed back in the view (i.e. I do not need this ObservableObject class to be a view itself).
However, there is an interesting "issue" where I cannot instantiate my @ObservedObject because the property initializers are run before "self" is available and I need to pass it the NSManagedObjectContext.
The only way I can think to get around this is to create the ObservableObject class outside of the view and pass it in the view's initializer. However, this isn't completely desirable as I would prefer this data be completely private to the view as other views do not need to know about its existence.
I also need it to be an ObserableObject so that the filters can change and it be reflected back in the view observing it.
Am I using the wrong tool or thinking about this wrong?
class Filter: ObservableObject {
@Published var someObjects: [Objects] = []
/* Need to instantiate with the context so objects can be fetched */
private let context: NSManagedObjectContext
}
struct ContentView: View {
@Environment(\.managedObjectContext) var context
	 /* Cannot initialize here as context isn't available */
@ObservedObject var filter = Filter(context: context)
}
Post not yet marked as solved
I am using CloudKit to sync my app across devices.
At first everything seems to work as expected but after a while CloudKit seems to get caught in an endless loop and the debug console throws tons of these messages (several thousands in serial):
CoreData: debug: CoreData+CloudKit: -[PFCloudKitSerializer applyUpdatedRecords:deletedRecordIDs:toStore:inManagedObjectContext:onlyUpdatingAttributes:andRelationships:madeChanges:error:]_block_invoke(1018): Failed to find matching objectIDs for <CKRecordID: 0x60000330c000; recordName=1E0972A7-D9DD-44A7-88F9-3AD13B32A330, zoneID=com.apple.coredata.cloudkit.zone:defaultOwner> / <CKRecordID: 0x60000330c020; recordName=EE02B981-E54D-486B-95A1-AC0839671C27, zoneID=com.apple.coredata.cloudkit.zone:defaultOwner> in pending relationship: 0xe92e2f9c5a6d27e2 x-coredata://75AFDFFD-8E35-4B9F-AA61-C477073B435B/NSCKImportPendingRelationship/p8626
I guess the most important part is:
Failed to find matching objectIDs for <CKRecordID: 0x60000330c000; ...
It's just a standard CloudKit implementation without any fancy custom code.
I am not able to find anything about it in documentation. Therefore I have no idea where to start to investigate or if this is expected behaviour.
I feel like this is slowing down my CloudKit sync quite a lot when I let the app run, the debug messages never end to appear.
Usually I'm expecting No more requests to execute. to appear at some point but even after hours this is not the case.
Does someone have similar issues or any idea about that?
Post not yet marked as solved
For the Core Data stack in my project's test target, I have the NSPersistentStoreDescription's url set to "/dev/null" as recommended by Apple at WWDC18. I previously was using an in-memory persistent store.
I have found that tests that test fetches where the fetch request is set to have the result type as NSFetchRequestResultType.dictionaryResultType will always fail. However, when running the project, the very same code will work as expected.
Is there really no way to test these fetches? I have found nothing from search engines.
I understand that a persistent store is required for this result type but is "/dev/null" not supposed to give us the same behaviour?
Post not yet marked as solved
I'm wondering if there is a difference between accessing relationship objects directly vs. fetching them with a fetchrequest. What is more efficient and what is Core Data doing under the hood?
Example:
Let's pretend I have the entities 'Car' and 'Color' with many to many relationship.
When I fetch a Car, it's relationship to its colors is a fault until I access it.
What does happen when I access it with car.colors ? Is Core Data fetching these Color objects under the hood or have they been there already all the time since fetching the Car object?
Does it make a performance difference when instead fetching the colors by a fetchrequest? -> fetchColors(for: car)
Post not yet marked as solved
So I know this is probably a stretch, but is there any way to airdrop an object from core data? From what I understand airdrop is only used with links, but is there a way to convert the data of a core data object into a file, convert the file into a link, send it via airdrop, and convert it back into a file, and then add that object from a file to another user's core data storage?
Any help would be greatly appreciated. Thanks!!
Post not yet marked as solved
Situation:
Sometimes between releases we change CoreData model and changes can be either supported by automatic lightweight migration or not.
We are checking if current model in app's bundle compatible with persisted data by NSManagedObjectModel.isConfiguration(withName:compatibleWithStoreMetadata:).
If it's not supported we are good to drop the database (because it's mainly used for caching purposes), but the problem that method returns false even if lightweight migration is possible so cache could be potentially preserved without manual migrations.
Thoughts:
Documentation states that:
To perform automatic lightweight migration, Core Data needs to be able to find the source and destination managed object models at runtime.
So we can manually check if lightweight migration possible by calling NSMappingModel.inferredMappingModel(forSourceModel:destinationModel:) But where to obtain this source model?
I know that I can keep all model versions in the bundle and create a new one for each change, but in fact I can just add new property to any entity in model (without creation a new model version) and lightweight migration works perfectly.
So what should be forSourceModel parameter in this case? In case model changes in between releases without preserving the old one? Or may be there are other ways to check if lightweight migration is going to take place during .loadPersistentStores call?