Managed Object Context as Singleton? - ios

I am facing issues with my app where if i create or delete a new object, then save the object within a different entity object, then go back and try to make a new object of the first entity type, my app will crash.
I can then reopen then app and make the object that crashed the app with no issue.
This is all being done via core data, there is an exercise, exercises are saved as a routine, then creating a new exercise after having created a routine will crash the app. Furthermore, deleting an exercise and a routine then trying to create a new one straight after will also crash the app
I have spend a long time reading around this and believe the likely cause is managed object context and wondered if creating it as a singleton was the solution? I set up the MoC by running the below in each VC's viewdidload:
func getMainContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
I then reference this VC level variable via .self wherever i need to reference the MoC to avoid clashes with creating further MoC within a VC.
I believed this should prevent issues as all core data work is linked to the shared MoC. However as documented above, there are still crashes occurring.
Below is a console print of the crash which hopefully will narrow down the source.
fatal error: Failure to save context: Error Domain=NSCocoaErrorDomain Code=134020 "(null)" UserInfo={NSAffectedObjectsErrorKey= (entity: UserExercise; id: 0x600000025060
The code block this is triggering off as an example as 1 location in the app it occurs is included below, to clarify this only occurs when i just deleted other objects, if i reloaded the app now this code would work and save just fine:
func createExercise() {
print("SAVE EXERCISE PRESSED")
if userExercise == nil {
print("SAVING THE NEW EXERCISE")
let newUserExercise = UserExercise(context: self.managedObjectContext!)
newUserExercise.name = userExerciseName.text
newUserExercise.sets = Int64(userSetsCount)
newUserExercise.reps = Int64(userRepsCount)
newUserExercise.dateCreated = NSDate()
newUserExercise.hasBeenTickedDone = false
} if self.associatedRoutineToAddTo != nil {
let fetchRequest: NSFetchRequest<UserRoutine> = UserRoutine.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "name == %#", self.associatedRoutineToAddTo!)
do {
let existingUserRoutine = try self.managedObjectContext!.fetch(fetchRequest).first
print("RETRIVED ROUTINES ARRAY CONTAINING \(existingUserRoutine)")
existingUserRoutine?.addToUserexercises(newUserExercise)
print("EXERCISE SUCESSFULLY ADDED TO ROUTINE")
} catch {
print("Fetching Routine Failed")
}
} else if self.associatedRoutineToAddTo == nil {
print("THIS IS A FRESH EXERCISE WITHOUT A PARENT ROUTINE")
}
} else if let userExercise = userExercise {
print("UPDATING THE EXISTING EXERCISE")
userExercise.name = userExerciseName.text
userExercise.sets = Int64(userSetsCount)
userExercise.reps = Int64(userRepsCount)
}
do {
try self.managedObjectContext?.save()
print("THE EXERCISE HAS BEEN SAVED")
} catch {
fatalError("Failure to save context: \(error)")
}
The variable declarations are:
var managedObjectContext: NSManagedObjectContext!
var userExercise: UserExercise?
var associatedRoutineToAddTo : String?
var editingUserExerciseID: NSManagedObjectID?
var editingUserExercise: UserExercise?

I was receiving the "NSCocoaErrorDomain Code=134020 (null)" error because my new entity was not added to the proper CoreData Configuration.

Related

How can I make multiple calls of NSBatchUpdateRequest within DB transaction so that either all rows is updated or none is updated?

Is there a way, to make multiple NSBatchUpdateRequest calls executed within a DB transaction, so that either all DB rows is updated or none is updated (When exception thrown)?
The following code illustrate the problem.
func debug() {
let coreDataStack = CoreDataStack.INSTANCE
let backgroundContext = coreDataStack.backgroundContext
backgroundContext.perform {
let fetchRequest = NSTabInfo.fetchSortedRequest()
do {
var objectIDs: [NSManagedObjectID] = []
let nsTabInfos = try fetchRequest.execute()
//
// QUESTION: We are updating multiple rows of data directly in a persistent store.
// How can we ensure either all rows is updated, or none row is updated is exception
// happens in between?
//
for nsTabInfo in nsTabInfos {
let batchUpdateRequest = NSBatchUpdateRequest(entityName: "NSTabInfo")
batchUpdateRequest.predicate = NSPredicate(format: "self == %#", nsTabInfo.objectID)
batchUpdateRequest.propertiesToUpdate = ["name": nsTabInfo.name! + "XXX"]
batchUpdateRequest.resultType = .updatedObjectIDsResultType
let batchUpdateResult = try backgroundContext.execute(batchUpdateRequest) as? NSBatchUpdateResult
guard let batchUpdateResultX = batchUpdateResult else { return }
guard let managedObjectIDs = batchUpdateResultX.result else { return }
if let nsManagedObjectIDs = managedObjectIDs as? [NSManagedObjectID] {
objectIDs.append(contentsOf: nsManagedObjectIDs)
}
//
// Simulate some exception
// We notice the first row is updated & rest of the rows are unchanged.
// This leaves our data in inconsistent state.
//
throw "Custom error!!!"
}
if !objectIDs.isEmpty {
let changes = [NSUpdatedObjectsKey : objectIDs]
coreDataStack.mergeChanges(changes)
}
} catch {
backgroundContext.rollback()
error_log(error)
}
}
}
class CoreDataStack {
static let INSTANCE = CoreDataStack()
private init() {
}
private(set) lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "wenote")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
// So that when backgroundContext write to persistent store, container.viewContext will retrieve update from
// persistent store.
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
private(set) lazy var backgroundContext: NSManagedObjectContext = {
let backgroundContext = persistentContainer.newBackgroundContext()
backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return backgroundContext
}()
func mergeChanges(_ changes: [AnyHashable : Any]) {
NSManagedObjectContext.mergeChanges(
fromRemoteContextSave: changes,
into: [persistentContainer.viewContext, backgroundContext]
)
}
}
We write a demo code to illustrate the following
Performing NSBatchUpdateRequest multiple times within a loop.
An exception happens in between.
We wishes none of the row in persistent store is updated. However, a row is already updated before the exception thrown.
May I know what technique I can use, which is similar to SQLite transaction feature, so that either all rows is updated, or none of the row is updated when exception happens?
CoreData.framework doesn't open up SQLite level controls to the user, it provides you NSManagedObjectContext.
How does it work in a similar manner?
You pull as many objects in many as you need and do your changes on them.
When you are done with your changes, you do context.save().
In that way, you save all of your changes in one shot.
In all cases, pulling all objects in memory might not be possible or a good idea, so then you need to implement your own solution around how to send all of these changes to disk.
From the NSBatchUpdateRequest docs -
A request to Core Data to do a batch update of data in a persistent store without loading any data into memory.
When you execute this, you are doing the changes in store that you can't roll back. For a large data-set, you can do following -
Say you have to perform a series of updates (5 different steps) on 100k records as an operation.
Start in a background thread, pull objects in memory in batches of 1k at a time.
You can load 1k objects easily in memory, mutate them - go through all of your changes/steps one by one and save these changes on this batch. If this is successful, you move on to the next batch.
In case one intermediate step fails on a batch, you can then use either NSManagedObjectContext.rollback() or NSManagedObjectContext.reset() depending on your implementation.
Here's a popular SO post on the differences between the two in case official docs don't provide enough clarity.

Core Data Creates A New Object When Updating An Old One

To start with, I don't believe this is a duplicate of: Updating object value in core data is also creating a new object (It's in Obj-C and they were calling insertNewObject every time they segued.)
Background Info: I learned how to use CoreData from the Ray Wenderlich book and referred to it when writing this code. I rolled my own custom stack as outlined in Chapter 3 if you have the book. I can show the code for this if needed.
Queue is the Entity I'm trying to update.
It has 1 property: name - String
And 1 to-many relationship: tasks: Task
My CoreData logic is in a Struct which contains the managedContext.
I have a basic find/create function to create a Queue object. This works. It creates 1 and only 1 object.
func findOrCreateMainQueue() -> Queue? {
let queue = Queue(context: managedContext)
queue.name = "Queue32"
let queueFetch: NSFetchRequest<Queue> = Queue.fetchRequest()
queueFetch.predicate = NSPredicate(format: "%K == %#", #keyPath(Queue.name), "Queue32" as CVarArg)
do {
let results = try managedContext.fetch(queueFetch)
print(results.count)
if results.count > 0 {
return results.first!
} else {
try managedContext.save()
}
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
return nil
}
(As you can see by the queue.name suffix number I have tried a lot of different things.)
I have tried just about everything I can think of:
This code is basically copy/pasted from: How do you update a CoreData entry that has already been saved in Swift?
func addTaskToMainQueue2(task: Task) {
let queueFetch: NSFetchRequest<Queue> = Queue.fetchRequest()
queueFetch.predicate = NSPredicate(format: "%K == %#", #keyPath(Queue.name), "Queue32" as CVarArg)
do {
let results = try managedContext.fetch(queueFetch)
print(results.count)
if results.count > 0 {
var tasks = results[0].tasks?.mutableCopy() as? NSMutableOrderedSet
tasks?.add(task)
results[0].setValue(tasks, forKey: "tasks")
}
} catch let error as NSError {
print("Fetch error: \(error) description: \(error.userInfo)")
}
do {
try managedContext.save()
} catch let error as NSError {
print("Save error: \(error),description: \(error.localizedDescription)")
}
}
Which causes a second Queue object to be created with the "Queue32" name.
Here is another thing I tried:
func addTaskToMainQueue(task: Task) {
if var queue = findOrCreateMainQueue() {
var tasks = queue.tasks?.mutableCopy() as? NSMutableOrderedSet
tasks?.add(task)
queue.tasks = tasks
do {
try managedContext.save()
} catch let error as NSError {
print("Save error: \(error),description: \(error.localizedDescription)")
}
}
}
For the sake of space I won't add code for other things I've tried.
I've tried using the find/create function and updating in that method.
I've tried saving the queue as a local object and passing it to the addTask function which causes duplication as well.
It also doesn't matter if I pass in the Task or create one in the addTask function.
I am starting to believe my issue is something in my dataModel file causing this as I've tried a number of 'How to update a Core Data object' tutorials online and I get the same result each time.
awakeFromInsert() is called whenever I try to update an object. Not sure if this should be happening.
In other places in my app updating works. For example, if I add a Subtask to a Task. It works fine. However, if I want to change the name of another entity called Project the object duplicates. (Project has an id attribute which is fetched, then the name attribute is changed.)
Thank you in advance. I've been pulling my hair out for hours.
I admit not having read all of your code but if you create a new managed object like this
let queue = Queue(context: managedContext)
then it will be added to the managedContext and will be saved to disk at some point. So this code
if results.count > 0 {
return results.first!
} else {
try managedContext.save()
}
is irrelevant in regard to the queue object created earlier because it will be saved even if results.count is > 0, although at a later point. So this means you will have to delete queue when the fetch is successful which feels unnecessary, better to wait with creating it
if results.count > 0 {
return results.first!
} else {
let queue = Queue(context: managedContext)
queue.name = "Queue32"
try managedContext.save()
}
Off topic but I see you return nil if a new object was created rather than fetched, is this intended?

Core Data: having issue inserting data with relationship

I have two entities (Categories, Event) and they have two-way many-to-many relationship. A Category can have more than one Event and an Event can have more than one Category. Category to Event relationship is optional in a sense that Category can exist without an Event, but Event to Category relationship is mandatory (Event cannot exist without Category). I'm trying to insert events and adding categories to them but I'm getting NSValidationErrorValue=Relationship error. This is my code :
private func storeEventsXMLStream(_ xml: XMLIndexer) {
let managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.persistentStoreCoordinator
// Remove all data before inserting
// This line of code is necessary because data needs to be downloaded on daily basis.
// Otherwise, I will get redundant data.
removeAllExistingData("Event_Ottawa", managedObjectContext: managedObjectContext)
autoreleasepool { // Scoping is necessary to fix memory leak
for xmlcat in xml["events"]["event"]{
let event = NSEntityDescription.insertNewObject(forEntityName: "Event_Ottawa", into: managedObjectContext) as! Event_Ottawa
event.id = Int32((xmlcat.element?.attribute(by: "id")?.text)!)!
event.website_url_english = xmlcat["website_url_english"].element?.text
event.website_url_french = xmlcat["website_url_french"].element?.text
// setting other attributes of events here. Exactly like I did in above 3 line
// Just another attribute. Storing it a String in Coredata
var recur_rules = ""
for rule in xmlcat["recur_rules"]["recur_rule"] {
recur_rules += (rule.element?.attribute(by: "weekday")?.text)!
}
if !recur_rules.isEmpty {
event.recur_rules = recur_rules
}
do {
var predicateArray:[NSPredicate] = []
// Categories are inserted to the Coredata before this method call. So I'm fetching the applicable one here.
for category in xmlcat["categories"]["category"] {
let predicate = NSPredicate(format: "id = %#", (category.element?.attribute(by: "id")?.text)!)
predicateArray.append(predicate)
}
let requestCategory:NSFetchRequest<Category_Event_Ottawa> = Category_Event_Ottawa.fetchRequest()
requestCategory.predicate = NSCompoundPredicate.init(andPredicateWithSubpredicates: predicateArray)
let managedContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.privateQueueConcurrencyType)
managedContext.persistentStoreCoordinator = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.persistentStoreCoordinator
let applicableCategories = try managedContext.fetch(requestCategory)
for category in applicableCategories {
event.addToCategory(category)
}
}
} catch {
print(error)
}
}
}
// only save once per batch insert
do {
try managedObjectContext.save()
} catch {
print(error)
}
managedObjectContext.reset() <-- I get EXC_BAD_ACCESS, when I use the same object context for fetching as insertion
}
And the error I receive :
Error Domain=NSCocoaErrorDomain Code=1560 "(null)" UserInfo={NSDetailedErrors=(
"Error Domain=NSCocoaErrorDomain Code=1550 \"The operation couldn\U2019t be completed. (Cocoa error 1550.)\" UserInfo={Dangling reference to an invalid object.=null, NSValidationErrorValue=Relationship 'category' on managed object (0x6100050963f0)
If use the same managed object context for both insertion and fetching, I don't get this error anymore. But I get EXC_BAD_ACCESS from the line that reset context object. I learnt it from another post that CoreData is not thread safe. So maybe that was an issue. But how do I resolve this issue? In case it's relevant, Deletion Rule for Event-Category relationship is Nullify and Category-Event is Cascade.
In this line:
requestCategory.predicate = NSCompoundPredicate.init(andPredicateWithSubpredicates: predicateArray)
you're creating a compound predicate with ANDs. You're asking Core Data to fetch you all of the categories that have id = 7 AND id = 8 AND ... etc. That's not going to work. The category can only have a single id. You actually want an 'or' predicate in this case.
However, I think the better way to do this is to load all of your categories into a dictionary keyed by their id before you start looping through your XML, and then just pull them out of the dictionary. That will be much more performant than fetching each time.
Also, you can't fetch categories in a separate context and then create relationships between objects from separate contexts. Core Data will crash if you try it.

After Core Data Migration Main TableView Crashes Swift

I have a fairly standard Core Data fed tableView with cell data populated from a fetchedResultsController.
Everything works as expected until I do a Core Data migration. The purpose of the lightweight migration is to provide a
simple backup not to change the model. The store uses SQLite. The plan is to do the migration to generate the new
data files and then to remove the new store and install the original store in order to keep the original file names.
The view for the backup procedure is also a tableView. Once the migration is completed, the new file is visible
in the backup tableView. Upon clicking the "back" button to return to the original tableView, the data is
visible as expected, but clicking on any row in the tableView causes an immediate crash and I'm presented with the
dreaded "Object's persistent store is not reachable from this NSManagedObjectContext's coordinator" error.
I've been struggling with this for a week. I must be missing a basic concept. Any help would be appreciated. (iOS 8, Xcode 6.4)
Here are the fetchedResultsController variables. Again these work all the time until a migration is made:
var myFetchedResultsController: NSFetchedResultsController? = nil
var fetchedResultsController: NSFetchedResultsController {
managedObjectContext = kAppDelegate.managedObjectContext
if myFetchedResultsController != nil {
return myFetchedResultsController!
}//if my ! nil
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Patient", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entity
fetchRequest.fetchBatchSize = 50
//Sort keys
let sortDescriptor = NSSortDescriptor(key: "dateEntered", ascending: false)
let sortDescriptors = [sortDescriptor]
fetchRequest.sortDescriptors = [sortDescriptor]
let aFetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
var countError : NSError? = nil
var count = managedObjectContext.countForFetchRequest(fetchRequest, error: &countError)
println("The count is \(count)")
//after creating a backup, this count is ALWAYS zero - never the real count
aFetchedResultsController.delegate = self
myFetchedResultsController = aFetchedResultsController
var error: NSError? = nil
if !myFetchedResultsController!.performFetch(&error) {
// Don't forget the code to handle the error appropriately.
println("Unresolved error \(error), \(error!.userInfo)")
//Remove this
abort()
}//if !my
return myFetchedResultsController!
}//var fetchedResultsController
The two functions for the backup procedure:
func createLocalBackupFile() {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyyMMddHHmmss"
let theDateTime = NSDate()
let formattedDateTime = dateFormatter.stringFromDate(theDateTime)
let backupFileName : String = "BiopBak" + formattedDateTime + ".sqlite"
println("backupFileName is \(backupFileName)")
let psu : CRSPersistentStoreUtilities = CRSPersistentStoreUtilities()//the function below is in this class
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
//println("In a background queue, creating the backup file")
psu.backupTheStore(backupFileName)
//go back to the main queue
dispatch_async(dispatch_get_main_queue(), { () -> Void in
println("Back on main queue after creating the backup file")
if (self.backupSqlFiles.count == 1 && self.backupSqlFiles[0] == "Placeholder for empty list") {
self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)
self.backupSqlFiles.removeAtIndex(0)
} else {
self.backupSqlFiles.append(backupFileName.stringByDeletingPathExtension)
}//if placeholder is only record in database - else
self.tableView.reloadData()
println("backupSqlFiles[] = \(self.backupSqlFiles)")
})//back to main block - inner
})//background processing block - outer
}//createLocalBackupFile
func backupTheStore(newSQLFileName : String) -> NSPersistentStore? {
let storeType = NSSQLiteStoreType
var migrateError : NSError?
var currentStore : NSPersistentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore
let options = [NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true]
let fileManager = NSFileManager.defaultManager()
let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let docsDir = paths[0] as! String
let docsDirURL = NSURL(fileURLWithPath: docsDir)
let originalStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent("BiopLogCloud.sqlite") as NSURL!
var newStoreURL : NSURL = docsDirURL?.URLByAppendingPathComponent(newSQLFileName) as NSURL!
kAppDelegate.persistentStoreCoordinator?.migratePersistentStore(currentStore, toURL: newStoreURL, options: options, withType: storeType, error: &migrateError)
currentStore = kAppDelegate.persistentStoreCoordinator?.persistentStores.last! as! NSPersistentStore
var removeStoreError : NSError?
var theStores = kAppDelegate.persistentStoreCoordinator?.persistentStores
if let theStores2 = theStores {
for removeStore in theStores2 {
var removed : Bool = true
kAppDelegate.persistentStoreCoordinator?.removePersistentStore(removeStore as! NSPersistentStore, error: &removeStoreError)
if (removeStoreError != nil) {
println("Unable to remove persistent store \(removeStore)")
}
}//for in
}//if let theStores
var addStoreError : NSError?
kAppDelegate.persistentStoreCoordinator?.addPersistentStoreWithType(storeType,
configuration: nil,
URL: originalStoreURL,
options: options,
error:&addStoreError)
if (addStoreError != nil) {
println("Unable to add persistent store \(originalStoreURL)")
//change this to add a user alert
}//if
//this does not seem to do any good
let ptvc : PatientTableViewController = PatientTableViewController()
dispatch_async(dispatch_get_main_queue()) {
() -> Void in
ptvc.tableView.reloadData()
}//block
return thisStore
}//backupTheStore
What appears to be happening is:
You're displaying some managed objects in your table view, which were fetched before the migratePersistentStore call.
You do the migratePersistentStore call. Your backupTheStore method will implicitly remove the original persistent store (as part of the migrate call) but it tries to fix things up by removing the new persistent store and re-adding the old one.
You then try to use one of the managed objects from step 1.
The problem, I think, is that although you have re-added the persistent store used to fetch those managed objects, your migration process has lost the connection from the managed objects to the store. The migrate call clears out the persistent store coordinator state, which breaks the managed object / persistent store connection, and adding the persistent store doesn't re-create that connection. (Maybe it should but that's apparently not how it's designed).
As a result, you have managed objects that the persistent store coordinator can't relate to a persistent store file, and you crash when you try to use them.
Reloading the table view isn't enough because it will just reload the same managed objects from the fetched results controller. You should also make sure to call performFetch on the fetched results controller to make it re-fetch its data. If that's not enough, set myFetchedResultsController to nil and then reload the table, so that you'll get a completely new fetch.
New important data - this is not just a backup and restore issue. This app is an iCloud app so of course the original store should be in the local ubiquity container not the app documents directory. My code above does indeed work for a non-iCloud setup. I've adjusted the originalStoreURL to point to the local ubiquity container and I can now restore the original data store.
As Tom pointed out above, by referring to the app documents directory for the restoration of the store, I was actually creating a new store which also persisted after every backup procedure - however that was a local, non-iCloud store.

Delete duplicated object in core data (swift)

I'm saving objects to core data from a JSON, which I get using a for loop (let's say I called this setup function.
Because the user might stop this loop, the objects saved in core data will be partial. The user can restart this setup function, restarting the parsing and the procedure to save object to core data.
Now, I'm getting duplicated objects in core data if I restart the setup().
The object has an attribute which is id.
I've thought I could fetch first objects that could eventually already exist in core data, save them to an array (a custom type one), and test for each new object to add to core data if already exist one with the same id.
The code used is the following:
if !existingCards.isEmpty {
for existingCard in existingCards {
if id == existingCard.id {
moc.deleteObject(existingCard)
println("DELETED \(existingCard.name)")
}
}
}
...
// "existingCards is the array of object fetched previously.
// Code to save the object to core data.
Actually, the app return
EXC_BAD_ACCESS(code=1, address Ox0)
Is there an easier way to achieve my purpose or what should I fix to make my code work? I'm quite new to swift and I can't figure other solution.
The main purpose is to delete duplicated core data, BTW.
Swift 4 code to delete duplicate object:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Card")
var resultsArr:[Card] = []
do {
resultsArr = try (mainManagedObjectContext!.fetch(fetchRequest) as! [Card])
} catch {
let fetchError = error as NSError
print(fetchError)
}
if resultsArr.count > 0 {
for x in resultsArr {
if x.id == id {
print("already exist")
mainManagedObjectContext.deleteObject(x)
}
}
}
At the end, I managed to make it work.
I had to rewrite my code, because I realized moc.deleteObject() works with a fetch before, which in my previous code wasn't in the same function, but it was in viewDidLoad().
// DO: - Fetch existing cards
var error: NSError?
var fetchRequest = NSFetchRequest(entityName: "Card")
if let results = moc.executeFetchRequest(fetchRequest, error: &error) as? [Card] {
if !results.isEmpty {
for x in results {
if x.id == id {
println("already exist")
moc.deleteObject(x)
}
}
}
} else {
println(error)
}
No more existingCards, the result of the the fetch is now processed as soon as possible. Something isn't clear to me yet, but now my code works. If you have any improvements/better ways, they're welcome.
P.S.: I actually found Apple reference useful but hard to understand because I don't know Obj-C. Often I can figure what the code do, but in swift functions and properties are a bit different.

Resources