briancl briancl - 6 months ago 21
Swift Question

Replaced List<T> object not persisting consistently in Realm

I have a

List<Workout>
object that occasionally needs to be sorted (e.g., if a user adds a Workout out of order), but I can't seem to get the new sorted
List<Workout>
to persist. My code works the moment it runs (i.e., it shows up on the view as sorted), but when I exit the ViewController or restart the app, I see nothing. The nothing is due to the
exercise.workoutDiary.removeAll()
persisting, but apparently the subsequent assignment to the
exercise.workoutDiary = sortedWorkoutDiary
is not persisting. Any ideas why?

Everything else works just fine. The typical
recordWorkout()
case works assuming nothing is entered out of order. So the persisting is working in nearly all cases except for this overwrite of the sorted List.

The update happens here:

struct ExerciseDetailViewModel {

private let exercise: Exercise!

func recordWorkout(newWorkout: Workout) {
let lastWorkout = exercise.workoutDiary.last // grab the last workout for later comparison

let realm = try! Realm()
try! realm.write {
exercise.workoutDiary.append(newWorkout) // write the workout no matter what
}

if let secondToLastWorkout = lastWorkout { // only bother checking out of order if there is a last workout...
if newWorkout.date < secondToLastWorkout.date { // ...and now look to see if they are out of order
let sortedWorkoutDiary = exercise.sortedWorkouts
try! realm.write {
exercise.workoutDiary.removeAll()
exercise.workoutDiary = sortedWorkoutDiary
}
}
}
}
}

final class Exercise: Object {

var workoutDiary = List<Workout>()
var sortedWorkouts: List<Workout> {
return List(workoutDiary.sorted("date"))
}
}

final class Workout: Object {

dynamic var date = NSDate()
var sets = List<WorkSet>()
}

Answer

List<T> properties in Realm Swift must be mutated in place, not assigned to. The Swift runtime does not provide any way for Realm to intercept assignments to properties of generic types. Instead, you should use methods like appendContentsOf(_:) to mutate the List<T>:

exercise.workoutDiary.removeAll()
exercise.workoutDiary.appendContentsOf(sortedWorkoutDiary)

This limitation on assignment to properties of generic types is why the Realm Swift documentation recommends that you declare such properties using let rather than var. This will allow the Swift compiler to catch these sorts of mistakes.

One further note: for your sortedWorkouts computed property, it'd be preferable for it to return Results<Workout> instead to avoid allocating and populating an intermediate List<Workout>.

Comments