Swift and Realm Error with Object Management [duplicate] - swift

Im getting this error "Can only delete an object from the Realm it belongs to" every time I try to delete an object from realm on my tableview. Here is the relevant code:
let realm = try! Realm()
var checklists = [ChecklistDataModel]()
override func viewWillAppear(_ animated: Bool) {
checklists = []
let getChecklists = realm.objects(ChecklistDataModel.self)
for item in getChecklists{
let newChecklist = ChecklistDataModel()
newChecklist.name = item.name
newChecklist.note = item.note
checklists.append(newChecklist)
}
tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return checklists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistCell", for: indexPath) as! ListsTableViewCell
cell.name.text = checklists[indexPath.row].name
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
try! realm.write {
realm.delete(checklists[indexPath.row])
}
//delete locally
checklists.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .fade)
}
}
I know it is this part to be specific:
// Delete the row from the data source
try! realm.write {
realm.delete(checklists[indexPath.row])
}
Any ideas of what is going on?
Thanks in advance!

You are trying to delete copies of your Realm objects stored in a collection instead of your actual Realm objects stored in Realm.
try! realm.write {
realm.delete(Realm.objects(ChecklistDataModel.self).filter("name=%#",checklists[indexPath.row].name))
}
Without the definition of CheklistDataModel, I am not sure if I got the NSPredicate right, but you should be able to figure it out from here.

From the code snippets you shared, you appear to be creating new ChecklistDataModel objects but never adding them to any Realm. Then you attempt to delete these objects from your Realm in your try! realm.write block.
Simply instantiating an object does not mean it has been added to a Realm; until it is added to a Realm through a successful write transaction it behaves just like any other Swift instance. Only after you've added the object to a Realm can you successfully delete it from that same Realm.

Related

Getting Swift runtime error: "Can't end BackgroundTask: no background task exists with identifier 1"

I’m trying to implement the TableView example from Chapter 5 in Swift Programming in Easy Steps exercise. I have checked and re-checked the example code (even downloaded and tested the actual example code), but I’m still getting this runtime error. Anyone know why this is happening?
2019-11-01 07:56:51.247052+0100 TableView_EasySteps[2067:39485] Can't
end BackgroundTask: no background task exists with identifier 1
(0x1), or it may have already been ended. Break in
UIApplicationEndBackgroundTaskError() to debug.
here is the ViewController code:
import UIKit
class WebsitesTableViewController: UITableViewController {
var websites:[[String]] = [
["Apple", "https://www.apple.com"] ,
["NY Times", "https://www.nytimes.com"] ,
["DN", "https://www.dn.se"] ,
["NFL", "https://www.nfl.com"] ,
["Premier League", "https://www.premierleague.com"] ,
["The Guardian", "https://www.theguardian.com"]
]
override func viewDidLoad() {
super.viewDidLoad()
// preserve selection between presentations
self.clearsSelectionOnViewWillAppear = false
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return websites.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cellIdentifier")
if cell == nil {
cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cellIdentifier")
}
cell!.textLabel!.text = websites[indexPath.row][0]
cell!.detailTextLabel!.text = websites[indexPath.row][1]
return cell!
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let url = URL(string: websites[indexPath.row][1]) {
UIApplication.shared.open(url)
}
}
// Override to support editing the table view.
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
websites.remove(at: indexPath.row)
// Delete the row from the data source
tableView.deleteRows(at: [indexPath], with: .fade)
}
}
}
Check the SceneDelegate.swift file and make sure it's part of the Control files if you're using MVC standard otherwise just make sure that it's part of the files in your Xcode project alongside the AppDelegate.swift and the ViewController.swift amongst others.

Trying to edit a List in realm

I'm trying to make a List with realm:
class TripsList : Object {
let trips = List<Trip>()
}
Then, inside my ViewController class:
var trips : Results<TripsList>?
override func viewDidLoad() {
super.viewDidLoad()
trips = realm.objects(TripsList.self)
}
When someone moves a UITableViewRow, I want to update my realm database.
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let movedObject = self.realm.objects(Trip.self)[sourceIndexPath.row]
trips.remove(at: sourceIndexPath.row)
trips.insert(movedObject, at: destinationIndexPath.row)
}
Here are my TableView Datasource methods:
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return realm.objects(Trip.self).count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.font = UIFont.systemFont(ofSize: 17)
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
cell.textLabel?.text = nameData.names[realm.objects(Trip.self)[indexPath.row].tripID]
return cell
}
The problem is there is no option to do trips.remove(at:) or trips.insert(_:at:).
My overall goal is the ability to insert and remove when someone moves a UITableViewRow and update my realm database.
You cannot directly modify a Results instance. Results are auto-updating collections, meaning that they always reflect the current state of the query you used to initialise them. You need to modify the Realm in order to modify the Results instance.
Moreover, a Results instance is only guaranteed to keep its ordering in case you sort it. So you'll need to introduce a property on Trip that you use to sort the objects and modify that property when the user moves a row.
Your TripsList class seems to be unnecessary, since it seems that you simply want to store a number of Trip objects in Realm, then retrieve them without actually doing any grouping. Even if you needed to group them, you could do so using Realm queries. Keeping this in mind, this is how I'd modify your current code to allow the user to sort their Trips and save the sorting to Realm.
class Trip: Object {
// Your existing code for Trip
...
// Sorting property
#objc dynamic var sortingIndex = 0
}
In your table view controller:
var trips : Results<Trip>?
override func viewDidLoad() {
super.viewDidLoad()
trips = realm.objects(Trip.self).sorted(byKeyPath: "sortingIndex")
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return trips?.count ?? 0
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = UITableViewCell()
cell.textLabel?.font = UIFont.systemFont(ofSize: 17)
cell.accessoryType = UITableViewCell.AccessoryType.disclosureIndicator
if let tripID = trips?[indexPath.row].tripID {
cell.textLabel?.text = nameData.names[tripID]
}
return cell
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
guard let movedObject = trips?.[sourceIndexPath.row] else { return }
// Depending on your exact needs, you might want to update the `sortingIndex` property of your other rows as well, whose position in the table view was affected by the reordering
try! realm.write {
movedObject.sortingIndex = destinationIndexPath.row
}
}

