如何在Realm Swift中更新对象

33
我正在尝试学习如何使用Realm Swift和Charts,以便最终在我正在构建的应用程序中同时使用它们,但是我很难弄清楚Realm。最终,我计划让Charts查看我的Realm数据库,然后根据数据显示图表,但在此之前,我需要检查是否已经存在一个realm对象,如果不存在,则创建该对象,并在用户使用应用程序时向该记录添加“计数”,并相应地更新图表。
当我学习时,我已将其分解为步骤。我已经找出如何检查记录是否存在,如果不存在,则构建它,如下所示:
我的Realm模型:
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)
        }
    }

我已经通过 Realm Browser 应用程序验证了他的工作。

接下来在我的待办列表上是要找出如何更新“今天日期记录”的记录。为此,我创建了一个按钮,当点击它时,会尝试这样做。我一直在搜索和搜索,并且认为由于我在模型中没有使用主键,所以我必须先删除特定的记录,然后再添加新数据。基于 Realm 文档和更多的搜索,我无法弄清楚如何做到这一点。虽然这就是我得到的代码,但它并不起作用:

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

当我点击btnUpdate1MW按钮时,Xcode会显示以下错误信息:
终止应用程序,原因是未捕获的异常“无效值”,原因是:“期望在类型为'WorkoutsCount'的对象上的属性'date'的类型为日期,但收到的是:2017-04-24 07:00:00 +0000”。

为什么不使用主键?它专门设计用于在更新现有对象时使用的情况。与删除和重新创建对象相比,它更容易使用,也是更优化的解决方案。 - Dávid Pásztor
因为当我回去添加主键时,它破坏了我已经工作的内容,在上面的第一组代码中在“let realm = try!Realm()”行出现错误,我无法弄清楚如何修复它。由于我花了一天时间让它工作,所以感觉像是在倒退一步。 - jammyman34
你是否在AppDelegate的applicationDidFinishLaunching方法中添加了迁移块? 如果没有,那就是你出错的原因,并且每次更改Realm模型时都会遇到相同的错误。 - Dávid Pásztor
3个回答

77

更新对象只需在写事务中将一个值分配给属性即可。请参阅我们的文档。

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

因此,您无需删除然后添加一个对象。只需在写事务中为属性分配新值,如下所示。

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

请注意:不要在查询中使用字符串插值。通常通过字符串插值构建重要字符串是一种不好的做法。请使用NSPredicate的字符串替换语法,例如filter("date = %@", removeTodaysItem)


有没有一种方法可以设置“workouts”变量,使其使用CONTAIN筛选器?自从我尝试使用谓词并且文档不清楚以允许我弄清楚它以来,我一直无法找出该语法...而且我也知道“不要使用字符串插值”的部分,但我很绝望,那些是我在网上找到的例子,但还是谢谢你澄清了 :) - jammyman34
在我的情况下,我收到了错误消息“workouts是一个let变量”,因此无法使用该方法更改属性。 - Free Bird

0
class ViewController: UIViewController {
    
    var personInfo : PresonInfo?
    let realm = try! Realm()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    func updateData(){
        let user = realm.objects(PresonInfo.self).where {
            $0.username == userNameLbl.text!
        }.first!
        try! realm.write {
            user.username = personInfo!.username
        }
        
    }
    
    @IBAction func updateBtnClick(_ sender: Any) {
        update()
    }
    
}

-1
和我一起工作
  func increaceProductQuntity(code: String) {
        let real = realm.objects(ProductsRealm.self).filter { $0.code == code
        }
        if let workout = real.first {
            internalQueue.sync {
                try! self.realm.write {
                    workout.pQuantity += 1
                    workout.totalPrice = String((Double(workout.pPrice) ?? 0.0) * Double(workout.pQuantity))
                }
            }
    
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接