4

Realm をしばらく使用していますが、とても満足しています! ただし、実装中にいくつかの質問に出くわしました。

入力が必要な場所を指摘するために、テスト シナリオを作成しました。

Person オブジェクトのデータベースを持つレルムがあります。これらはすべて UITableView に表示されます。オブジェクトの特定の順序を維持したいのですが、ユーザーはオブジェクトの順序を変更できる必要があります。私が読んだことから、これを達成するにはRealms 'List'を使用する必要があります。これは、Person という名前のクラスが 1 つと、PersonList という名前のクラスが 1 つあることを意味します。PersonList には 1 つのプロパティしかありません: - リスト。

アプリの Realm には PersonList のオブジェクトを 1 つだけ持つ必要がありますが、Person のオブジェクトを複数持つことができます。

私の質問:

  1. Realm に PersonList のインスタンスを 1 つだけ持つベスト プラクティスは何ですか? 以下の例でわかるように、最初に存在するかどうかを確認し、存在しない場合は作成します。

  2. Realm Notifications の使用に関するベスト プラクティスは何ですか。Realm 内の 1 つの PersonList オブジェクトの list プロパティに追加するのは正しいですか?

  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()
    }
}
4

1 に答える 1

4

Realm をご利用いただきありがとうございます。あなたの質問について:

Realm に PersonList のインスタンスを 1 つだけ持つベスト プラクティスは何ですか? 以下の例でわかるように、最初に存在するかどうかを確認し、存在しない場合は作成します。

この状況に対処するには、いくつかの方法があります。私がお勧めするのはPersonList、主キーを指定し、 を使用するときは常にその主キーに定数値を使用することですPersonList。Realm は、特定の主キー値を持つオブジェクトを 1 つだけ格納できるという不変条件を適用します。

そのような:

  • 一定の主キーとともに使用Realm.object(ofType:forPrimaryKey:)して、既存の を取得しますPersonList
  • そのメソッドが を返す場合はnil、新しい を作成しますPersonList
  • を保存したいときはいつでも、 を に設定してPersonList使用します。これにより、オブジェクトが存在しない場合は追加され、以前に追加された場合はデータベース内の既存のコピーが更新されます。Realm.add(_:update:)updatetrue

Realm Notifications の使用に関するベスト プラクティスは何ですか。Realm 内の 1 つの PersonList オブジェクトの list プロパティに追加するのは正しいですか?

はい、あなたの通知の使用は適切だと思います。

Realm で書き込みトランザクションを処理する別のクラスが必要だとしましょう。私の例でわかるように、すべての読み取り/書き込みトランザクションは UITableViewController クラスに保持されています - これは面倒だと思いますか?

これは Realm に関する質問というよりコーディング スタイルに関する質問ですが、最終的には個人的な好みの問題です。すべてのロジックで「大規模なビュー コントローラー」を作成することを避けたい場合は、いくつかの方法を試すことができます。

  • ビュー コントローラー クラスをメイン クラスといくつかの拡張機能に分割し、それぞれが独自のファイルに存在するようにします。たとえば、Realm 関連のメソッド用の拡張機能、テーブル ビュー デリゲート/データ ソース メソッド用の拡張機能などがあります。保存されたプロパティは拡張機能に存在できず、プライマリ クラス宣言で宣言する必要があることに注意してください。

  • ロジックを整理するために、1 つ以上のヘルパー クラスを作成できます。たとえば、モーダル ポップアップを表示して Realm に書き込むメソッドがいくつかあります。それらは必ずしもテーブルビュークラスに存在する必要はなく、PersonManagerクラスに存在する可能性があります。このクラスは、アラート コントローラの作成と提示、およびレルムとの対話を担当します。その後、テーブル ビュー コントローラーと通信する必要がある場合は、クロージャー ベースのコールバックまたはデリゲート パターンを使用できますPersonManager(ただし、Realm 通知がテーブル ビューの更新を自動的に処理するため、必要ではない場合もあります)。

それが役立つことを願っています。

于 2016-09-27T00:48:39.883 に答える