什么是最佳实践 - Realm Ordered List?

时间:2016-09-26 07:31:14

标签: ios swift realm swift3 realm-list

我一直在使用Realm一段时间,我对它非常满意! 但是,我在实施过程中偶然发现了一些问题。

我做了一个测试场景,试图指出我需要输入的地方。

我有一个包含Person对象数据库的领域。这些都在UITableView中呈现。我想保留对象的特定顺序,用户应该能够重新排序对象。从我所读到的,我必须使用Realms'List'来实现这一目标。这再次意味着我有一个名为Person的类和一个名为PersonList的类。 PersonList只有一个属性: - list。

app应该只在其Realm中有一个PersonList对象,但可能有多个Person对象。

我的问题:

  1. 在我的Realm中只有一个PersonList实例的最佳做法是什么?正如您在下面的示例中所看到的,我首先检查是否存在,如果不存在,我会创建它。

  2. 使用领域通知时的最佳做法是什么?将它添加到我的Realm中的一个PersonList对象的list属性是否正确?

  3. 假设我想要一个单独的类来处理我的Realm中的写入事务。正如您在我的示例中所看到的,所有读/写事务都保存在UITableViewController类中 - 这被认为是混乱的吗?

  4. 下面的示例应该可以使用Xcode 8,Swift 3和Realm 1.1.0正常运行。

    我感谢任何反馈和想法!

    此致 埃里克

    import UIKit
    import RealmSwift
    
    class PersonList : Object {
        var list = List<Person>()
    }
    
    class Person : Object {
    
        dynamic var favorite = false
    
        dynamic var username : String?
        dynamic var firstName : String?
        dynamic var lastName : String?
    
        var fullName : String? {
            get {
    
                guard let firstName = firstName, let lastName = lastName else {
    
                    return nil
                }
    
                return "\(firstName) \(lastName)"
            }
        }
    }
    
    class ViewController: UITableViewController {
    
        var results : List<Person>?
        var notificationToken: NotificationToken? = nil
    
        func addPerson() {
    
            let alert = UIAlertController(title: "Add Person", message: "Please fill in the information", preferredStyle: .alert)
    
            alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
    
            alert.addAction(UIAlertAction(title: "Add", style: .default, handler: { alertAction in
    
                if let firstNameTextField = alert.textFields?[0], let lastNameTextField = alert.textFields?[1] {
    
                    self.savePerson(firstName: firstNameTextField.text, lastName: lastNameTextField.text)
                }
    
            }))
    
            alert.addTextField { (textField : UITextField!) -> Void in
                textField.placeholder = "First Name"
            }
            alert.addTextField { (textField : UITextField!) -> Void in
                textField.placeholder = "Last Name"
            }
    
            self.present(alert, animated: true, completion: nil)
    
        }
    
        func savePerson(firstName: String?, lastName: String?) {
    
            guard let firstName = firstName, !firstName.isEmpty else {
    
                let alert = UIAlertController(title: "Oops!", message: "First name missing!", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
    
                self.present(alert, animated: true, completion: nil)
    
                return
            }
    
            guard let lastName = lastName, !lastName.isEmpty else {
    
                let alert = UIAlertController(title: "Oops!", message: "Last name missing!", preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
    
                self.present(alert, animated: true, completion: nil)
    
                return
            }
    
            let realm = try! Realm()
    
            let newPerson = Person()
            newPerson.firstName = firstName
            newPerson.lastName = lastName
            newPerson.username = "\(Date())"
    
            do {
                try realm.write {
    
                    results?.append(newPerson)
    
                }
            }
            catch let error {
                print("Error: \(error)")
            }
        }
    
        func editButtonAction(_ sender: UIBarButtonItem) {
    
            if tableView.isEditing {
    
                tableView.setEditing(false, animated: true)
    
                sender.title = "Edit"
            }
            else {
    
                tableView.setEditing(true, animated: true)
    
                sender.title = "Done"
            }
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.addPerson))
    
            let editButton = UIBarButtonItem(title: "Edit", style: UIBarButtonItemStyle.plain, target: self, action: #selector(self.editButtonAction(_:)))
    
            self.navigationItem.rightBarButtonItems = [addButton, editButton]
    
            tableView.allowsSelectionDuringEditing = true
    
            let realm = try! Realm()
    
    
            //First, make sure a list exists in realm
            if realm.objects(PersonList.self).first?.list == nil {
    
                print("No existing list found in realm. Creating one.")
    
                let defaultList = PersonList()
    
                do {
                    try realm.write {
    
                        realm.add(defaultList)
    
                    }
                }
                catch let error { print("Error creating person list: \(error)") }
    
            }
    
            results = realm.objects(PersonList.self).first?.list
    
            // Observe Results Notifications
            notificationToken = results?.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
                guard let tableView = self?.tableView else { return }
                switch changes {
                case .initial:
                    // Results are now populated and can be accessed without blocking the UI
                    tableView.reloadData()
                    break
                case .update(_, let deletions, let insertions, let modifications):
    
                    // Query results have changed, so apply them to the UITableView
                    tableView.beginUpdates()
    
                    tableView.insertRows(at: insertions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
    
                    tableView.deleteRows(at: deletions.map { IndexPath(row: $0, section: 0) }, with: .automatic)
                    tableView.reloadRows(at: modifications.map { IndexPath(row: $0, section: 0) }, with: .automatic)
                    tableView.endUpdates()
                    break
                case .error(let error):
                    // An error occurred while opening the Realm file on the background worker thread
                    print("Error: \(error)")
                    break
                }
            }
        }
    
        override func numberOfSections(in tableView: UITableView) -> Int {
            return 1
        }
    
        override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
            return results?.count ?? 0
        }
    
    
        override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    
            let reuseIdentifier = "PersonTestCell"
    
            var cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier)
    
            if cell == nil {
    
                cell = UITableViewCell(style: UITableViewCellStyle.subtitle, reuseIdentifier: reuseIdentifier)
            }
    
            if let results = self.results {
    
                let person = results[indexPath.row]
    
                cell!.textLabel?.text = person.fullName ?? "Name not found."
    
                cell!.detailTextLabel?.text = person.username ?? "Username not found."
    
            }
    
    
            return cell!
    
        }
    
        override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    
            tableView.deselectRow(at: indexPath, animated: true)
        }
    
        // Override to support conditional editing of the table view.
        override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
    
            return true
        }
    
        // Override to support editing the table view.
        override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
    
            if editingStyle == .delete {
    
                if let results = self.results {
    
                    //Delete Person
                    let realm = try! Realm()
    
                    do {
                        try realm.write {
    
                            results.remove(objectAtIndex: indexPath.row)
    
                        }
                    }
                    catch let error {
                        print("Error: \(error)")
                    }
                }
    
            }
    
        }
    
        override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCellEditingStyle {
    
            return UITableViewCellEditingStyle.delete
        }
    
        // Override to support rearranging the table view.
        override func tableView(_ tableView: UITableView, moveRowAt fromIndexPath: IndexPath, to toIndexPath: IndexPath) {
    
            let realm = try! Realm()
    
            do {
                try realm.write {
    
                    results?.move(from: toIndexPath.row, to: fromIndexPath.row)
                    results?.move(from: fromIndexPath.row, to: toIndexPath.row)
                }
            }
            catch let error {
                print("Error: \(error)")
            }
    
    
        }
    
        // Override to support conditional rearranging of the table view.
        override func tableView(_ tableView: UITableView, canMoveRowAt indexPath: IndexPath) -> Bool {
            // Return false if you do not want the item to be re-orderable.
    
            return true
    
        }
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    
        deinit {
            notificationToken?.stop()
        }
    }
    

