Ios 向领域结果数组添加通知

Ios 向领域结果数组添加通知,ios,swift,notifications,tableview,realm,Ios,Swift,Notifications,Tableview,Realm,刚开始在iOS项目中使用Realm,我已经查看了示例和文档,但似乎不知道如何获得Realm结果数组的细粒度通知 例如,如果它只是一个结果对象,则可以执行此操作 // Observe Results Notifications notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in guard let tableView = self?.tableVie

刚开始在iOS项目中使用Realm,我已经查看了示例和文档,但似乎不知道如何获得Realm结果数组的细粒度通知

例如,如果它只是一个结果对象,则可以执行此操作

// 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
    fatalError("\(error)")
    break
  }
}
这对于没有分区的普通tableview来说效果很好,因为它只是将新单元格插入到分区0中

但是,看看(tableview和一些部分)示例,它们只是向领域对象本身添加了一个通知块。通知您任何更改,而不是特定的插入/删除等

像这样:

    // Set realm notification block
    notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
        self.tableView.reloadData()
    }
虽然这是可行的,但它并不是最好的解决方案,因为你会失去iOS免费提供的漂亮动画

我的问题是,如何将细粒度通知添加到结果数组中

var objectsBySection=[Results]()

我曾经考虑过在数组中循环并向每个结果对象添加一个通知块,但是由于可以向这个2D数组添加新的结果对象,所以这似乎不是一个好的解决方案

有没有人有过使用具有动态增长的节/单元格数量的分区表视图的领域的经验

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

多亏了@bogdanf,我找到了解决这个问题的办法。我在这里发布我的解决方案,因为它与@bogdanf建议的不完全相同,但他的回答让我找到了解决方案,所以就在这里

首先,在我的应用程序中,部分并不完全是无限的。用户在添加对象时会添加对象,但数量有限。也就是说,我可以创建一个数组并将我的实际领域对象附加到它们上,从而允许我通过它们相应的部分对对象进行分组

因此,这是第一步,我创建了一个包含我所有分区的数组,在我的应用程序中,总共有48个分区,所以在添加通知时运行时不会太糟糕

创建节数组后,我在realm中查询与节对应的正确对象,如下所示:

func initObjectsBySection() {
    print("Initializing")
    for (index, section) in sections.enumerated() {
        let unsorted = realm.objects(Object.self).filter("section == %@" , section)
        let sorted = unsorted.sorted(byProperty: "year", ascending: false)
        objectsBySection.append(sorted)
        registerNotification(for: objectsBySection[index], in: index)
    }
}
bogdanf建议的注册通知是
RegisterNotification
,但有一些改动:

func registerNotification(for objects: Results<Object>, in section: Int) {
    let token = objects.addNotificationBlock { [unowned self] (changes: RealmCollectionChange) in
        switch changes {
        case .initial:
            // Results are now populated and can be accessed without blocking the UI
            self.tableView.reloadData()
            break
        case .update:
            // Query results have changed, so apply them to the UITableView
            self.tableView.beginUpdates()
            self.tableView.reloadSections(IndexSet.init(integer: section), with: .automatic)
            self.tableView.endUpdates()
            break
        case .error(let error):
            // An error occurred while opening the Realm file on the background worker thread
            fatalError("\(error)")
            break
        }

    }
    notifTokens.append(token)
}

基本上就是这样。

我会这样做,按照你的建议在数组中循环:

var objectsBySection = [Results<DemoObject>]()

// Fill the objectsBySection array like in your example
...


for (index, objects) in objectsBySection.enumerated() {
    registerNotifications(for: objects, in: index)
}
我们假设
var notificationTokens
是在类级别定义的

现在,您提到可以随时添加新的部分,所以我们也来处理这个问题。因此,我们添加普通的非细粒度通知块,并检查是否添加了新的部分

notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
    // Let's see what the section list looks like now
    let sections = Set(realm.objects(DemoObject.self).value(forKey: "sectionTitle") as! [String])

    if !Set(sectionTitles).isSuperset(of: sections) {
        sectionTitles = Array(sections)

        self.tableView.reloadData()
    }
}
因此,在我过于简单的方法中,只有添加了新的部分,它才会重新加载所有内容。如果您想从漂亮的插入动画中获益,您可以检查添加了哪些部分,将它们逐个插入表中,然后向其中添加新对象


注意:我检查是否添加了节的方法非常密集,基本上它会遍历数据库中的所有对象,因此您可能希望在应用程序中使用真实的负载来检查它。不幸的是,在Realm允许
distinct
group by
查询之前,这是我能想象的解决此问题的唯一方法。

如何知道每个部分中包含哪些对象(换句话说,objectsBySection是如何创建的)?如果条件可以表示为领域查询,那么解决方案非常简单。@bogdanf在我链接的示例(来自领域)中,他们使用查询确定哪些对象适用于哪些部分,因此这是一个领域查询。是否尝试将对象一次添加到几个部分?我认为这会使你的应用程序崩溃,因为几乎没有tableView更新同时在主线程上运行。这看起来非常。。。hackish@SAHM是的,但对我的申请有效。遗憾的是,realm不像CoreData那样支持2D数组。不过,我后来改用委派而不是域通知来处理同一个应用程序。我在实际的应用程序中使用了一个扩展来删除相同的对象,并使它们唯一。谢谢你写的很棒的文章,当我回家的时候,我会给这个机会并更新答案!所以这个解决方案并不适合我,但我把你作为答案,因为这个很棒的答案让我想出了一个解决方案。用我使用的解决方案更新了我的问题。我认为@distinctUnionOfObjects适用于领域,但我不知道如何在Swift中使用它。你会如何改变你关于章节的回答?当多个部分同时尝试更新时,您将收到“无效更新”错误。
func registerNotifications(for results: Results<DemoObject>, in section:Int) {

    let notificationToken = results.addNotificationBlock { [weak self] (changes: RealmCollectionChange) in
        guard let tableView = self?.tableView else { return }

        switch changes {
        ...
        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: section) }), with: .automatic)
            tableView.deleteRows(at: deletions.map({ IndexPath(row: $0, section: section)}), with: .automatic)
            tableView.reloadRows(at: modifications.map({ IndexPath(row: $0, section: section) }), with: .automatic)
            tableView.endUpdates()
            break
        ...
        }
    }
    notificationTokens.append(notificationToken)
}
notificationToken = realm.addNotificationBlock { [unowned self] note, realm in
    // Let's see what the section list looks like now
    let sections = Set(realm.objects(DemoObject.self).value(forKey: "sectionTitle") as! [String])

    if !Set(sectionTitles).isSuperset(of: sections) {
        sectionTitles = Array(sections)

        self.tableView.reloadData()
    }
}