I want to update a core data object in swift 3. After some googled I didn't found anything about swift 3.
So my question is: how can I update a core data object in swift 3?
Fetch the existing values using a fetch request with a predicate. Use a unique value in the predicate. Once you've fetched the object, update the object with new values and save the context.
let empId = "001"
let fetchRequest:NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "EmpDetails")
let predicate = NSPredicate(format: "empId = '\(empId)'")
fetchRequest.predicate = predicate
do {
let result = try persistentContainer.viewContext.fetch(fetchRequest)
if let objectToUpdate = result.first as? NSManagedObject {
objectToUpdate.setValue("newName", forKey: "name")
objectToUpdate.setValue("newDepartment", forKey: "department")
objectToUpdate.setValue("001", forKey: "empID")
try persistentContainer.viewContext.save()
}
} catch {
print(error)
}
Using NSManagedObject subclass
let empId = "001"
let fetchRequest: NSFetchRequest<Employee> = Employee.fetchRequest()
fetchRequest.predicate = NSPredicate(format: "%K = %#", #keyPath(Employee.id), empId)
do {
let results = try persistentContainer.viewContext.fetch(fetchRequest)
if let employee = results.first {
employee.name = "new name"
employee.department = "new department"
}
try persistentContainer.viewContext.save()
} catch let error as NSError {
print(error.localizedDescription)
}
Batch updates
Batch updates help to update multiple Core Data objects without having
to fetch anything into memory.
let batchUpdate = NSBatchUpdateRequest(entityName: "Employee")
batchUpdate.propertiesToUpdate = [#keyPath(Employee.isActive): true]
batchUpdate.affectedStores = persistentContainer.viewContext.persistentStoreCoordinator?.persistentStores
batchUpdate.resultType = .updatedObjectsCountResultType
do {
let batchResult = try coreDataStack.managedContext.execute(batchUpdate) as? NSBatchUpdateResult
print(batchResult?.result)
} catch let error as NSError {
print(error.localizedDescription)
}
Pass unique id in variable "id"(Unique variable created in Core data model) and all the variable as you want to update values:
func context() -> NSManagedObjectContext {
let context=(UIApplication.shared.delegate as!AppDelegate).persistentContainer.viewContext
return context
}
func save() {
(UIApplication.shared.delegate as! AppDelegate).saveContext()
}
func UpdateCartByTestId(id:Int64,name:String) {
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "Update")
fetchRequest.returnsObjectsAsFaults = false
fetchRequest.predicate = NSPredicate(format:"id == %d",id)
let result = try? context().fetch(fetchRequest)
if result?.count == 1 {
let dic = result![0]
dic.setValue(id, forKey: "id")
dic.setValue(name, forKey: "name")
save()
}
}
Related
i had added this code for search via product_id in coredata but that returns all records i want to just get specific data from row that contain that product_id
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "CartEntity")
let predicate = NSPredicate(format: "product_id == %#", "\(Product_id)")
request.predicate = predicate
request.fetchLimit = 1
do{
let count = try managedContext.count(for: request)
if(count == 0){
// no matching object
print("no")
self.savecoredata()
}
else{
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "CartEntity")
request.returnsObjectsAsFaults = false
do {
let result = try managedContext.fetch(request)
for dataresult in result as! [NSManagedObject] {
let userName = dataresult.value(forKey: "proname") as! String
let age = dataresult.value(forKey: "price") as! String
print("User Name is : "+userName+" and price is : "+age)
print(datastored)
}
} catch {
print("Fetching data Failed")
}
print("yes")
// deleteFeed(id: "\(Product_id)")
// first delete old and than insert new id
}
}
catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
The problem is that you have to different requests, first you use one with a predicate and then you use one without a predicate which will return all rows. Either reuse the predicate for the second request as well or even better skip the first count request that seems unnecessary and perform only the second one
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "CartEntity")
request.returnsObjectsAsFaults = false
request.predicate = NSPredicate(format: "product_id == %#", "\(Product_id)")
do {
let result = try managedContext.fetch(request)
for dataresult in result as! [NSManagedObject] {
//... rest of code
First of all use the predicate of the first request and secondly use a specific fetch request to return the actual NSManagedObject subclass. The benefit is to get the values directly with dot notation rather than with error-prone KVC (key-value coding).
This is the corresponding logic of your entire code, however the savecoredata line seems to be pointless
let request : NSFetchRequest<CartEntity> = CartEntity.fetchRequest()
request.predicate = NSPredicate(format: "product_id == %ld", Product_id)
do {
if let result = try managedContext.fetch(request).first {
let userName = result.proname
let price = result.price
print("User Name is : \(userName) and price is : \(price)")
} else {
// no matching object
print("no")
self.savecoredata()
}
} catch {
print(error)
}
The issue seems to be with your predicate. Check if the product_id is string or integer. If you are doing casting then apply format "%d", "%ld"
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "CartEntity")
let predicate = NSPredicate(format: "product_id = \(Product_id)")
request.predicate = predicate
request.fetchLimit = 1
var count = 0
do{
count = try managedContext.count(for: request)
}
catch let error {
print("Could not fetch \(error), \(error.localizedDescription)")
}
guard count != 0 else {
// no matching object
print("no")
self.savecoredata()
return
}
// remove predicate, which is actually not required.
request.predicate = nil
request.returnsObjectsAsFaults = false
do {
let result = try managedContext.fetch(request)
for dataresult in result as! [NSManagedObject] {
let userName = dataresult.value(forKey: "proname") as! String
let age = dataresult.value(forKey: "price") as! String
print("User Name is : "+userName+" and price is : "+age)
print(datastored)
}
} catch let error {
print("Fetching data Failed - \(error)")
}
print("yes")
// deleteFeed(id: "\(Product_id)")
// first delete old and than insert new id
I want to fetch data from Core data and look for duplicats and then only save the data then there is no duplicate of the movieid.
Maybe some one can help me ..
How can I compare the result with the movieid string ?
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "MovieData")
//request.predicate = NSPredicate(format: "movieid = %#", movieID)
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
print(data.value(forKey: "movieid") as! String)
}
} catch {
print("Failed")
}
Almost. Apply the predicate to get only the record with the specific movieID. However it assumes that movieID is an object (NSNumber), if it's an scalar Int you have to use %ld as placeholder.
If the fetch returns an empty array there is no duplicate and you can insert a new object
let request = NSFetchRequest<NSManagedObject>(entityName: "MovieData")
request.predicate = NSPredicate(format: "movieid = %#", movieID)
do {
let result = try context.fetch(request)
if result.isEmpty {
let newMovie = NSEntityDescription.insertNewObject(forEntityName: "MovieData", into: context) as! MovieData
newMovie.movieid = movieID
try context.save()
}
} catch {
print(error)
}
While saving in core data you need to create predicate and in there you need to check if there are values already saved with same "movieid" then it has to be updated , this way you won't have duplicate data . Please refer the method and try using the same for saving the values in DB . This way duplicate values won't be saved in DB
class func insertupdaterecord (movieID:String, context: NSManagedObjectContext)
{
let entityDescription = NSEntityDescription.entity(forEntityName: "movie", in: context)
let pred = NSPredicate(format: "movieid = %#", movieID)
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "movie")
fetchRequest.entity = entityDescription
fetchRequest.predicate = pred
let result = try! (context.fetch(fetchRequest) as NSArray).lastObject
let updateInsertInfo : movie
if result != nil
{
updateInsertInfo = result as! movie
}
else
{
print("Record not found!")
}
do
{
try context.save()
}
catch let error as NSError
{
print("Error while saving \(error) in database.")
}
}
Create a cache for movieid values to check for duplicates and loop through the fetched result and delete any objects with a movieid already in the cache and then save once the loop is done.
var selection: [String] = []
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "MovieData")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
guard let movieId = data.value(forKey: "movieid") as? String else {
context.delete(data) // or however you want to handle this situation
continue
}
if selection.contains(movieId) {
context.delete(data)
} else {
selection.append(movieId)
}
}
try context.save()
} catch {
print("Failed")
}
I have several items of jobs in core data entity whose jobId is -1. I need to fetch all those items and update jobId by proper ids which are in my object that is passed in updateMyJobs method. I haven't extracted NSManagedObject class to work on core data (i.e.- I've checked the entity as Class definition)
Here's my code:
func updateMyJobs(jobId: Int){
managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "DBJobsNearBy")
fetchRequest.predicate = NSPredicate(format: "jobId = '-1'")
let result = try? managedObjectContext.fetch(fetchRequest)
let resultData = result as! [DBJobsNearBy]
for object in resultData {
print(object.jobId)
if object.jobId == -1 {
object.setValue("\(jobId)", forKey: "jobId")
}
}
do {
try managedObjectContext.save()
print("saved!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I passed some integer value and call the above method to update the item like this.
DatabaseHandler.shared.updateMyJobs(jobId: 222)
Error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "jobId"; desired type = NSNumber; given type = Swift._NSContiguousString; value = 222.'
I'm trying to set new jobId to the related object in core data entity. Is it necessary to extract NSManagedObject Class or not. Please someone help me to do this. Thank You.
Please read the error message. It's very clear. In setValue you are passing a String (via String Interpolation) rather than expected Int or NSNumber
object.setValue(jobId, forKey: "jobId")
or
object.setValue(NSNumber(value: jobId), forKey: "jobId")
But the best and recommended way is dot notation
object.jobId = jobId
Update Object to core data
#IBAction func buttonUpdate(_ sender: Any) {
let entity = NSEntityDescription.entity(forEntityName: "Students", in: managedContext)
let request = NSFetchRequest<NSFetchRequestResult>()
request.entity = entity
let newName = UserDefaults.standard.value(forKey: "new") as! String
let predicate = NSPredicate(format: "(name = %#)", newName)
request.predicate = predicate
do {
let results =
try managedContext.fetch(request)
let objectUpdate = results[0] as! NSManagedObject
objectUpdate.setValue(txtName.text!, forKey: "name")
objectUpdate.setValue(txtPhone.text!, forKey: "phone")
objectUpdate.setValue(txt_Address.text!, forKey: "address")
do {
try managedContext.save()
labelStatus.text = "Updated"
}catch let error as NSError {
labelStatus.text = error.localizedFailureReason
}
}
catch let error as NSError {
labelStatus.text = error.localizedFailureReason
}
}
You need to set NSNumber instead of String. Replace this line:
object.setValue("\(jobId)", forKey: "jobId")
with:
object.jobId = jobId
I'm developing an app to store one phone number at time using core data, the user should be able to enter a new phone number into ui text field,if it's equal to nil, it should store a new phone number,else it should replace old number with new number; it should store only one value.
but the code doesn't work as it should
what's wrong in my code?
let moContext: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = moContext.managedObjectContext
let phoneNu = NSEntityDescription.insertNewObjectForEntityForName("Setting", inManagedObjectContext: context)
let fetchRequest = NSFetchRequest(entityName: "Setting")
fetchRequest.predicate = NSPredicate(format: "phoneNumber = %#", phoneNumber)
// phoneNu.setValue(phoneNumber.text, forKey: "phoneNumber")
do{
let request = NSFetchRequest(entityName: "Setting")
let phoneN = try context.executeFetchRequest(request)
if phoneN.count == 0{
phoneNu.setValue(phoneNumber.text, forKey: "phoneNumber")
}else if phoneN.count != 0{
for item in phoneN as! [NSManagedObject]{
let number = item.valueForKey("phoneNumber")
number?[0]?.setValue(phoneNumber.text, forKey: "phoneNumber")
}
}
}catch{
print("error")
}
do{
let request = NSFetchRequest(entityName: "Setting")
let phoneNumber = try context.executeFetchRequest(request)
if phoneNumber.count > 0{
for item in phoneNumber as! [NSManagedObject]{
let number = item.valueForKey("phoneNumber")
print(number!)
}
}
}catch{
}
This is my solution
let moContext: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext = moContext.managedObjectContext
guard let phoneNumber = phoneNumber.text where phoneNumber.characters.count > 0 else {
return// returns if textField is empty.
}
let fetchRequest = NSFetchRequest(entityName: "Setting")
fetchRequest.predicate = NSPredicate(format: "phoneNumber = %#", phoneNumber)
do{
let request = NSFetchRequest(entityName: "Setting")
let phoneObjectArray = try context.executeFetchRequest(request)
guard let settingFetched = phoneObjectArray as? [Setting] where settingFetched.count > 0 else {
// if enters here -> 0 phone numbers were found
if let setting = NSEntityDescription.insertNewObjectForEntityForName("Setting", inManagedObjectContext: context) as? Setting {
setting.phoneNumber = phoneNumber
// call context.save()
}
return // this I forgot
}
let settingObject = settingFetched.first
// fetch results return store phone number.
// update phone number
settingObject.phoneNumber = phoneNumber
// call context.save()
} catch let error as NSError! {
print("error: \(error)")
}
You need to call the "save()" function to persist the information.
I did some changes to the name variables, added some validations and assume some model class name based on your code.
I always recommend to use your NSManagedObject subclass (Setting I assume) instead of just using NSManagedObject and key/value coding.
There could be some minor syntax mistakes on the code because I was using a text editor.
Hope this helps!
For what you want to do, Core Data is overkill.
Use NSUserDefaults instead. So much easier.
I have successfully written the code to download the parse objects, fetch the current objects in my database and then compare.
My algorithm:
I iterate through the parse objects and run a fetchrequest and compare their objectID's. If I get nothing, I make a new object for my database. Otherwise I then look at the modifiedDate I have in my database and the updatedAt from parse and compare to see if I need to set new values. This code works great.
The code:
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
let object = object as! PFObject
let name = object["name"] as! String
let email = object["email"] as! String
let subjectsTaught = object["subjectsTaught"] as [String: String]
let category = object["category"] as! String
let uniqueID = object.objectId!
let modifiedDate = object.updatedAt!
let fetchRequest2 = NSFetchRequest(entityName: "Teacher")
fetchRequest2.predicate = NSPredicate(format: "uniqueID == %#", uniqueID)
var error2: NSError?
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest2, error: &error2) as? [Teacher] {
if foundTeachers.isEmpty == true {
let teacher = NSEntityDescription.insertNewObjectForEntityForName("Teacher", inManagedObjectContext: self.managedObjectContext) as! Teacher
teacher.name = name
teacher.email = email
teacher.subjectsTaught = subjectsTaught
teacher.category = category
teacher.uniqueID = uniqueID
teacher.modifiedDate = modifiedDate
} else {
if let teacher = foundTeachers.first {
let date1 = teacher.modifiedDate
let date2 = modifiedDate
let compareResult = date1.compare(date2)
if compareResult == NSComparisonResult.OrderedAscending {
teacher.setValue(name, forKey: "name")
teacher.setValue(email, forKey: "email")
teacher.setValue(subjectsTaught, forKey: "subjectsTaught")
teacher.setValue(category, forKey: "category")
teacher.setValue(modifiedDate, forKey: "modifiedDate")
}
}
}
}
var error: NSError?
if !self.managedObjectContext.save(&error) {
println("Error \(error)")
abort()
}
}
}
My question is how should I figure out which objects where not in parse? I don't want to query parse for every object in my database as I assume that would be network intensive.
Should I do a fetchrequest for all Teacher objects in the beginning and as I iterate through the parse objects, delete them as I go? If I have objects left, those should be deleted?
Okay, I figured out what to do. I ended up running a fetchrequest first for all teacher and appending their names to an array. During the parse iteration, I deleted teachers from that list as I went through them and at the end, used that list to delete teachers from the database.
let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("Teacher", inManagedObjectContext: self.managedObjectContext)
var error: NSError?
var foundTeacherNames = [String]()
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest, error: &error) as? [Teacher] {
for teacher in foundTeachers {
foundTeacherNames.append(teacher.name)
}
}
//Find teachers in parse database
let query = PFQuery(className: "TeacherList")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let objects = objects {
for object in objects {
let object = object as! PFObject
let name = object["name"] as! String
let email = object["email"] as! String
let subjectsTaught = object["subjectsTaught"] as! [String: String]
let category = object["category"] as! String
let uniqueID = object.objectId!
let modifiedDate = object.updatedAt!
let fetchRequest2 = NSFetchRequest(entityName: "Teacher")
fetchRequest2.predicate = NSPredicate(format: "uniqueID == %#", uniqueID)
var error2: NSError?
if let foundTeachers = self.managedObjectContext.executeFetchRequest(fetchRequest2, error: &error2) as? [Teacher] {
if foundTeachers.isEmpty == true {
let teacher = NSEntityDescription.insertNewObjectForEntityForName("Teacher", inManagedObjectContext: self.managedObjectContext) as! Teacher
teacher.name = name
teacher.email = email
teacher.subjectsTaught = subjectsTaught
teacher.category = category
teacher.uniqueID = uniqueID
teacher.modifiedDate = modifiedDate
} else {
if let teacher = foundTeachers.first {
let date1 = teacher.modifiedDate
let date2 = modifiedDate
let compareResult = date1.compare(date2)
if compareResult == NSComparisonResult.OrderedAscending {
teacher.setValue(name, forKey: "name")
teacher.setValue(email, forKey: "email")
teacher.setValue(subjectsTaught, forKey: "subjectsTaught")
teacher.setValue(category, forKey: "category")
teacher.setValue(modifiedDate, forKey: "modifiedDate")
}
}
}
if contains(foundTeacherNames, name) {
let i = find(foundTeacherNames, name)!
foundTeacherNames.removeAtIndex(i)
}
}
var error: NSError?
if !self.managedObjectContext.save(&error) {
println("Error \(error)")
abort()
}
if !foundTeacherNames.isEmpty {
for teacher in foundTeacherNames {
let request = NSFetchRequest(entityName: "Teacher")
request.predicate = NSPredicate(format: "name = %#", teacher)
if let fetchResults = self.managedObjectContext.executeFetchRequest(request, error: nil) as? [NSManagedObject] {
if fetchResults.count != 0 {
self.managedObjectContext.deleteObject(fetchResults[0])
}
}
}
}
Yes, the best way is to fetch all entities and then check for the unique ids. You could use key-value-coding (or its Swift equivalents such as map) to just get the ids you are interested in.
let existingIDs = entitiesFromParse.map() { $0.uniqueID as? String }
You can then check if an ID exists with
let idExists = existingIDs.contains(idToCheck)
This is preferable to multiple fetch requests which are expensive.