Я использую 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 по умолчанию помещает каждое сообщение чата в отдельный раздел.