I have tableView, and I already succeed saved it into core data (create new in core data).
When I update, it only update base on last data, not all data, and it cause the other data filled with the last data. so the result like this:
the actual is the first and the second tableView cell data different, but after update, it copy last data to other data.
This is my update code :
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequestPhone = NSFetchRequest<NSManagedObject>(entityName: "Phone")
do {
let phones = try managedContext.fetch(fetchRequestPhone)
if (phones.count > 0) {
for phone in phones {
phone.setValue(phoneNameValue, forKey: "phoneName")
phone.setValue(phoneNumberValue, forKey: "phoneNumber")
do{
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
}
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
What is the correct code to repair this?
Related
I have a function that deletes an NSManagedObject from CoreData (Test is a subclass of NSManagedObject:
public func delete(_ test: Test, completion: #escaping (Bool) -> Void) {
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return completion(false) }
let managedContext = appDelegate.persistentContainer.viewContext
do {
managedContext.delete(test)
completion(true)
} catch let error as NSError {
print("Could not delete. \(error), \(error.userInfo)")
completion(false)
}
}
Right now, it appears that the object is being deleted from CoreData in the moment, but if I rerun my app, the object that I just deleted appears again. What am I doing wrong when trying to delete this object?
You have to save the context to make changes persist, including deletions.
do {
try managedContext.save()
} catch let error as NSError {
print("Unresolved error \(error), \(error.userInfo)")
}
During a loading-process I'm trying to update my UI (a label and a progress bar) from within a loop.
The UI is not updating though, until the loading-function is done.
The loading-function is not using another thread, I'm still in the main thread so from my understanding it should be possible to instantly-update the UI...
Is there something I am missing?
loading-function: (called from viewDidAppear)
func LoadDataFromDataCore(){
//1
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
//2
let fetchRequest =
NSFetchRequest<NSManagedObject>(entityName: "CDGlobeTile")
//3
do {
let DataCoreResult = try managedContext.fetch(fetchRequest)
var LabelFromCDArray = [LabelTile]()
for (index, CDGlobeTile) in DataCoreResult.enumerated() {
updateLoadingBar(Progress: Float(index) / Float(DataCoreResult.count - 1))
// "unpack" the data from CoreData...
}
do{
try managedContext.save()
} catch let error as NSError {
print("Error saving context. \(error), \(error.userInfo)")
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
print(" finished loading")
}
updateLoadingBar - function:
func updateLoadingBar(Progress: Float){
let percentage = Progress * 100
if let currentDispatch = OperationQueue.current?.underlyingQueue {
print(currentDispatch)
// this gives me "<OS_dispatch_queue_main: com.apple.main-thread>"
}
LoadingLabel.text = String(format: "%2.0f", percentage)
ProgressBar.progress = Progress
}
I've saved a user input name in the previous storyboard into the xcdatamodel under the entity "UserInfo" with the name "name". I'm trying to fetch it in the next storyboard to display in a label to greet the user. I'm getting the error "Cannot Invoke Initializer for type 'NSFetchRequet' with an Argument with list of type '(entityName:String, attributeName: String)"
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
//getting the managed context where the entity we need is
let managedContext = appDelegate.persistentContainer.viewContext
//make fetch request
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "UserInfo", attributeName: "name")
//try to fetch the entity we need, else print error
do {
Username = try managedContext.fetch(fetchRequest)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
There is no initializer for NSFetchRequest that takes the argument attributeName:, just like the error says. Your options are NSFetchRequest(entityName:) or NSFetchRequest().
When in doubt, look up the class in the API reference to make sure you understand how to use it.
NSFetchRequest doesn't have an initializer entityName:attributeName, you have to use
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "UserInfo")
fetch returns always an array. If there is only one record in the entity, get the first item and the value for attribute name:
do {
let users = try managedContext.fetch(fetchRequest)
if let user = users.first {
Username = user.value(forKey: "name")
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
If there are multiple records you might apply a predicate.
And don't guard AppDelegate. If this class didn't exist the app won't even launch. The exclamation mark is safe as safe can.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
I have a list of categories that my user can choose. Since I am planning to add new categories without having to send a new update, I would like to load the new categories from my server every time a user launch the app. I came up with this code but every time it runs it keeps adding the same categories.
func loadDealCategory(){
let myUrl = NSURL(string: "\(ipAddress)/v1.0/dealCategory.php")
let request = NSMutableURLRequest(URL: myUrl!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{ data, response, error in
if error != nil {
print("error\(error)")
}else{
do{
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSArray
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
for jsonDictionary in json {
// listing of columns in JSON seed file
let categoryDescription = jsonDictionary.valueForKey("category_description") as! String
print(categoryDescription)
let newCategory = NSEntityDescription.insertNewObjectForEntityForName("Categories", inManagedObjectContext: managedContext)
newCategory.setValue(categoryDescription, forKey: "category_description")
// I tried also format: "category_description = %#"
let fetchRequest = NSFetchRequest(entityName: "Categories")
fetchRequest.predicate = NSPredicate(format: "category_description LIKE %#", categoryDescription)
do {
let fetchResults = try managedContext.executeFetchRequest(fetchRequest)
print(fetchResults)
if fetchResults.count == 0{
do {
try managedContext.save()
//5
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}else{
print("\(categoryDescription) already exist!")
}
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
}
} catch{
print("something went wrong loading json")
}
}
}
task.resume()
}
is it ok , loading it in func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Found the problem, I was inserting the object before checking if it was already in the entity.
if fetchResults.count == 0{
let newCategory = NSEntityDescription.insertNewObjectForEntityForName("Categories", inManagedObjectContext: managedContext)
newCategory.setValue(categoryDescription, forKey: "category_description")
}else{
print("Could not save \(error), \(error.userInfo)")
}
and only then save
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
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