netshark1000 netshark1000 - 1 month ago 6
Swift Question

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?

TiM TiM
Answer

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)
    }
}