Check before the update in Core Data

I update one field in Core Data. This code works well:


func updateData(){
        guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }
        let managedContext = appDelegate.persistentContainer.viewContext
        let fetchRequest:NSFetchRequest = NSFetchRequest.init(entityName: "User")
        fetchRequest.predicate = NSPredicate(format: "username = %@", "John")
       
        do {
            let test = try managedContext.fetch(fetchRequest)
            let objectUpdate = test[0] as! NSManagedObject
            objectUpdate.setValue("John2", forKey: "username")
            objectUpdate.setValue("John2@example.com", forKey: "email")
            objectUpdate.setValue("newPassword", forKey: "password")
            do {
                try managedContext.save()
            }
            catch {
                print(error)
            }
        } catch {
            print(error)
        }
    }


I would like to add a way to check if what I try to update exists. In that specific case, I want to check first if I have in the database the username John

Accepted Reply

hi,


if all you want is a "did it succeed" indication, you could use something like this, returning a boolean that indicates whether everything worked:


func updateData() -> Bool {
  var success = false
  guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
  let managedContext = appDelegate.persistentContainer.viewContext
  let fetchRequest = NSFetchRequest.init(entityName: "User")
  fetchRequest.predicate = NSPredicate(format: "username = %@", "John")

  do {
    let test = try managedContext.fetch(fetchRequest)
    if test.count == 1 {
      let objectUpdate = test[0] as! NSManagedObject
      objectUpdate.setValue("John2", forKey: "username")
      objectUpdate.setValue("John2@example.com", forKey: "email")
      objectUpdate.setValue("newPassword", forKey: "password")
      appDelegate.saveContext() // look in AppDelegate.swift for this function
      success = true
    }
  } catch {
    print(error)
  }
  return success
}


check the return value of updateData() to issue your success message:


if updateData() {
  print ("I have found the name and I have changed")
} else {
  print ("I could not find the name you are looking for")
}


there are many variations on how to use what's above -- it's not the best code, it may not generalize well to whatever you specifically are doing, and the appDelegate may throw/catch an error in saveContext() that would not be recognized in this code.


but then, consider: your code would be cleaner and less prone to error if you used what XCode generates for you for whatever class of managed object you defined in the .xcdatamodeld file. if, for example, the NSManagedObject you're working with was defined as an entity named User -- with attributes named username, email, and password (all String types) -- this code should work (i think -- i did not test, but it mirrors code i have in a current project)


func updateData() -> Bool {
  var success = false
  guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
  let managedContext = appDelegate.persistentContainer.viewContext
  let fetchRequest: NSFetchRequest<User> = User.fetchRequest() // this provided by XCode code generation
  fetchRequest.predicate = NSPredicate(format: "username = %@", "John")

  do {
    let test = try managedContext.fetch(fetchRequest)
    if test.count == 1 {
      let userToUpdate = test[0]
      userToUpdate.username = "John2"
      userToUpdate.email = "John2@example.com"
      userToUpdate.password = "newPassword"
      appDelegate.saveContext() // look in AppDelegate.swift for this function
      success = true
    }
  } catch {
    print(error)
  }
  return success
}


hope that helps,

DMG


** EDIT A DAY LATER: line 5 above was updated to provide specificity for the type of fetchRequest as NSFetchRequest<User>.

Replies

hi,


i think the code you wrote looks workable -- although you did not mention any error or say exactly what it is you want to do that the current code doesn't appear do for you. unless, of course, you're simply asking what is the value of objectUpdate in line 9 and you want to check that new values have been assigned to its attributes -- and you might just print(objectUpdate.username) before line 10 and again after line 10.


nevertheless, i'd suggest you might want to go over to Youtube.com and search for "Stanford CS193P 2017" watch the episodes Lecture 10 (Core Data) and Lecture 11 (Core Data Demo). i think you'll find those very helpful at this stage (even though this was in 2017, everything you'll see pretty much works as is today).


hope that helps,

DMG

Thank you for the reference to the videos. I will love to study that.


func updateData() is a simple code to create data in Core Data that I found in a tutorial. There is no error there, it is ok. What I miss in that code is a way to check if the value I want to update exists in Core Data.


For instance, the example is looking for John. If it finds it, it will change it to John2. But, if in the database there is no John, that will not work, of course. So, I need to add something like the conditional that I put at the beginning. My question is how can I say in that example "if (you find the username John in Core Data) ..."

hi,


if all you want is a "did it succeed" indication, you could use something like this, returning a boolean that indicates whether everything worked:


func updateData() -> Bool {
  var success = false
  guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
  let managedContext = appDelegate.persistentContainer.viewContext
  let fetchRequest = NSFetchRequest.init(entityName: "User")
  fetchRequest.predicate = NSPredicate(format: "username = %@", "John")

  do {
    let test = try managedContext.fetch(fetchRequest)
    if test.count == 1 {
      let objectUpdate = test[0] as! NSManagedObject
      objectUpdate.setValue("John2", forKey: "username")
      objectUpdate.setValue("John2@example.com", forKey: "email")
      objectUpdate.setValue("newPassword", forKey: "password")
      appDelegate.saveContext() // look in AppDelegate.swift for this function
      success = true
    }
  } catch {
    print(error)
  }
  return success
}


check the return value of updateData() to issue your success message:


if updateData() {
  print ("I have found the name and I have changed")
} else {
  print ("I could not find the name you are looking for")
}


there are many variations on how to use what's above -- it's not the best code, it may not generalize well to whatever you specifically are doing, and the appDelegate may throw/catch an error in saveContext() that would not be recognized in this code.


but then, consider: your code would be cleaner and less prone to error if you used what XCode generates for you for whatever class of managed object you defined in the .xcdatamodeld file. if, for example, the NSManagedObject you're working with was defined as an entity named User -- with attributes named username, email, and password (all String types) -- this code should work (i think -- i did not test, but it mirrors code i have in a current project)


func updateData() -> Bool {
  var success = false
  guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
  let managedContext = appDelegate.persistentContainer.viewContext
  let fetchRequest: NSFetchRequest<User> = User.fetchRequest() // this provided by XCode code generation
  fetchRequest.predicate = NSPredicate(format: "username = %@", "John")

  do {
    let test = try managedContext.fetch(fetchRequest)
    if test.count == 1 {
      let userToUpdate = test[0]
      userToUpdate.username = "John2"
      userToUpdate.email = "John2@example.com"
      userToUpdate.password = "newPassword"
      appDelegate.saveContext() // look in AppDelegate.swift for this function
      success = true
    }
  } catch {
    print(error)
  }
  return success
}


hope that helps,

DMG


** EDIT A DAY LATER: line 5 above was updated to provide specificity for the type of fetchRequest as NSFetchRequest<User>.

Do it this way

  • set the NSFetchRequestResultType of the NSFetchRequest to NSCountResultType
  • excecute the NSFetchRequest by using the method countForFetchRequest on the NSManagedObjectContext
  • check if the returned count is greater than zero