how to update an object in realm swift

jammyman34 picture jammyman34 · Apr 26, 2017 · Viewed 39.8k times · Source

I'm trying to learn how to use Realm Swift and Charts so i can eventually use them both in an app I'm building and I'm having a hell of a time figuring out Realm. Eventually I'm planning on having Charts look at my Realm DB and then display the charts based on the data, but before I do that I have a need to check to see if a realm object exists yet, if it does not, to create it and then as the user uses the app add 'counts' to that record and update the chart accordingly.

As I'm learning I've broken this down into steps. I've already figured out how to check to see if a record exists and if not to build it like this:

My Realm Model:

class WorkoutsCount: Object{    
    dynamic var date: Date = Date()
    dynamic var count: Int = Int(0)
}

// function to check if this weeks days have been created in Realm DB yet and creates them if not
    let realm = try! Realm()
    lazy var workouts: Results<WorkoutsCount> = { self.realm.objects(WorkoutsCount.self)}()
    let startOfWeekDate = Date().startOfWeek(weekday: 1)
    let nextDay = 24 * 60 * 60

    // checks the DB to see if it contains the start of this week
    func searchForDB(findDate: Date) -> WorkoutsCount?{
        let predicate = NSPredicate(format: "date = %@", findDate as CVarArg)
        let dateObject = self.realm.objects(WorkoutsCount.self).filter(predicate).first

        if dateObject?.date == findDate{
            return dateObject
        }
        return nil
    }

    func setThisWeeksDays(){
        //if the beginning of this week doesn't exist in the DB then create each day with 0's as the count data
        if searchForDB(findDate: startOfWeekDate) == nil{
            try! realm.write() {

                let defaultWorkoutDates = [startOfWeekDate, startOfWeekDate + TimeInterval(nextDay), startOfWeekDate + TimeInterval(nextDay*2), startOfWeekDate + TimeInterval(nextDay*3), startOfWeekDate + TimeInterval(nextDay*4), startOfWeekDate + TimeInterval(nextDay*5), startOfWeekDate + TimeInterval(nextDay*6)]

                for workouts in defaultWorkoutDates {
                    let newWorkoutDate = WorkoutsCount()
                    newWorkoutDate.date = workouts
                    self.realm.add(newWorkoutDate)
                }
            }            
            workouts = realm.objects(WorkoutsCount.self)
        }
    }

I've verified that his works via the Realm Browser app.

Next on my To-Do list is to figure out how to update a record for "todays date's record". To do this I've created a button so when tapped it would attempt to do this. I've been googling and googling and have come to think since I don't use a primary-key in my Model that I have to first delete the specific record in question and then add it again with the new data. I can't for the life of me figure out how to do this based on the Realm documentation and even more googling. This is what I've got, though it does not work:

@IBAction func btnUpdate1MW(_ sender: Any) {
        if searchForDB(findDate: today) != nil{
            if plusOne <= 7{
                plusOne += 1
                CounterImage1MW.image = UIImage(named:  "1MWs-done-\(plusOne)")

                let realm:Realm = try! Realm()

                // deletes the original item prior to being updated and added back below
                let removeTodaysItem = today
                let workout = realm.objects(WorkoutsCount.self).filter("date = '\(removeTodaysItem)'")
                if workout.count > 0{
                    for date in workout{
                        try! realm.write {
                            realm.delete(date)
                        }
                    }
                }
                // adds back the item with an updated count
                do {
                    let realm = try Realm()
                    try realm.write {
                        realm.create(WorkoutsCount.self, value: ["date": today, "count": plusOne], update: false)
                    }
                } catch let error as NSError {
                    fatalError(error.localizedDescription)
                }
            }

            print("add to 1MW + 1")
        }
    }

When I tap the btnUpdate1MW button I get the following error in Xcode:

Terminating app due to uncaught exception 'Invalid value', reason: 'Expected object of type date for property 'date' on object of type 'WorkoutsCount', but received: 2017-04-24 07:00:00 +0000'

Answer

kishikawa katsumi picture kishikawa katsumi · Apr 26, 2017

Updating objects is just assign a value to a property within a write transaction. See our documentation.

https://realm.io/docs/swift/latest/#updating-objects

So you don't need to delete and then add an object. Just assign new value to the property in write transaction like the following.

let workouts = realm.objects(WorkoutsCount.self).filter("date = %@", removeTodaysItem)

let realm = try! Realm()
if let workout = workouts.first {
    try! realm.write {
        workout.date = today
        workout.count = plusOne
    }
}

FYI: Please don't use string interpolation in a query. Generally constructing an important string by string interpolation is bad practice. Use NSPredicate's string substitute syntax like filter("date = %@", removeTodaysItem).