Realm: rearranging list of objects - ios

Please, tell me, how to rearrange items of Realm's list of objects by index? I.e. I'm looking something like
let movingElement = array[oldIndex]
array.removeAtIndex(oldIndex)
array.insert(movingElement, atIndex: newIndex)
if it was with a casual Swift array of something.
But for List in Realm I can not do the same thing:
let realm = try! Realm()
var all = try! Realm().objects(element)
realm.write {
all.removeAtIndex() // all of type
Another option is to
let realm = try! Realm()
let element = try! Realm().objects(Element)[oldIndex]
realm.write{
realm.delete(element)
realm.add(...) // How to set index to place new object at?
}
But how to insert element in proper place? May be there is a proper method how to move elements of realm of the same type (class) by index?
Thanks in advance!

You cannot do it with query results, as they are unordered (or in a specific order if you sort them). But if you put them into Realm List (which you can store as member in a Realm object), then you can use both move and swap methods to reorder elements.
Here is the API docs for the the List type: https://realm.io/docs/swift/latest/api/Classes/List.html

Related

How to create initial Realm objects that get added upon installation of app

Say I am creating an object that takes two strings and acts like a dictionary.
class WordInDictionary: Object {
#objc dynamic var word: String = ""
#objc dynamic var meaning: String = ""
What should I do if I wanted to have some initial objects that get added to the database just once upon installation/update of the app?
Also, is there a way to make it so that just those initial objects can't be deleted?
"What should I do if I wanted to have some initial objects that get added to the database just once upon installation/update of the app?"
One option would be to have some code near the realm initialisation that checks if there are any WordInDictionary objects already in the realm - if not then add the required default objects.
E.g.
let realm = try! Realm()
if realm.objects(WordInDictionary.self).isEmpty
{
// Add required words here
}
"Also, is there a way to make it so that just those initial objects can't be deleted?"
I don't know of a way to make realm objects read-only. You'd have to implement this in code in some way, e.g. have a isDeletable boolean member which is true for every user-created object and false for your default members, then only delete those from realm.
E.g. for your deletion code:
func deleteWords(wordsToDelete: Results<WordInDictionary>)
{
try! realm.write
{
realm.delete(wordsToDelete.filter("isDeletable = true")
}
}

Delete objects from List in Realm - Swift

I have a collection view where you can select multiple cells to delete. This means that if multiple cells are deleted, then multiple objects should also be deleted in Realm - 1 object per cell.
I have a function which takes an array of Int's which will be populated from the selected indexPaths of the collection view.
The problem is that I'm not sure how to do both
1) Delete the objects in Realm and
2) Have the List up to date without the deleted objects
My code is:
I get the index paths like so:
let indexPaths = collectionView.indexPathsForSelectedItems
This is my function to take in the indexPaths, update the List, and delete the objects in Realm. It is currently not working because the objects are not being deleted. I noticed that removeAll does not delete anything.
func removeVideos(at indexes: [Int]) {
let newVideos = List<Video>()
for (index, video) in favorite!.videos.enumerated() {
if !indexes.contains(index) {
newVideos.append(video)
}
}
let realm = try! Realm()
try! realm.write {
favorite!.videos.removeAll()
newVideos.forEach { newVideo in
favorite!.videos.append(newVideo)
}
}
}
I call that function like so:
removeVideos(at: indexPaths.map { $0.item })
Any thoughts?
List.removeAll() doesn't delete the objects from Realm. It just removes them out of that List object, deleting their relationship to their parent object (in this case, the favorite object). Deleting objects along with their parent List object is a feature called 'cascading deletes' and it's still being discussed on the Realm GitHub.
If you actually want to delete them, then simply call realm.delete(favorite!.videos). This will delete them from Realm and automatically clear out the List property.
You might need to be careful with your implementation there though. Once an Object has been deleted from Realm, any existing references to it cannot be re-added back into Realm. It may just be appropriate to just delete the newVideo objects themselves instead of cleaning out the whole List.
func removeVideos(at indexes: [Int]) {
let newVideos = [Video]()
for (index, video) in favorite!.videos.enumerated() {
if !indexes.contains(index) {
newVideos.append(video)
}
}
let realm = try! Realm()
try! realm.write {
realm.delete(newVideos)
}
}
As long as you've set a Realm notification block on your collection view, this should be all you need to do for them to be removed from your UI.

Realm: live updates of constant values

I'm using SwiftRealm 2.03 and do not understand the magic how constant data (even metadata) gets updated if the data in realm changes...
Here an example:
private func closePastExistingTravelTimes(){
let travelTimes = fetchTravelTimes(onlyNotClosedTravelTimes: true)
guard travelTimes.count > 1 else {
return
}
let numberOfTravelTimes = travelTimes.count
for index in 0..<numberOfTravelTimes-2{
print("index:\(index) count:\(travelTimes.count)")
try! realm.write {
let travelTime = travelTimes[index]
travelTime.travelPhaseIsClosed = true
realm.add(travelTime, update: true)
}
}
}
I'm loading data in the beginning and store them in an constant.
Then I iterate over the items and change the condition of the query so that the fetched data would change if I would query again. But I don't. What even is more suprising that the constant numberOfTravelTimes is even adjusted as you can see below in the log.
index:0 count:5
index:1 count:4
index:2 count:3
index:3 count:2 --> BAM - Exception
What is happening here? How can I be save in my example?
Realm Results objects are live, meaning if you update an object in such a way that it no longer conforms to a Results query, the Results object will update to exclude it. This means you need to be careful when you base a for loop off a Results object, since if the Results object mutates in the middle of the loop, you'll end up with an exception.
Normally, the easiest, but not the most elegant way to mitigate this is to copy all of the Realm objects in a Results object to a static array so it won't mutate during the loop.
In this particular case however, it would be more appropriate to just enclose the entire for loop inside the Realm write transaction. This is generally best practice (Since it's best to batch as many Realm writes into as few write transactions as possible), but in this case, it will have the added advantage of not updating the contents of the Results object until after you're done with it.
try! realm.write { // Open the Realm write transaction outside of the loop
for index in 0..<numberOfTravelTimes-2 {
print("index:\(index) count:\(travelTimes.count)")
let travelTime = travelTimes[index]
travelTime.travelPhaseIsClosed = true
realm.add(travelTime, update: true)
}
}
You should iterate your results in reverse order.
for index in (0 ..<numberOfTravelTimes-1).reverse()
See if it helps

Fetching Single Row in Realm

I'm having a problem in fetching a single row in a table. My problem is that I cannot display it. Here is what I have so far
let feed: RLMObject = FeedsModel.objectsWhere("id = 1").firstObject()!
print(feed.title)
Thank you very much!
You should fetch the single object with primary key like this:
let realm = try! Realm()
let feeds = realm.objectForPrimaryKey(FeedsModel.self, key: "1")
You're attempting to access the title property of RLMObject, which doesn't exist. This property only exists on FeedsModel. Instead, you should cast the object as a FeedsModel:
let feed = FeedsModel.objectsWhere("id = 1").firstObject() as! FeedsModel
print(feed.title)
If you'd like to use Realm from Swift with nicer generics, I'd encourage you to consider using Realm Swift instead of Realm Objective-C.

Convert Results<t> to [AnyObject]

i'm using realm as a backend, however when i wanna show my realm in the following class i keep getting following error: Cannot convert value of type Results<League> to expected argument type [AnyObject]]?
do i need to map the result or what is my options?
let realm = try! Realm()
let predicate = NSPredicate(format: "#matches.#count > 0")
menuArray = realm.objects(League).sorted("id").filter(predicate)
let menuView = BTNavigationDropdownMenu(title: menuArray!.first!.name!, items: menuArray!)
self.navigationItem.titleView = menuView
I think there is a difference between a value of type Results<Object> (which is a custom Realm container type) and a value of type [AnyObject] (which is a Swift array of AnyObject objects), so you're probably right in thinking that you need to convert the result.
Try this:
let menuView = BTNavigationDropdownMenu(title: menuArray.first!.name!, items: menuArray.map { $0 })
This approach is probably OK in the context of a simple dropdown menu like the one you're using, but there may be a degradation in performance in other contexts.

Resources