how to delete cell and data in firebase and swift 3

I'm trying to delete the data from firebase with no luck so far. This is the code I'm using, can anyone give me a hand with it please.
class TableViewController: UITableViewController {
var ref: FIRDatabaseReference?
var grocery = [Grocery]()
override func viewDidLoad() {
super.viewDidLoad()
loadData()
}
func loadData() {
let uid = FIRAuth.auth()?.currentUser?.uid
FIRDatabase.database().reference().child("Users").child(uid!).child("Grocery").observe(.childAdded) { (snspshot: FIRDataSnapshot) in
if let dict = snspshot.value as? [String: Any] {
let Items = dict["Item"] as! String
let Quintities = dict["Quintities"] as! String
let Done = dict["Done"] as! Bool
let themBe = Grocery(Items: Items, Quintitiess: Quintities, Dones: Done)
self.grocery.append(themBe)
print(themBe)
self.tableView.reloadData()
}
}
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return grocery.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "TasksTableViewCell") as! TasksTableViewCell
cell.titleLabel?.text = grocery[indexPath.row].Item
cell.numLabel?.text = grocery[indexPath.row].Quintities
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
self.grocery.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
}
}
----------
import Foundation
class Grocery {
var Item: String
var Quintities: String
var Done: Bool
init(Items: String, Quintitiess: String, Dones: Bool) {
Item = Items
Quintities = Quintitiess
Done = Dones
}
}
You are only deleting data for your UITableView. The logic that you need is to delete from your UITableView and Fireabase Database. As the firebase docs says you can either call removeValue, or setValue to nil or updateChildValues.
To make the deletion easier, I'd save the key of the object where the data is saved (snapshot.keys), so when you want to delete you can just get that key and perform actions.

How to delete specific coreData object from TableView

So I'm trying to delete data from a tableView, which It will delete the cell at the row, but it won't delete the information from coreData, causing it to load again when I call a .reloadData(). I'm really new to coredata and I don't know how to select a specific Recipe item that I make.
Here's where I handle the delete:
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
// handle delete (by removing the data from your array and updating the tableview)
recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
}
}
Here's how I am creating the items in coreData, if this helps. Also, I have a Git repository here if anyone's willing to look that deep
#IBAction func createNewRecipeButton(_ sender: Any) {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let newRecipe = Recipe(context: context)
newRecipe.title = recipeTitleTextBox.text
newRecipe.instructions = recipeInstructionsTextBox.text
newRecipe.time = recipeTimeTextBox.text
(UIApplication.shared.delegate as! AppDelegate).saveContext()
navigationController!.popToRootViewController(animated: true)
}
Your current removal method merely deletes the recipe from the storage array. You need to tell the context to get rid of it as well…
func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if (editingStyle == UITableViewCellEditingStyle.delete) {
let recipe = recipes.remove(at: indexPath.row)
tableView.deleteRows(at: [indexPath], with: UITableViewRowAnimation.automatic)
guard let moc = recipe.managedObjectContext else { return }
moc.delete(recipe)
moc.processPendingChanges()
}
}
You might also want to look into using a NSFetchedResultsController. There are several tutorials around.

Can only delete an object from the Realm it belongs to

Im getting this error "Can only delete an object from the Realm it belongs to" every time I try to delete an object from realm on my tableview. Here is the relevant code:
let realm = try! Realm()
var checklists = [ChecklistDataModel]()
override func viewWillAppear(_ animated: Bool) {
checklists = []
let getChecklists = realm.objects(ChecklistDataModel.self)
for item in getChecklists{
let newChecklist = ChecklistDataModel()
newChecklist.name = item.name
newChecklist.note = item.note
checklists.append(newChecklist)
}
tableView.reloadData()
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return checklists.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "ChecklistCell", for: indexPath) as! ListsTableViewCell
cell.name.text = checklists[indexPath.row].name
return cell
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
if editingStyle == .delete {
// Delete the row from the data source
try! realm.write {
realm.delete(checklists[indexPath.row])
}
//delete locally
checklists.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .fade)
}
}
I know it is this part to be specific:
// Delete the row from the data source
try! realm.write {
realm.delete(checklists[indexPath.row])
}
Any ideas of what is going on?
Thanks in advance!
You are trying to delete copies of your Realm objects stored in a collection instead of your actual Realm objects stored in Realm.
try! realm.write {
realm.delete(Realm.objects(ChecklistDataModel.self).filter("name=%#",checklists[indexPath.row].name))
}
Without the definition of CheklistDataModel, I am not sure if I got the NSPredicate right, but you should be able to figure it out from here.
From the code snippets you shared, you appear to be creating new ChecklistDataModel objects but never adding them to any Realm. Then you attempt to delete these objects from your Realm in your try! realm.write block.
Simply instantiating an object does not mean it has been added to a Realm; until it is added to a Realm through a successful write transaction it behaves just like any other Swift instance. Only after you've added the object to a Realm can you successfully delete it from that same Realm.