1 个答案:

答案 0 :(得分:3)

感谢您使用Realm!至于你的问题:

  

在我的Realm中只有一个PersonList实例的最佳做法是什么?正如您在下面的示例中所看到的,我首先检查是否存在,如果不存在,我会创建它。

有两种方法可以解决这种情况。我建议您为PersonList提供一个主键,并在使用PersonList时为该主键使用常量值。 Realm强制执行不变量,即只能存储具有给定主键值的一个对象。

因此:

  • 使用Realm.object(ofType:forPrimaryKey:)与常量主键获取现有PersonList
  • 如果该方法返回nil,请创建一个新的PersonList
  • 如果您想保存PersonList,请使用Realm.add(_:update:)update设置为true。如果该对象不存在,则会添加该对象;如果先前已添加该对象,则更新该数据库中的现有副本。
  

使用领域通知时的最佳做法是什么?将它添加到我的Realm中的一个PersonList对象的list属性是否正确?

是的,您对通知的使用似乎对我来说很合适。

  

假设我想要一个单独的类来处理我的Realm中的写入事务。正如您在我的示例中所看到的,所有读/写事务都保存在UITableViewController类中 - 这被认为是混乱的吗?

这更像是一个编码风格问题,而不是一个Realm问题,但它最终只是个人偏好的问题。如果您想避免使用所有逻辑创建“大规模视图控制器”,可以尝试以下几种方法:

  • 将您的视图控制器类拆分为主类和许多扩展,每个扩展都存在于自己的文件中。例如,您可能具有与Realm相关的方法的扩展,一个用于表视图委托/数据源方法等。请注意,存储的属性不能存在于扩展中,并且必须在主类声明中声明。

  • 您可以创建一个或多个帮助程序类来组织逻辑。例如,您有几种方法可以显示模态弹出窗口并写入Realm。那些不一定要生活在表视图类中,并且可以存在于PersonManager类中。该类将负责创建和呈现警报控制器以及与Realm进行交互。然后,如果PersonManager需要与表视图控制器进行通信,则可以使用基于闭包的回调或委托模式(尽管Realm通知会自动处理刷新表视图,甚至可能不需要!)。

希望有所帮助。