Skip to content

Commit b01ed7d

Browse files
Channel list performance improvements
1 parent 790c353 commit b01ed7d

File tree

5 files changed

+35
-17
lines changed

5 files changed

+35
-17
lines changed

Sources/StreamChatSwiftUI/ChatChannelList/ChannelHeaderLoader.swift

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ open class ChannelHeaderLoader: ObservableObject {
1717
/// Prevents image requests to be executed if they failed previously.
1818
private var failedImageLoads = Set<String>()
1919

20+
/// Batches loaded images for update, to improve performance.
21+
private var scheduledUpdate = false
22+
2023
/// Context provided utils.
2124
internal lazy var imageLoader = utils.imageLoader
2225
internal lazy var imageCDN = utils.imageCDN
@@ -29,7 +32,17 @@ open class ChannelHeaderLoader: ObservableObject {
2932
internal lazy var placeholder3 = images.userAvatarPlaceholder3
3033
internal lazy var placeholder4 = images.userAvatarPlaceholder4
3134

32-
@Published var loadedImages = [String: UIImage]()
35+
var loadedImages = [String: UIImage]() {
36+
willSet {
37+
if !scheduledUpdate {
38+
scheduledUpdate = true
39+
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { [weak self] in
40+
self?.objectWillChange.send()
41+
self?.scheduledUpdate = false
42+
}
43+
}
44+
}
45+
}
3346

3447
public init() {}
3548

@@ -38,7 +51,7 @@ open class ChannelHeaderLoader: ObservableObject {
3851
/// - Parameter channel: the provided channel.
3952
/// - Returns: the available image.
4053
public func image(for channel: ChatChannel) -> UIImage {
41-
if let image = loadedImages[channel.cid.id] {
54+
if let image = loadedImages[channel.cid.rawValue] {
4255
return image
4356
}
4457

@@ -78,6 +91,10 @@ open class ChannelHeaderLoader: ObservableObject {
7891
// MARK: - private
7992

8093
private func loadMergedAvatar(from channel: ChatChannel, urls: [URL]) {
94+
if failedImageLoads.contains(channel.cid.rawValue) {
95+
return
96+
}
97+
8198
imageLoader.loadImages(
8299
from: urls,
83100
placeholders: [],
@@ -87,9 +104,13 @@ open class ChannelHeaderLoader: ObservableObject {
87104
) { [weak self] images in
88105
guard let self = self else { return }
89106
DispatchQueue.global(qos: .userInteractive).async {
90-
let image = self.channelAvatarsMerger.createMergedAvatar(from: images) ?? self.placeholder2
107+
let image = self.channelAvatarsMerger.createMergedAvatar(from: images)
91108
DispatchQueue.main.async {
92-
self.loadedImages[channel.cid.id] = image
109+
if let image = image {
110+
self.loadedImages[channel.cid.rawValue] = image
111+
} else {
112+
self.failedImageLoads.insert(channel.cid.rawValue)
113+
}
93114
}
94115
}
95116
}
@@ -99,7 +120,7 @@ open class ChannelHeaderLoader: ObservableObject {
99120
for channel: ChatChannel,
100121
from url: URL
101122
) {
102-
if failedImageLoads.contains(channel.cid.id) {
123+
if failedImageLoads.contains(channel.cid.rawValue) {
103124
return
104125
}
105126

@@ -113,10 +134,10 @@ open class ChannelHeaderLoader: ObservableObject {
113134
switch result {
114135
case let .success(image):
115136
DispatchQueue.main.async {
116-
self.loadedImages[channel.cid.id] = image
137+
self.loadedImages[channel.cid.rawValue] = image
117138
}
118139
case let .failure(error):
119-
self.failedImageLoads.insert(channel.cid.id)
140+
self.failedImageLoads.insert(channel.cid.rawValue)
120141
log.error("error loading image: \(error.localizedDescription)")
121142
}
122143
}

Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelList.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ extension ChatChannel: Identifiable {
176176
}
177177

178178
public var id: String {
179-
"\(cid.id)-\(lastMessageAt ?? createdAt)-\(lastActiveMembersCount)-\(mutedString)-\(unreadCount.messages)-\(typingUsersString)-\(readUsersId)"
179+
"\(cid.id)-\(lastMessageAt ?? createdAt)-\(lastActiveMembersCount)-\(mutedString)-\(typingUsersString)-\(readUsersId)"
180180
}
181181

182182
private var readUsersId: String {

Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelListViewModel.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,15 @@ open class ChatChannelListViewModel: ObservableObject, ChatChannelListController
123123
///
124124
/// - Parameter index: the currently displayed index.
125125
public func checkForChannels(index: Int) {
126-
if index < (controller?.channels.count ?? 0) - 10 {
126+
if index < (controller?.channels.count ?? 0) - 15 {
127127
return
128128
}
129129

130130
if !loadingNextChannels {
131131
loadingNextChannels = true
132-
controller?.loadNextChannels { [weak self] _ in
132+
controller?.loadNextChannels(limit: 30) { [weak self] _ in
133133
guard let self = self else { return }
134134
self.loadingNextChannels = false
135-
self.updateChannels()
136135
}
137136
}
138137
}
@@ -194,10 +193,6 @@ open class ChatChannelListViewModel: ObservableObject, ChatChannelListController
194193

195194
// MARK: - ChatChannelListControllerDelegate
196195

197-
public func controllerWillChangeChannels(_ controller: ChatChannelListController) {
198-
handleChannelListChanges(controller)
199-
}
200-
201196
public func controller(
202197
_ controller: ChatChannelListController,
203198
didChangeChannels changes: [ListChange<ChatChannel>]

Sources/StreamChatSwiftUI/ChatChannelList/ChatChannelSwipeableListItem.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,9 @@ public struct ChatChannelSwipeableListItem<Factory: ViewFactory, ChannelListItem
151151
}
152152

153153
if horizontalTranslation != 0 {
154-
swipedChannelId = channel.id
154+
if swipedChannelId != channel.id {
155+
swipedChannelId = channel.id
156+
}
155157
offsetX = horizontalTranslation
156158
} else {
157159
offsetX = 0

StreamChatSwiftUITests/Tests/ChatChannelList/ChatChannelListViewModel_Tests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class ChatChannelListViewModel_Tests: StreamChatTestCase {
6060
func test_channelListVM_onChannelAppear_loadNextChannelsNotCalled() {
6161
// Given
6262
var channels = [ChatChannel]()
63-
for _ in 0..<15 {
63+
for _ in 0..<20 {
6464
channels.append(ChatChannel.mockDMChannel())
6565
}
6666
let channelListController = makeChannelListController(channels: channels)

0 commit comments

Comments
 (0)