I am working with core data to store different activities and keep track of how many times each activity has been performed. The entity is called "Activities" with attributes "name" and "total". When an activity has been performed more than once, I want to change the attribute "total" (By adding +1) for that specific activity, instead of adding a new activity. How can I do this? This is my code so far:
let appDel = (UIApplication.sharedApplication().delegate as! AppDelegate)
let context: NSManagedObjectContext = appDel.managedObjectContext
let request = NSFetchRequest(entityName: "Activities")
request.returnsObjectsAsFaults = false
request.predicate = NSPredicate(format:"(name == %#)", name)
do {
let results = try context.executeFetchRequest(request) as! [Activities]
if results.count == 0 {
newActivity.setValue(1, forKey: "total")
newActivity.setValue(name), forKey: "name")
do {
try context.save()
}
catch {
print(error)
}
}
}
catch let error as NSError {
print(error)
}
}
If you want to update (if I am not mistaken), you can just change the total like this:
results.total = YourWantedNumber or results.setValue(YourWantedNumber, forKey: "total")
context.save()
Related
When I save to Core Data and then try to read from it, only the most recently saved value is retained.
The rest are nil when I try to print them out.
In my .xcdatamodeld, my entity is named CD_Cookbook and it has an attribute of name.
I'm not sure what I'm doing wrong.
func newCookbook(cookbook: String) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
let managedContext = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "CD_Cookbook", in: managedContext)!
let item = NSManagedObject(entity: entity, insertInto: managedContext)
item.setValue(cookbook, forKey: "name")
do {
try managedContext.save()
}
catch {
print("did not save cookbook name to core data", error)
}
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "CD_Cookbook")
do {
let cd = try managedContext.fetch(fetchRequest)
print(cd)
}
catch {
print("Failed to fetch cookbook names from Core Data", error)
}
}
Instead of:
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "CD_Cookbook")
Do this instead:
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "CD_Cookbook")
I've been trying to delete an NSManagedObject.
This is my code:
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "MyEnt")
request.predicate = NSPredicate(format: "SELF = %#", EnttoDelete.objectID)
request.returnsObjectsAsFaults = false
do {
let results = try context.fetch(request)
if results.count > 0 {
for result in results as! [NSManagedObject] {
print ("Ent found")
context.delete(result)
do {
try context.save()
} catch {
print("failed to delete")
}
}
}
} catch {
print ("Error in do")
}
}
Has you see I have de Entitie do be deleted (EnttoDelete) and therefor it's ID (EnttoDelete.objectID).
Now I've researchedm even in stackoverflow and I think this should work. But it's not.
How can I delete desired entitie?
I think I sort this, doing a different thing:
let app = UIApplication.shared.delegate as! AppDelegate
let context = app.persistentContainer.viewContext
var thisID: NSManagedObjectID = (thatLand?.objectID)!
let object = context.object(with: thisID)
context.delete(object)
do {
try context.save()
} catch {
print("failed to delete")
}
I thinks it's the better solution. Not sure though!
So I have this code which works fine, but I want a much better one.
func deleteCoreDataObjects() {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
//where groupData is an Array of an Entity
for i in 0..<self.groupData.count {
context.delete(groupData[i])
}
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
Currently I'm deleting the objects one by one via for loop.
You can try this:
func deleteAllData(entity: String)
{
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false
do
{
let results = try managedContext.executeFetchRequest(fetchRequest)
for managedObject in results
{
let managedObjectData:NSManagedObject = managedObject as! NSManagedObject
managedContext.deleteObject(managedObjectData)
}
} catch let error as NSError {
print("Detele all data in \(entity) error : \(error) \(error.userInfo)")
}
}
Usage:
self.deleteAllData("your_entityName")
Already seen in: https://stackoverflow.com/a/33931528/2894160
Best is delete the persistence storage and then add new one instead of looping each entity (if you want to delete all entities from coredata).
func deletePersistentStoreCoordinator () {
do {
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("YourDatabaseName.sqlite")
try self.persistentStoreCoordinator.destroyPersistentStoreAtURL(url, withType: NSSQLiteStoreType, options: nil)
try self.persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
}
catch{
}
}
Here is the code for deleting records from Core Data :
//Delete user info from local db
func deleteUserInfo() {
let context = appdelegate.managedObjectContext
let coord = appdelegate.persistentStoreCoordinator
let fetchRequest = NSFetchRequest(entityName: "User")
if #available(iOS 9.0, *) {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
//let predicate = NSPredicate(format: "id == %#", key)
//fetchRequest.predicate = predicate
do {
try coord.executeRequest(deleteRequest, withContext: context)
}
catch let error as NSError {
//Error handling
}
catch {}
} else {
// Fallback on earlier versions
do {
let users: NSArray = try appdelegate.managedObjectContext.executeFetchRequest(fetchRequest)
for user in users {
appdelegate.managedObjectContext.delete(user)
}
try appdelegate.managedObjectContext.save()
} catch let error as NSError {
//Error handling
}
catch {}
}
}
Context:
A game, at the end of the game the user retrieves a score. A function is called to save the score to the DB
Logic:
Query the DB to check if a record exists
If a record exists, retrieve the score, if the current game score is higher then the score retrieved then update the record
If you are new user then make a new entry into DB
Issues
It keeps saving new entries to the DB and not updating the 1st entry. I only want the user to have the 1 entry that gets updated as they keep getting higher scores.
Is there anywhere I can see the localDB to see what's going on?
Also this seems verbose, is there an easier way?
Code
func saveName(score: Int, whereToStore: String) {
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let fetchRequest = NSFetchRequest(entityName: "Scores")
//2
let entity = NSEntityDescription.entityForName("Scores",
inManagedObjectContext:managedContext)
let yourScore = NSManagedObject(entity: entity!,
insertIntoManagedObjectContext: managedContext)
/*Fetch results */
do
{
let fetchedResult = try managedContext.executeFetchRequest(fetchRequest) as? [NSManagedObject]
if let results = fetchedResult
{
if results.count > 1{
//Do some update code here if existing record in DB
print("Code to update number one result")
//yourScore.setValue(score, forKey: whereToStore)
let updateScore = results[0].valueForKey(whereToStore) as! Int
let updateScoreObject = results[0]
print("Score got back is \(updateScore)")
if (score > updateScore){
print("time to update")
updateScoreObject.setValue(score, forKey: whereToStore)
do {
try updateScoreObject.managedObjectContext!.save()
print("updated \(updateScoreObject)")
} catch {
let saveError = error as NSError
print(saveError)
}
}
} else {
// Do the saving here if you are a new user
//3
yourScore.setValue(score, forKey: whereToStore)
print("DOES THIS CODE RUN")
print("your score is\(yourScore)")
//4
do {
try managedContext.save()
//5
print("worked, I saved")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
}
else
{
print("Could not fetch result")
}
}
catch
{
print("There is some error.")
}
}
Update
Added
let documentsUrl = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first!
print("\(documentsUrl)")
So I can find the local SQLite and opened up with a DB viewer, as expected it keeps saving new entries rather than updating the 1st
I know how to fetch all value from one attribute in Core Data using an array. I just need to press a button and -1 all the value and save it back to the Core Data.
How can I update all the value once in swift?
Thanks.
For swift 3
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Params")
do{
if let fetchResults = try managedContext.fetch(request) as? [NSManagedObject] {
if fetchResults.count != 0 {
for index in 0...fetchResults.count-1 {
let managedObject = fetchResults[index]
managedObject.setValue("-1", forKey: "value")
}
try managedContext.save()
}
}
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
Try following example it might be helpful.Replace you entity name.
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context: NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Params")
var params = NSEntityDescription.insertNewObjectForEntityForName("Params", inManagedObjectContext: context) as! NSManagedObject
if let fetchResults = appDel.managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [NSManagedObject] {
if fetchResults.count != 0{
for (var v = 0 ; v < fetchResults.count ; v++) {
var managedObject = fetchResults[v]
println(fetchResults)
managedObject.setValue("-1", forKey: "value")
context.save(nil)
}
}
}
While the question is quiet old, I am writing it for those who wish to do this task in a more optimized manner. According to the documentation,
Batch updates run faster than processing the Core Data entities yourself in code because they operate in the persistent store itself, at the SQL level.
Hence using NSBatchUpdateRequest is the best way to achieve the result. Here's a swift code that could do the job in the given scenario with using an extension to help simplify the code while also taking into account the fact that changes made in batch update are not reflected in the objects currently in memory.
extension NSManagedObjectContext {
/// Executes the given `NSBatchUpdateRequest` and directly merges the changes to bring the given managed object context up to date.
///
/// - Parameter batchUpdateRequest: The `NSBatchUpdateRequest` to execute.
/// - Throws: An error if anything went wrong executing the batch deletion.
public func executeAndMergeChanges(using batchUpdateRequest: NSBatchUpdateRequest) throws {
batchUpdateRequest.resultType = .updatedObjectIDsResultType
let result = try execute(batchUpdateRequest) as? NSBatchUpdateResult
let changes: [AnyHashable: Any] = [NSUpdatedObjectsKey: result?.result as? [NSManagedObjectID] ?? []]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
}
}
class MyCoreDataClass {
...
func updateAllParams() {
let request = NSBatchUpdateRequest(entityName: "Params")
request.propertiesToUpdate = ["value" : NSExpression(forConstantValue: -1)]
do {
try managedObjectContext.executeAndMergeChanges(using: request)
} catch {
print(error)
}
}
}
P.S: You need to be aware that validation rules enforced in data model are not considered while executing a batch update. According to the documentation,
When you use batch updates any validation rules that are a part of the data model are not enforced when the batch update is executed. Therefore, ensure that any changes caused by the batch update will continue to pass the validation rules.