Swift UITableView Realm wrong order after delete - swift

I have a database in Realm which i display in a UITableView which works perfectly. I added a delete functionality which lokks like below.
if let item = items?[indexPath.row] {
do {
try realm.write {
realm.delete(item)
}
} catch {
print("Error deleting item, \(error)")
}
tableView.reloadData()
}
Let's say I have some data in the database like 1,2,3,4,5. When I delete the '2' I want it to be like 1,3,4,5 but it is 1,5,3,4. So the last entry respectively the entry which is displayed at the bottom will always replace the deleted data. How can I fix it?

Objects is Realm are stored as unordered if you want them in order then you have to apply below code or store it as a List.
let result = realm.objects(ModelClass).sorted("attribute", ascending: true)
ModelClass is the name of your Realm Object Class
Attribute is
Object property on the base of which you want them.
Hope it will help you.

Related

Nill value when passing data beetween related entities in Core Data

I'm trying for some days now (without success) saving on Core Data some properties of an entity ("Alumno") just after saving on its "children" viewController ("Especialidad") its related value.
Mine is a music teaching app, just as a contacts one. I pass the selected "Especialidad" (the played instrument in this case), after saving it to Core Data, for the correspondent "segue" to be performed, from its table view
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
especialidadSeleccionada = especialidades[indexPath.row]
performSegue(withIdentifier: "GuardarEspecialidad", sender: self)
}
to its related "parent", the complete pupil's data, containing this "Especialidad", among others properties.
Next, I try to save every pupil's data on Core Data:
if nombreField.text != "" && especialidadField.text != "" {
let context = (UIApplication.shared.delegate as!
AppDelegate).persistentContainer.viewContext
let nuevoAlumno = Alumno(context: context)
nuevoAlumno.nombre = nombreField.text!
print("Data before: \(especialidadSeleccionada)")
nuevoAlumno.especialidadRelacionada?.nombre = especialidadSeleccionada?.nombre
print("Data after: \(nuevoAlumno.especialidadRelacionada?.nombre)")
//... and so on with the rest of properties
do {
try context.save()
} catch {
print("error")
}
But it saves every property EXCEPT "nuevoAlumno.especialidadRelacionada?.nombre", marked as nill. As you can see, for debugging purposes, I print its value BEFORE the equality, obtaining a non-optional value, and AFTER it, getting... an optional value!!
I'm almost sure that it has to be something about their relationship. I've double-checked that "Especialidades" exact data is correct, but I really can't find the way to save "especialidad.nombre" in this "Alumno" entity...
An idea: maybe using "didSet" for that property of "Alumnos" could help? I've tried, but don't know how.
Any help? Thanks in advance!
I have reached to a solution. I was just using an incorrect property syntax:
nuevoAlumno.especialidadRelacionada = especialidadSeleccionada
Now it works.
Looks to me like nuevoAlumno.especialidadRelacionada might be nil. Note that you're printing two different things in your before/after. I'd say set a breakpoint on this line:
nuevoAlumno.especialidadRelacionada?.nombre = especialidadSeleccionada?.nombre
and inspect all the values there.

Realm updates are not saving

So I'm trying to update a List property of an Object class in Realm. The property of the Object updates, but for some reason when it gets to the Realm, the update never occurs. In other words, I've confirmed that the changes have been made to the actual object just after I made the changes(the List updates in the object), but after I add to the realm, and then get the object back from the realm, it's as if nothing ever changed. Here is the code:
do{
try! realm.write{
let course = realm.objects(Course.self).filter("id =='\(courseID!)'").first
course!.days = List<String>()
for day in daysSelected{
course?.days.append(day)
}
realm.add(course!, update: .modified)
}
}catch{
print(error)
}
Also, you should know that when I update other properties like Strings, the changes go through just fine. Am I doing something wrong with lists? In my object class, the list is declared as:
var days: List<String> = List<String>()
According to the documentation:
Properties of List type defined on Object subclasses must be declared
as let and cannot be dynamic.
Rather than defining your list as:
var days: List<String> = List<String>()
Define it as:
let days = List<String>()

SWIFT: Print data from firebase database in tableview

I am new to firebase.
I want to make an app with data from my Firebase database to show in a tableview.
Example:
Let's say I have made a database that looks like this:
{
"fruits": {
"Apple": {
"color": "Red",
"taste": "Bla bla...",
},
"Banana": {
"color": "Yellow",
"taste": "Bla bla...",
},
"Pear": {
"color": "Green",
"taste": "Bla bla...",
},
}
}
Firebase automatically sort the items in my database by alphabetic order. Now I want to make a tableview with all the fruits listed in alphabetic order. When I tap on a fruit it will push to a new window with the informations about that specific fruit like "color", "taste", etc.
The important thing to keep in mind is that, whenever I'm adding items to my database (like more types of fruit), it has to keep showing in alphabetic order. I HAVE read the documentation, but without any luck.
How do I do this? I know how to make tableviews and create databases. But how do I connect my database to my tableview and make it print out my data from a-z? maybe by index number?
Thank you. :-)
You may want to consider a different structure
fruits
fruit_id_0 <- key created with childByAutoId
name: "Apple"
color: "Red"
taste: "blah blah"
From there you have a ton of options.
One option is to use .childAdded, which will iterate over all of your nodes, one at a time and leave an observer which notifies the app whenever a new child is added. The dataSource array is ordered by name in code.
let fruitsRef = self.ref.child("fruits")
fruitsRef.observe(.childAdded, with: { snapshot in
let fruitDict = snapshot.value as! [String: Any]
let fruitName = fruitDict["name"] as! String
self.fruitArray.append(fruitName)
self.fruitArray.sort() //sorts in place
self.myTableView.reloadData()
})
From there, any time a new fruit is added the closure will be called. The newly added fruit is added, array sorted and UI updated.
If you have just a few entries the above is fine, but if you have a lot of fruit like I do, it would sort and update the tableView after each fruit is loaded, over and over which could cause flicker or other undesired results.
A better option would be to leverage the fact that .value observers occur after .child added observers. With that in mind, you can load up your entire initial dataset, and once that's complete, sort it and update the tableView. At the same time leaving an observer for new fruit which will add it to the dataSource, sort and update your UI.
Here's some code that would be initated with a button click
func button0() {
self.configureChildAdded()
self.setInitialLoadCompleted()
}
two class vars to keep track of where we are in the loading process and an array to store the fruit names
var initialLoad = true
var fruitArray = [String]()
and then two classes that will initially load and sort your fruits and leave a .childAdded observer which will watch for additional fruits and add them to the dataSource, sort, and update a tableView
func configChildAdded() {
let fruitsRef = self.ref.child("fruits")
fruitsRef.observe(.childAdded, with: { snapshot in //iterate over all fruits
let fruitDict = snapshot.value as! [String: Any]
let fruitName = fruitDict["name"] as! String
self.fruitArray.append(fruitName)
//upon first load, don't reload the tableView until all children are loaded
if ( self.initialLoad == false ) {
self.fruitArray.sort()
self.myTableView.reloadData()
}
})
}
//this .value event fires AFTER the child added events to load the tableView the first time
// all this does is update the UI and set the initalLoad to true and only fires once
func setInitialLoadCompleted() {
let fruitsRef = self.ref.child("fruits")
fruitsRef.observeSingleEvent(of: .value, with: { snapshot in
self.myTableView.reloadData()
self.initialLoad = false
})
}
Note that using this method will totally avoid using any kind of Firebase ordering as it's done in code. This would make it much easier to do multi field ordering etc.

