managedContext.insertObject extra argument in call `atIndex` in Swift? - ios

So here is my code: (I want to re-order the table and update Core Data):
override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
let entity = NSEntityDescription.entityForName(entity, inManagedObjectContext: managedContext)
let entityObject = NSManagedObject(entity: entity!, insertIntoManagedObjectContext: managedContext)
entityObject.setValue(content, forKey: key)
self.managedContext.insertObject(entityObject, atIndex: 0) //ERROR HERE
do {
try managedContext.save()
} catch let error as NSError {
}
}
I've seen similar code here but why mine isn't working? Thanks!

The link you provided is not doing the same thing you did here. What he did there was removing and inserting object in to a array of Playlist.
If you really want to re-order the table and update Core Data accordingly, you may want to add a index field to your Core Data model, and update it with the index of cell every time the cell is moved.
So you can populate the data to table view in order of the index filed, and keep cell order synchronized with data model.

First of all a side note: The objects in NSManagedObjectContext are unordered so there is no method to insert an object at an particular index.
Since the object is inserted already two lines above in the method NSManagedObject(entity:insertIntoManagedObjectContext:), delete the line which causes the error.

Agreed with Vadian, you do not need to worry about ordering the Managed Object Context.
To update the UI you should run -
"tableView.reloadData"
in addition to above code.

Related

How to insert multiple rows of a tableview, when selected, to core data with Swift 4?

I have two connected problems.
The first is - I’m trying to save selected items from a tableview into core data when the user clicks next, but I want to avoid duplications. However, if the items are already in core data, they need to be shown as already selected - then if the user deselects the row, it deletes that item from core data.
The second is - I’ve got a save function in my model that takes the name of the item and the name of the picture related to that item in the parenthesis and I want to use that in my tableview controller, to save the selected items as mentioned above, but I’m having trouble accessing the specific values. I thought of using cell.itemName[indexPath.row] but that doesn’t seem to exist.
Here is the save function:
func save(result_name: String, result_theme: String, result_pic: String, existing: ClothesResults?) {
let entity = NSEntityDescription.entity(forEntityName: "ClothesResults", in: managedContext)
let result = NSManagedObject(entity: entity!, insertInto: managedContext) as! ClothesResults
result.setValue(result_name, forKey: "resultName")
result.setValue(result_pic, forKey: "resultPic")
resultsDB.append(result)
update()
}
I thought of using an if statement in the prepareForSegue function in my tableviewcontroller like (pseudo code):
if !row exists {
insert into core data somehow
}

Parse.com query not working properly

I am trying to filter the posts based on their profile. For instance, when I go to my profile I only want to see my posts, not all the posts in my database. I attempted to make a filter for that but the code below does not seem to work and I am unsure as to why that is. It may be something obvious but I can not seem to pinpoint the issue, any ideas?
I have attached a picture of the database to further assist anybody.
The code runs perfectly fine it just does not filter the usernames.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var user = PFUser.currentUser()?.username!
let bucketCellObj = tableView.dequeueReusableCellWithIdentifier("bucket", forIndexPath: indexPath) as! BucketTableViewCell
var query = PFQuery(className: "Bucket")
query.whereKey("creator", equalTo: user!)
query.findObjectsInBackgroundWithBlock { (PFObject, error) -> Void in
if error == nil {
bucketCellObj.bucketname.numberOfLines = 0
bucketCellObj.username.text = self.bucketsArray[indexPath.row]["creator"] as? String
bucketCellObj.bucketname.text = self.bucketsArray[indexPath.row]["name"] as? String
bucketCellObj.selectionStyle = .None
} else {
print("ERROR")
}
}
return bucketCellObj
}
What you are doing here might work under some circumstances but will certainly bite you at some point.
What your code does:
show some arbitrary number of cells - probably based on self.bucketsArray.count or something similar
in each cell creation, run a parse query
when the query returns, customize the already displayed cell accordingly - without any usage of the requested query response
That will cause problems for a couple of reasons:
you perform too many requests, each cell requests some data, each new displayed cell requests its own data again
you display the old data in the cell until the new data is fetched which could take a few seconds due the amount of requests
you could encouter a problem where you requests some data for a cell, that cell moves off-screen, gets reused, then the first query returns, still holds the reference to it and will therefore display wrong data
How it can be solved
Do not requests the data in the cellForRowAtIndexPath.
Request the data once in viewDidLoad or similar. as soon as the data gets returned, parse it and initiate a tableView.reload().
In the cellForRowAtIndexPath make use of the already retrieved data, do not perform anymore async tasks.

Delete items from tableview row and core data simultaneously

I'm trying to delete items from a TableView and an entity called "Books." I have no idea if I'm remotely on the right track, however. When I try this code:
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
var appdel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
var context:NSManagedObjectContext = appdel.managedObjectContext!
var request = NSFetchRequest(entityName: "Books")
if editingStyle == UITableViewCellEditingStyle.Delete {
addBook.myBooks.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
context.deleteObject(addBook.myBooks[indexPath.row] as! NSManagedObject)
}
}
I get a warning on the last line that says "Cast from 'String' to unrelated type 'NSManagedObject' always fails." Does anyone know how I can get around this? I've read that you can use a fetchedResultsController to handle core data in tables easily but I am new to programming and found that method a bit more confusing when setting up core data in general. Is the fetchedResultsController necessary to manage the data in my TableView?
From your error it sounds like addBook.myBooks is an array of strings.
The immediate problem is that deleteObject doesn't work on strings, it works on managed objects-- that is, instances of NSManagedObject or a subclass of NSManagedObject. You can't delete a string from Core Data like that, you have to delete the managed object that corresponds to the string. The error is specifically telling you that as! NSManagedObject doesn't work on a string, because a string is a completely different kind of thing from a managed object.
[It's also a problem that you're removing the string at indexPath.row via removeAtIndex, and then later trying to use the string at indexPath.row that you just removed, but that's not the real problem here.]
What you need to do is find out the managed object that corresponds to the table view row you're deleting, and pass that to deleteObject. Without a fuller picture of how your view controller works it's impossible to say exactly how you would do that, but there are a couple of things that are clear:
Those first three lines in your method are not doing anything useful. Cut them-- even if you made them work, they'd be the wrong approach here. You don't want to have to fetch the managed object you're deleting right here. By the time you reach this method you should already know enough to delete it.
It's not necessary to use NSFetchedResultsController to put Core Data together with table views. But if you're new to programming you'll probably find things a lot easier if you use it.
Try to save your context after deleteObject
context.deleteObject(addBook.myBooks![indexPath.row])
addBook.myBooks.removeAtIndex(indexPath.row)
do {
try context.save()
}
catch {
print("Error")
}
self.tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Automatic)
}

