Каковы лучшие практики - упорядоченный список областей?

Я уже давно пользуюсь Realm и очень им доволен! Однако во время реализации я наткнулся на некоторые вопросы.

Я создал тестовый сценарий, чтобы попытаться указать, где мне нужны некоторые данные.

У меня есть область с базой данных объектов Person. Все они представлены в UITableView. Я хочу сохранить определенный порядок объектов, и пользователь должен иметь возможность изменять порядок объектов. Из того, что я прочитал, я должен использовать Список миров, чтобы достичь этого. Это снова означает, что у меня есть один класс с именем Person и один класс с именем PersonList. PersonList имеет только одно свойство: - список.

Приложение должно иметь только один объект PersonList в своей области Realm, но может иметь несколько объектов Person.

Мои вопросы:

  1. Как лучше всего иметь только один экземпляр PersonList в моем Realm? Как вы можете видеть в моем примере ниже, я сначала проверяю, существует ли он, если нет, я его создаю.

  2. Как лучше всего использовать уведомления Realm? Правильно ли добавлять его в свойство списка одного объекта PersonList в моем Realm?

  3. Скажем, я хочу иметь отдельный класс, который обрабатывает транзакции записи в моем Realm. Как вы можете видеть в моем примере, все транзакции чтения / записи хранятся в классе UITableViewController - считается ли это беспорядочным?

Мой пример ниже должен нормально работать с 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()
    }
}

person fisher    schedule 26.09.2016    source источник


Ответы (1)


Спасибо за использование Realm! Что касается ваших вопросов:

Как лучше всего иметь только один экземпляр PersonList в моем Realm? Как вы можете видеть в моем примере ниже, я сначала проверяю, существует ли он, если нет, я его создаю.

Есть несколько способов справиться с этой ситуацией. Я рекомендую вам дать PersonList первичный ключ и использовать постоянное значение для этого первичного ключа всякий раз, когда вы работаете с PersonList. Realm обеспечивает инвариант, согласно которому может быть сохранен только один объект с заданным значением первичного ключа.

Как таковой:

  • Используйте Realm.object(ofType:forPrimaryKey:) с постоянным первичным ключом, чтобы получить существующий PersonList.
  • Если этот метод возвращает nil, создайте новый PersonList.
  • Каждый раз, когда вы хотите сохранить PersonList, используйте Realm.add(_:update:) с update, установленным на true. Это добавит объект, если он не существует, или обновит существующую копию в базе данных, если он был добавлен ранее.

Как лучше всего использовать уведомления Realm? Правильно ли добавлять его в свойство списка одного объекта PersonList в моем Realm?

Да, мне кажется уместным ваше использование уведомлений.

Скажем, я хочу иметь отдельный класс, который обрабатывает транзакции записи в моем Realm. Как вы можете видеть в моем примере, все транзакции чтения / записи хранятся в классе UITableViewController - считается ли это беспорядочным?

Это скорее вопрос стиля кодирования, чем вопрос области, но в конечном итоге это вопрос личных предпочтений. Если вы хотите избежать создания «контроллера массивного представления» со всей вашей логикой, вы можете попробовать следующее:

  • Разделите класс контроллера представления на основной класс и несколько расширений, каждое из которых находится в своем собственном файле. Например, у вас может быть расширение для методов, связанных с Realm, одно для методов делегата / источника данных табличного представления и т. Д. Обратите внимание, что сохраненные свойства не могут существовать в расширении и должны быть объявлены в объявлении основного класса.

  • Вы можете создать один или несколько вспомогательных классов для организации своей логики. Например, у вас есть несколько методов, которые представляют модальные всплывающие окна и записывают в Realm. Они не обязательно должны жить в классе табличного представления и могут жить в классе PersonManager. Этот класс будет отвечать за создание и представление контроллеров предупреждений и за взаимодействие с Realm. Затем вы могли бы использовать обратные вызовы на основе закрытия или шаблон делегата, если вашему PersonManager нужно было связаться с контроллером табличного представления (хотя с уведомлениями Realm, автоматически обрабатывающими обновление вашего табличного представления, это может даже не понадобиться!).

Надеюсь, это поможет.

person AustinZ    schedule 27.09.2016
comment
Спасибо за подробное объяснение! Это сделало для меня яснее. - person fisher; 27.09.2016