Realm をしばらく使用していますが、とても満足しています! ただし、実装中にいくつかの質問に出くわしました。
入力が必要な場所を指摘するために、テスト シナリオを作成しました。
Person オブジェクトのデータベースを持つレルムがあります。これらはすべて UITableView に表示されます。オブジェクトの特定の順序を維持したいのですが、ユーザーはオブジェクトの順序を変更できる必要があります。私が読んだことから、これを達成するにはRealms 'List'を使用する必要があります。これは、Person という名前のクラスが 1 つと、PersonList という名前のクラスが 1 つあることを意味します。PersonList には 1 つのプロパティしかありません: - リスト。
アプリの Realm には PersonList のオブジェクトを 1 つだけ持つ必要がありますが、Person のオブジェクトを複数持つことができます。
私の質問:
Realm に PersonList のインスタンスを 1 つだけ持つベスト プラクティスは何ですか? 以下の例でわかるように、最初に存在するかどうかを確認し、存在しない場合は作成します。
Realm Notifications の使用に関するベスト プラクティスは何ですか。Realm 内の 1 つの PersonList オブジェクトの list プロパティに追加するのは正しいですか?
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()
}
}