Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve channel list performance by enabling reconfigure cells #3186

Merged
merged 1 commit into from
May 10, 2024

Conversation

laevandus
Copy link
Contributor

馃敆 Issue Links

Resolves ios-issues-tracking/790

馃幆 Goal

Enable cell reconfiguring and guard against reentrancy which led to an assertion in Apple's code/

馃摑 Summary

  • Enable cell reconfiguring in channel list which makes it much faster
  • Guard against reentrancy which can happen if reconfigure cells call ends up triggering Core Data store change which is then picked up by database observers (and leads to calling reloadChannels again)

馃И Manual Testing Notes

Exploratory testing:
Open channel list and trigger channel list changes (e.g. new messages arriving)

鈽戯笍 Contributor Checklist

  • I have signed the Stream CLA (required)
  • This change follows zero 鈿狅笍 policy (required)
  • This change should be manually QAed
  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (docusaurus, tutorial, CMS)

@laevandus laevandus added the 馃帹聽SDK: StreamChatUI Tasks related to the StreamChatUI SDK label May 8, 2024
@laevandus laevandus requested a review from a team as a code owner May 8, 2024 13:52
Comment on lines 256 to 272
public func reloadChannels() {
if isApplyingChangesets {
DispatchQueue.main.async {
self.reloadChannels()
}
return
}

isApplyingChangesets = true
let previousChannels = channels
let newChannels = Array(controller.channels)
let stagedChangeset = StagedChangeset(source: previousChannels, target: newChannels)
collectionView.reload(using: stagedChangeset, reconfigure: { _ in false }) { [weak self] newChannels in
collectionView.reload(using: stagedChangeset, reconfigure: { _ in true }) { [weak self] newChannels in
self?.channels = newChannels
}
isApplyingChangesets = false
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue was when reload called reconfigure cell and that in turn triggered Core Data store change which led to calling reloadChannels again. Then Apple throws an assert.
Pretty hard to unit-test this case.
I am thinking about using a boolean here which just reschedules reload channels if re-entrancy is detected.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not worth to to test it 馃

@Stream-iOS-Bot
Copy link
Collaborator

StreamChat XCMetrics

target metric benchmark branch performance status
MessageList Hitches total duration 10 ms 10.01 ms -0.1% 馃斀 馃煛
Duration 2.6 s 2.55 s 1.92% 馃敿 馃煝
Hitch time ratio 4 ms per s 3.92 ms per s 2.0% 馃敿 馃煝
Frame rate 79 fps 78.13 fps 1.1% 馃敿 馃煝
Number of hitches 1 0.8 20.0% 馃敿 馃煝
ChannelList Hitches total duration 12.5 ms 5.01 ms 59.92% 馃敿 馃煝
Duration 2.6 s 2.12 s 18.46% 馃敿 馃煝
Hitch time ratio 5 ms per s 2.43 ms per s 51.4% 馃敿 馃煝
Frame rate 76 fps 79.67 fps -4.83% 馃斀 馃煛
Number of hitches 1.2 0.6 50.0% 馃敿 馃煝

self?.channels = newChannels
}
isApplyingChangesets = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be set inside the reload completion block? 馃

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think that

Copy link
Contributor Author

@laevandus laevandus May 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually not a completion but it is the setData closure which is called as a first thing in performBatchUpdates (too early). This can be called multiple times although for us it seems to be called once (haven't dived into when exactly multiple changesets are generated).

 func reload<C>(
        using stagedChangeset: StagedChangeset<C>,
        reconfigure: ((IndexPath) -> Bool)? = nil,
        interrupt: ((Changeset<C>) -> Bool)? = nil,
        setData: (C) -> Void
    ) {

where setData is called after each changeset

for changeset in stagedChangeset {
            if let interrupt = interrupt, interrupt(changeset), let data = stagedChangeset.last?.data {
                setData(data)
                return reloadData()
            }
            
            performBatchUpdates({
                setData(changeset.data)
                
                if !changeset.sectionDeleted.isEmpty {
                    deleteSections(IndexSet(changeset.sectionDeleted))
                }
                // later we call reconfigureItems which can trigger re-entrancy

In summary, it is in a correct places. Note that performBatchUpdates's updates closure is not escaping and is called synchronously. Therefore, resetting the boolean is indeed at a correct place.

Flow:

  1. Set boolean to true
  2. Call reload
  3. Loop changesets
  4. Call setData
  5. Call reconfigureItems <-- boolean must be set false after calling reconfigure
  6. exit the reload function
  7. Set the boolean to false

@laevandus laevandus force-pushed the fix/enable-reconfigure-channel-cells branch from 121512c to a74a7ed Compare May 9, 2024 13:13
@laevandus laevandus force-pushed the fix/enable-reconfigure-channel-cells branch from a74a7ed to 442afd4 Compare May 10, 2024 06:44
@laevandus laevandus enabled auto-merge (squash) May 10, 2024 06:44
Copy link

sonarcloud bot commented May 10, 2024

@laevandus laevandus merged commit 16d9153 into develop May 10, 2024
15 checks passed
@laevandus laevandus deleted the fix/enable-reconfigure-channel-cells branch May 10, 2024 07:18
@laevandus laevandus mentioned this pull request May 13, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
馃帹聽SDK: StreamChatUI Tasks related to the StreamChatUI SDK
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants