How do you update a CoreData entry that has already been saved in Swift?
Since batchupdate is more useful in larger chunks of data, I think this is a more subtle approach.
func saveLoginData(accessToken: String, userName: String) {
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context: NSManagedObjectContext = appDel.managedObjectContext!
var fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "LoginData")
fetchRequest.predicate = NSPredicate(format: "userName = %@", userName)
if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
if fetchResults.count != 0{
var managedObject = fetchResults[0]
managedObject.setValue(accessToken, forKey: "accessToken")
context.save(nil)
}
}
}
I tried to translate it a bit to your situation if I'm not mistaken, but have not tested it.
fetchRequest.predicate
basically sets the filter on the attribute userName
from the entity LoginData
, with the (user)name you enter when calling the function. Assuming in this example you have only one username
with the same name. Then it does a fetchrequest
with the given filter, so you can change it's value with setValue
with the accesToken
you also enter when calling the function. The code after: if fetchResults.count != 0
, only executes when the username
exists.
Updated for Swift 4 & XCode 9.2
To answer your question...
How do you update a CoreData entry that has already been saved in Swift?
You first need to get a reference to your AppDelegate
and viewContext
. You then need to set up a NSFetchRequest
for the Entity you are looking to update, in my example that would be "Alert". You then set up your fetch to find the result you are looking for. In the example, my result found Alerts by their creation date and alert type.
To further read how to query using a predicate. Stack Overflow Example & Apple Documentation
I then context.fetch(fetchRequest)
, set the results to the value I wanted to update, and dealt with errors in a try catch.
Finally I context.save()
.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Alert")
fetchRequest.predicate = NSPredicate(format: "creationDate = %@ AND alertType = %&",
argumentArray: [creationDate, alertType])
do {
let results = try context.fetch(fetchRequest) as? [NSManagedObject]
if results?.count != 0 { // Atleast one was returned
// In my case, I only updated the first item in results
results[0].setValue(yourValueToBeSet, forKey: "yourCoreDataAttribute")
}
} catch {
print("Fetch Failed: \(error)")
}
do {
try context.save()
}
catch {
print("Saving Core Data Failed: \(error)")
}
Swift 5
You can create a method that can work to both, include and update.
Assuming you have an entity
created on CoreData with the name Users
:
var context: NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
let user: Users!
let fetchUser: NSFetchRequest<Users> = Users.fetchRequest()
fetchUser.predicate = NSPredicate(format: "id = %@", id as String)
let results = try? context.fetch(fetchUser)
if results?.count == 0 {
// here you are inserting
user = Users(context: context)
} else {
// here you are updating
user = results?.first
}
user.id = id
user.name = name
...
try context.save()
Swift >= 2 the method now returns a non-optional and throws an error in the error case, which must be handled with try-catch:
let context = self.fetchedResultsController.managedObjectContext
let entity = self.fetchedResultsController.fetchRequest.entity!
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entity.name!)
fetchRequest.predicate = NSPredicate(format: "notificationId = 13")
do {
let list = try context.executeFetchRequest(fetchRequest) as? [NSManagedObject]
// Check notificationId available then not save
if list!.count == 0 {
let newManagedObject = NSEntityDescription.insertNewObjectForEntityForName(entity.name!, inManagedObjectContext: context)
newManagedObject.setValue("This is first message13", forKey: "message")
newManagedObject.setValue(1, forKey: "appId")
newManagedObject.setValue(13, forKey: "notificationId")
newManagedObject.setValue("First one", forKey: "tital")
}
// success ...
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
You're creating multiple new LoginData
objects, but your loadLoginData
method is always returning the same object, the first one from the fetch request results.
You want to keep updating the same object, so you need to change your saveLoginDetails
method.
Instead of creating a new object (which is what insertNewObjectForEntityName
) does, use the loadLoginDetails
method to get your existing one, and change the property on there.