How to add two items at a specific index in an entity in CoreData in Swift

I am moving rows inside a collectionView, I have no sorting on the data so all new cells are inserted at the end of the table, but when I move a cell I want to specifically select the index where it should be moved inside the entity in core data, this way the collectionView will automatically display the moved cell at the updated row without any additional work, is that possible without creating an additional ID attribute?
The entity is named List and it has two attributes, task_name and task_bool.
var myData:[NSManagedObject] = []
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
let newItem: AnyObject = NSEntityDescription.insertNewObjectForEntityForName("List",
inManagedObjectContext:
managedContext)
var deleltedRow = myArray[indexPath.row].valueForKey("task_name") as! String
myData.removeAtIndex(indexPath.row)
newItem.setValue(deleltedRow, forKey: "task_name"); // how would I add atIndex: here??
newItem.setValue(true, forKey: "task_bool"); // how would I add atIndex: here??
Core data maintains an unordered collection of objects. If you want to display those objects in a specific order then you need to sort them, either while fetching or afterwards. That means that the objects need to have some information inside them on which you can sort. You don't have anything currently so you'd need to add an order type attribute and maintain it with appropriate values.
Core Data does not store objects in an ordered manner. If you want to display these objects in a specific order, you will need a property/attribute in your entity which lets you order the list the way you want.
It might be a good idea to add a order attribute to the entity and use that for the ordering.

Delete all data from specific Realm Object Swift

Before i get too far into my question. My goal, which may influence your answers, is to remove Object data if it is no longer in the cloud.
So if I have an array ["one", "two", "three"]
Then in my server I remove "two"
I want my realm to update the change.
I figure the best way to do this is to delete all data in the specific Object, then call my REST API to download the new data. If there is a better way, please let me know.
Okay so here is my problem.
I have an Object Notifications()
every time my REST API is called, before it downloads anything I am running this:
let realm = Realm()
let notifications = Notifications()
realm.beginWrite()
realm.delete(notifications)
realm.commitWrite()
I get this error after running: Can only delete an object from the Realm it belongs to.
so i tried something like this:
for notification in notifications {
realm.delete(notification)
}
realm.commitWrite()
The error I get within xcode is this: "Type Notifications does not conform to protocol 'SequenceType'
Not really sure where to go from here.
Just trying to figure out realm. Completely new to it
Note: realm.deleteAll() works, but I don't want all of my realm deleted, just certain Objects
You're looking for this:
let realm = Realm()
let deletedValue = "two"
realm.write {
let deletedNotifications = realm.objects(Notifications).filter("value == %#", deletedValue)
realm.delete(deletedNotifications)
}
or perhaps this:
let realm = Realm()
let serverValues = ["one", "three"]
realm.write {
realm.delete(realm.objects(Notifications)) // deletes all 'Notifications' objects from the realm
for value in serverValues {
let notification = Notifications()
notification.value = value
realm.add(notification)
}
}
Although ideally, you'd be setting a primary key on Notifications so that you can simply update those existing objects rather than taking the extreme approach of nuking all your local objects simply to recreate them all (or almost).