Сбой UICollectionView при обновлении через изменение коллекции Realm - PullRequest
0 голосов
/ 05 июня 2019

Я использую Realm вместе с MessageKit framework для реализации чата в моем приложении.MessageKit использует UICollectionView внутри, и каждое сообщение чата помещается в отдельный раздел представления коллекции.

Моя модель сообщения чата выглядит следующим образом:

class ChatMessage: Object {

    @objc dynamic var id: Int = generateMessageId()
    @objc dynamic var timestamp = Date()
    @objc dynamic var senderId: Int = -1    
    @objc dynamic var text: String = ""

    var recipients = List<Recipient>()

    @objc dynamic var receivedByServer = false
    var readReceipts = List<Recipient>()
}

В моем контроллере представления у меня естьмассив сообщений чата, объявленных так:

var messages: Results<ChatMessage>!
// and in viewDidLoad()...
messages = conversation.messages.sorted(byKeyPath: "timestamp", ascending: true)

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

notificationToken = messages.observe { [weak self] (changes: RealmCollectionChange) in
    switch changes {
    case .initial:
        print("Messages collection has been populated...")
    case .update(_, let deletions, let insertions, let modifications):
        print("Messages collection has been updated...")
        self?.messagesCollectionView.performBatchUpdates( {
            if modifications.count > 0 {
                print("modifications count: \(modifications.count)")
                self?.messagesCollectionView.reloadSections(IndexSet(modifications))
            }
            if deletions.count > 0 {
                print("deletions count: \(deletions.count)")
                self?.messagesCollectionView.deleteSections(IndexSet(deletions))
            }
            if insertions.count > 0 {
                print("insertions count: \(insertions.count)")
                self?.messagesCollectionView.insertSections(IndexSet(insertions))
            }
        }, completion: { [ weak self] _ in
            print("Scroll to bottom of UICollectionView...")
            self?.messagesCollectionView.scrollToBottom(animated: true)
        })
    case .error(let error):
        print("Error occurred while observing messages: \(error)")
    }
}

Первоначально этот код работал нормально, но затемЯ начал видеть проблему, которая, кажется, связана с множественными обновлениями, происходящими очень быстро.Например, когда я отправляю сообщение чата, сервер подтверждает, что сообщение было получено, что приведет к изменению объекта сообщения чата.Когда получатель читает сообщение, это также приведет к обновлению объекта сообщения чата.В некоторых случаях эти два события происходят вместе очень быстро, и я считаю, что мой код, который вызывает messagesCollectionView.reloadSections (IndexSet (updates)), будет выполнен дважды.Кажется, что второй вызов действительно происходит, пока первый вызов еще обрабатывается, и это приводит к сбою.Или, по крайней мере, я считаю, что именно это и является причиной проблемы.

Вот что я получаю от XCode, когда происходит сбой:

[CollectionView] An attempt to prepare a layout while a prepareLayout call was already in progress (i.e. reentrant call) has been ignored. Please file a bug. UICollectionView instance is (<MessageKit.MessagesCollectionView: 0x10c01b400; baseClass = UICollectionView; frame = (0 0; 414 896); clipsToBounds = YES; gestureRecognizers = <NSArray: 0x2830aa670>; layer = <CALayer: 0x283ea44a0>; contentOffset: {0, 137}; contentSize: {414, 604}; adjustedContentInset: {88, 0, 429, 0}> collection view layout: <MessageKit.MessagesCollectionViewFlowLayout: 0x10b7069d0>)
[CollectionView] An attempt to update layout information was detected while already in the process of computing the layout (i.e. reentrant call). This will result in unexpected behaviour or a crash. This may happen if a layout pass is triggered while calling out to a delegate. UICollectionViewFlowLayout instance is (<MessageKit.MessagesCollectionViewFlowLayout: 0x10b7069d0>)
*** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayM objectAtIndexedSubscript:]: index 0 beyond bounds for empty array'

Это правильный способ обновления UICollectionView, когдаиспользование коллекции Realm в качестве источника данных и наблюдение за этой коллекцией на предмет изменений или я все это делаю неправильно?Кроме того, я обновляю разделы (вместо элементов), поскольку MessageKit по умолчанию помещает каждое сообщение чата в отдельный раздел.

...