iOS Swift: Delete parse array item referenced by index

I have data stored in parse column with following column keys
phone = ["iPhone", "Galaxy S6", "Nexus"]
quantity = [20, 30, 20]
boolV = [[true, false], [true, false], [false,true]]
I am presenting this date in tableView format to the user and want to give him the option to delete these items.
At present I am following this:
Inside tableView commitEditingStyle function
// find the index value of user selected cell
var indexValue = find(phone, "iPhone") // 0
// Using this, I can delete the array elements from phone like this
quantity.removeAtIndex(indexValue!)
boolV.removeAtIndex(indexValue!)
To delete from parse I using the following:
object.removeObject("iPhone", forKey: "phone") // This works fine
object.removeObject("self.quantity[indexValue]", forKey: "quantity") // But this deletes both "20" quantity from the table
object.removeObject("self.boolV[indexValue]", forKey: "boolV") // again it deleted both "[true, false]" values form the array.
I am sure I am missing something here. I just want to delete array item pointed by the indexValue. How can I do that in Parse? Any help would be appreciated.
Thanks.
This really seems like you should use a different data model with 2 classes and a relationship rather than 1 class and a set of array attributes. Then, to do the deletion you remove an item from the relationship and delete it from the data store.
The way you have it at the moment you can't delete individual items from the array, you need to take the array, edit it and then store the full new updated array.
you should delete in the background like this: objects is your tableView connected array
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if editingStyle == .Delete {
// Delete the row from the data source
let objectToDelete = objects?[indexPath.row] as! PFObject
objectToDelete.deleteInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// Force a reload of the table - fetching fresh data from Parse platform
self.loadObjects()
} else {
// There was a problem, check error.description
}
}
} else if editingStyle == .Insert {
// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
}
}

CoreData many to many relationship Best practices

give the structure:
Person
field1
...
fieldn
>>itemsTaken(inverse: takenFrom)
Item
field1
...
fieldn
>> takenFrom(inverse: itemsTaken)
Person.itemsTaken <<------>>Items.takenFrom
the scenario is that I have a list of Persons and a list of Items
now I would to show within the Person Detail View the items he taken (this is simply solved), and to show on the Item detail View the complete list of persons and select a subset of person that taken that item.
the problem is the 2nd view where I would to add/remove from the orderedset "takenFrom" a person.
override func tableView(tableView: UITableView!, didSelectRowAtIndexPath indexPath: NSIndexPath!) {
let mo = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
if var x: NSMutableOrderedSet = mo.valueForKey("itemsTaken") as? NSMutableOrderedSet {
x.addObject(detailItem)
mo.setValue(x, forKey: "itemsTaken")
}
if var x: NSMutableOrderedSet = detailItem.valueForKey("takenFrom") as? NSMutableOrderedSet {
x.addObject(mo)
detailItem.setValue(x, forKey: "takenFrom")
}
(UIApplication.sharedApplication().delegate as AppDelegate).saveContext()
}
override func tableView(tableView: UITableView!, didDeselectRowAtIndexPath indexPath: NSIndexPath!) {
let mo = self.fetchedResultsController.objectAtIndexPath(indexPath) as NSManagedObject
if var x: NSMutableOrderedSet = mo.valueForKey("itemsTaken") as? NSMutableOrderedSet {
x.removeObject(detailItem)
mo.setValue(x, forKey: "itemsTaken")
}
if var x: NSMutableOrderedSet = detailItem.valueForKey("takenFrom") as? NSMutableOrderedSet {
x.removeObject(mo)
detailItem.setValue(x, forKey: "takenFrom")
}
(UIApplication.sharedApplication().delegate as AppDelegate).saveContext()
}
that works but when I restarted the app all references for relationship are lost
but I'm not sure I'm proceeding in the correct way.
before this approach I had 2 more entities in order to have for each entity a one 2 many relationships. I'm looking for a cleaner solution
do you have any suggestion or question?
just a clarification. the target is to add/remove references from person.itemsTaken and/or Item.takenFRom
I would to avoid to delete Person. can I remove only the reference within the relationship?
"Cleaner solution": first step would be to make your code more readable. Why call a variable mo or x if the object is better described as a person or as itemsTaken? An additional strategy to make your code more readable is to use NSManagedObject subclasses.
Second, it seems that you are adding the relationship twice (once for each side). I don't think this is necessary.
Third, you might want to check if your mutable ordered set is extracted the way you expect. I think it might be advisable to use the mutable accessor instead:
var itemsTaken = person.mutableSetValueForKey("itemsTaken")
Not sure if you still have to cast or do other things in order to keep the ordering in the ordered set. In my experience, the ordered set never really worked reliably even in Objective-C, so you might just want to keep an additional attribute for the ordering and change the model to use a simple many-to-many relationship.

Resources