我一直在使用Realm一段时间,我对它非常满意! 但是,我在实施过程中偶然发现了一些问题。
我做了一个测试场景,试图指出我需要输入的地方。
我有一个包含Person对象数据库的领域。这些都在UITableView中呈现。我想保留对象的特定顺序,用户应该能够重新排序对象。从我所读到的,我必须使用Realms'List'来实现这一目标。这再次意味着我有一个名为Person的类和一个名为PersonList的类。 PersonList只有一个属性: - list。
app应该只在其Realm中有一个PersonList对象,但可能有多个Person对象。
我的问题:
在我的Realm中只有一个PersonList实例的最佳做法是什么?正如您在下面的示例中所看到的,我首先检查是否存在,如果不存在,我会创建它。
使用领域通知时的最佳做法是什么?将它添加到我的Realm中的一个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()
}
}
答案 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通知会自动处理刷新表视图,甚至可能不需要!)。
希望有所帮助。