Skip to content

Commit 9296308

Browse files
WIP Performance improvements
1 parent b8fb1b1 commit 9296308

30 files changed

+328
-112
lines changed

DemoAppSwiftUI/AppDelegate.swift

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@
22
// Copyright © 2022 Stream.io Inc. All rights reserved.
33
//
44

5+
import FPSCounter
56
import StreamChat
67
import StreamChatSwiftUI
78
import SwiftUI
89
import UIKit
910

10-
class AppDelegate: NSObject, UIApplicationDelegate {
11+
class AppDelegate: NSObject, UIApplicationDelegate, FPSCounterDelegate {
1112

1213
var streamChat: StreamChat?
1314

15+
var fpsCounter = FPSCounter()
16+
1417
var chatClient: ChatClient = {
1518
var config = ChatClientConfig(apiKey: .init(apiKeyString))
1619
// config.isLocalStorageEnabled = true
@@ -63,6 +66,8 @@ class AppDelegate: NSObject, UIApplicationDelegate {
6366
}
6467
}
6568

69+
fpsCounter.delegate = self
70+
fpsCounter.startTracking()
6671
UNUserNotificationCenter.current().delegate = NotificationsHandler.shared
6772

6873
return true
@@ -95,6 +100,10 @@ class AppDelegate: NSObject, UIApplicationDelegate {
95100
UserDefaults(suiteName: applicationGroupIdentifier)?.set(currentUserId, forKey: currentUserIdRegisteredForPush)
96101
}
97102
}
103+
104+
func fpsCounter(_ counter: FPSCounter, didUpdateFramesPerSecond fps: Int) {
105+
print("======== current fps = \(fps) =========")
106+
}
98107
}
99108

100109
extension UIColor {

DemoAppSwiftUI/CreateGroupView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ struct SelectedUserGroupView: View {
8787
var body: some View {
8888
VStack {
8989
MessageAvatarView(
90-
author: user,
90+
avatarURL: user.imageURL,
9191
size: CGSize(width: avatarSize, height: avatarSize)
9292
)
9393

DemoAppSwiftUI/NewChatView.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ struct SelectedUserView: View {
113113
var body: some View {
114114
HStack {
115115
MessageAvatarView(
116-
author: user,
116+
avatarURL: user.imageURL,
117117
size: CGSize(width: 20, height: 20)
118118
)
119119

@@ -200,7 +200,7 @@ struct ChatUserView: View {
200200
var body: some View {
201201
HStack {
202202
LazyView(
203-
MessageAvatarView(author: user)
203+
MessageAvatarView(avatarURL: user.imageURL)
204204
)
205205

206206
VStack(alignment: .leading, spacing: 4) {

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelDataSource.swift

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ protocol MessagesDataSource: AnyObject {
1414
/// - messages, the collection of updated messages.
1515
func dataSource(
1616
channelDataSource: ChannelDataSource,
17-
didUpdateMessages messages: LazyCachedMapCollection<ChatMessage>
17+
didUpdateMessages messages: LazyCachedMapCollection<ChatMessage>,
18+
changes: [ListChange<ChatMessage>]
1819
)
1920

2021
/// Called when the channel is updated.
@@ -70,7 +71,8 @@ class ChatChannelDataSource: ChannelDataSource, ChatChannelControllerDelegate {
7071
) {
7172
delegate?.dataSource(
7273
channelDataSource: self,
73-
didUpdateMessages: channelController.messages
74+
didUpdateMessages: channelController.messages,
75+
changes: changes
7476
)
7577
}
7678

@@ -117,22 +119,34 @@ class MessageThreadDataSource: ChannelDataSource, ChatMessageControllerDelegate
117119
self.messageController.delegate = self
118120
self.messageController.loadPreviousReplies { [weak self] _ in
119121
guard let self = self else { return }
120-
self.delegate?.dataSource(channelDataSource: self, didUpdateMessages: self.messages)
122+
self.delegate?.dataSource(
123+
channelDataSource: self,
124+
didUpdateMessages: self.messages,
125+
changes: []
126+
)
121127
}
122128
}
123129

124130
func messageController(
125131
_ controller: ChatMessageController,
126132
didChangeReplies changes: [ListChange<ChatMessage>]
127133
) {
128-
delegate?.dataSource(channelDataSource: self, didUpdateMessages: controller.replies)
134+
delegate?.dataSource(
135+
channelDataSource: self,
136+
didUpdateMessages: controller.replies,
137+
changes: changes
138+
)
129139
}
130140

131141
func messageController(
132142
_ controller: ChatMessageController,
133143
didChangeMessage change: EntityChange<ChatMessage>
134144
) {
135-
delegate?.dataSource(channelDataSource: self, didUpdateMessages: controller.replies)
145+
delegate?.dataSource(
146+
channelDataSource: self,
147+
didUpdateMessages: controller.replies,
148+
changes: []
149+
)
136150
}
137151

138152
func loadPreviousMessages(

Sources/StreamChatSwiftUI/ChatChannel/ChatChannelViewModel.swift

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,11 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
1818
private var cancellables = Set<AnyCancellable>()
1919
private var lastRefreshThreshold = 200
2020
private let refreshThreshold = 200
21-
private var timer: Timer?
21+
private var timer: DispatchSourceTimer?
22+
private let queue = DispatchQueue(label: "io.getstream.background.timer")
2223
private var currentDate: Date? {
2324
didSet {
24-
guard showScrollToLatestButton == true, let currentDate = currentDate else {
25-
currentDateString = nil
26-
return
27-
}
28-
currentDateString = messageListDateOverlay.string(from: currentDate)
25+
handleDateChange()
2926
}
3027
}
3128

@@ -39,9 +36,11 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
3936
}()
4037

4138
private lazy var messagesDateFormatter = utils.dateFormatter
39+
private lazy var messageCachingUtils = utils.messageCachingUtils
4240

43-
@Atomic private var loadingPreviousMessages: Bool = false
44-
@Atomic private var lastMessageRead: String?
41+
private var loadingPreviousMessages: Bool = false
42+
private var lastMessageRead: String?
43+
private var messageChanges = [ListChange<ChatMessage>]()
4544

4645
public var channelController: ChatChannelController
4746
public var messageController: ChatMessageController?
@@ -54,27 +53,7 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
5453
@Published public var currentDateString: String?
5554
@Published public var messages = LazyCachedMapCollection<ChatMessage>() {
5655
didSet {
57-
var temp = [String: [String]]()
58-
for (index, message) in messages.enumerated() {
59-
let dateString = messagesDateFormatter.string(from: message.createdAt)
60-
let prefix = message.author.id
61-
let key = "\(prefix)-\(dateString)"
62-
if temp[key] == nil {
63-
temp[key] = [message.id]
64-
} else {
65-
// check if the previous message is not sent by the same user.
66-
let previousIndex = index - 1
67-
if previousIndex >= 0 {
68-
let previous = messages[previousIndex]
69-
70-
let shouldAddKey = message.author.id != previous.author.id
71-
if shouldAddKey {
72-
temp[key]?.append(message.id)
73-
}
74-
}
75-
}
76-
}
77-
messagesGroupingInfo = temp
56+
groupMessages()
7857
}
7958
}
8059

@@ -155,15 +134,18 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
155134
public func handleMessageAppear(index: Int) {
156135
let message = messages[index]
157136
checkForNewMessages(index: index)
158-
save(lastDate: message.createdAt)
137+
// if utils.messageListConfig.dateIndicatorPlacement == .overlay {
138+
// save(lastDate: message.createdAt)
139+
// }
159140
if index == 0 {
160141
maybeSendReadEvent(for: message)
161142
}
162143
}
163144

164145
func dataSource(
165146
channelDataSource: ChannelDataSource,
166-
didUpdateMessages messages: LazyCachedMapCollection<ChatMessage>
147+
didUpdateMessages messages: LazyCachedMapCollection<ChatMessage>,
148+
changes: [ListChange<ChatMessage>]
167149
) {
168150
if !isActive {
169151
return
@@ -193,7 +175,6 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
193175
didUpdateChannel channel: EntityChange<ChatChannel>,
194176
channelController: ChatChannelController
195177
) {
196-
messages = channelController.messages
197178
checkHeaderType()
198179
}
199180

@@ -228,14 +209,15 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
228209
// MARK: - private
229210

230211
private func checkForNewMessages(index: Int) {
231-
if index < channelDataSource.messages.count - 20 {
212+
if index < channelDataSource.messages.count - 25 {
232213
return
233214
}
234215

235-
if _loadingPreviousMessages.compareAndSwap(old: false, new: true) {
216+
if !loadingPreviousMessages {
217+
loadingPreviousMessages = true
236218
channelDataSource.loadPreviousMessages(
237219
before: nil,
238-
limit: refreshThreshold,
220+
limit: 50,
239221
completion: { [weak self] _ in
240222
guard let self = self else { return }
241223
self.loadingPreviousMessages = false
@@ -246,14 +228,13 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
246228

247229
private func save(lastDate: Date) {
248230
currentDate = lastDate
249-
timer?.invalidate()
250-
timer = Timer.scheduledTimer(
251-
withTimeInterval: 0.5,
252-
repeats: false,
253-
block: { [weak self] _ in
254-
self?.currentDate = nil
255-
}
256-
)
231+
timer?.cancel()
232+
timer = DispatchSource.makeTimerSource(queue: queue)
233+
timer?.schedule(deadline: .now() + 0.5, repeating: .never)
234+
timer?.setEventHandler(handler: { [weak self] in
235+
self?.currentDate = nil
236+
})
237+
timer?.resume()
257238
}
258239

259240
private func maybeSendReadEvent(for message: ChatMessage) {
@@ -296,20 +277,57 @@ open class ChatChannelViewModel: ObservableObject, MessagesDataSource {
296277
}
297278
}
298279
}
280+
281+
private func groupMessages() {
282+
var temp = [String: [String]]()
283+
for (index, message) in messages.enumerated() {
284+
let dateString = messagesDateFormatter.string(from: message.createdAt)
285+
let prefix = messageCachingUtils.authorId(for: message)
286+
let key = "\(prefix)-\(dateString)"
287+
if temp[key] == nil {
288+
temp[key] = [message.id]
289+
} else {
290+
// check if the previous message is not sent by the same user.
291+
let previousIndex = index - 1
292+
if previousIndex >= 0 {
293+
let previous = messages[previousIndex]
294+
let previousAuthorId = messageCachingUtils.authorId(for: previous)
295+
let shouldAddKey = prefix != previousAuthorId
296+
if shouldAddKey {
297+
temp[key]?.append(message.id)
298+
}
299+
}
300+
}
301+
}
302+
303+
messagesGroupingInfo = temp
304+
}
305+
306+
private func handleDateChange() {
307+
guard showScrollToLatestButton == true, let currentDate = currentDate else {
308+
DispatchQueue.main.async { [weak self] in
309+
self?.currentDateString = nil
310+
}
311+
return
312+
}
313+
314+
DispatchQueue.global(qos: .userInitiated).async { [unowned self] in
315+
let dateString = messageListDateOverlay.string(from: currentDate)
316+
DispatchQueue.main.async { [unowned self] in
317+
if currentDateString != dateString {
318+
currentDateString = dateString
319+
}
320+
}
321+
}
322+
}
299323
}
300324

301325
extension ChatMessage: Identifiable {
302326
var messageId: String {
303-
let statesId = uploadingStatesId
304-
305-
if statesId.isEmpty {
306-
if !reactionScores.isEmpty {
307-
return baseId + reactionScoresId
308-
} else {
309-
return baseId
310-
}
327+
var statesId = "empty"
328+
if localState != nil {
329+
statesId = uploadingStatesId
311330
}
312-
313331
return baseId + statesId + reactionScoresId + repliesCountId + "\(updatedAt)" + pinStateId
314332
}
315333

@@ -347,6 +365,9 @@ extension ChatMessage: Identifiable {
347365

348366
var reactionScoresId: String {
349367
var output = ""
368+
if reactionScores.isEmpty {
369+
return output
370+
}
350371
let sorted = reactionScores.keys.sorted { type1, type2 in
351372
type1.id > type2.id
352373
}

Sources/StreamChatSwiftUI/ChatChannel/Composer/Suggestions/Mentions/MentionUsersView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ public struct MentionUserView: View {
5555
public var body: some View {
5656
HStack {
5757
MessageAvatarView(
58-
author: user,
58+
avatarURL: user.imageURL,
5959
showOnlineIndicator: true
6060
)
6161
Text(user.name ?? user.id)

Sources/StreamChatSwiftUI/ChatChannel/MessageList/FileAttachmentView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ import StreamChat
66
import SwiftUI
77

88
public struct FileAttachmentsContainer: View {
9+
10+
@Injected(\.utils) private var utils
11+
912
var message: ChatMessage
1013
var width: CGFloat
1114
var isFirst: Bool
1215
@Binding var scrolledId: String?
1316

1417
public var body: some View {
1518
VStack(alignment: message.alignmentInBubble) {
16-
if let quotedMessage = message.quotedMessage {
19+
if let quotedMessage = utils.messageCachingUtils.quotedMessage(for: message) {
1720
QuotedMessageViewContainer(
1821
quotedMessage: quotedMessage,
1922
fillAvailableSpace: !message.attachmentCounts.isEmpty,

Sources/StreamChatSwiftUI/ChatChannel/MessageList/GiphyAttachmentView.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ public struct GiphyAttachmentView<Factory: ViewFactory>: View {
1313
@Injected(\.chatClient) private var chatClient
1414
@Injected(\.colors) private var colors
1515
@Injected(\.fonts) private var fonts
16+
@Injected(\.utils) private var utils
1617

1718
let factory: Factory
1819
let message: ChatMessage
@@ -25,7 +26,7 @@ public struct GiphyAttachmentView<Factory: ViewFactory>: View {
2526
alignment: message.alignmentInBubble,
2627
spacing: 0
2728
) {
28-
if let quotedMessage = message.quotedMessage {
29+
if let quotedMessage = utils.messageCachingUtils.quotedMessage(for: message) {
2930
QuotedMessageViewContainer(
3031
quotedMessage: quotedMessage,
3132
fillAvailableSpace: !message.attachmentCounts.isEmpty,
@@ -106,7 +107,7 @@ struct LazyGiphyView: View {
106107
}
107108
}
108109
}
109-
.onDisappear(.reset)
110+
.onDisappear(.cancel)
110111
.processors([ImageProcessors.Resize(width: width)])
111112
.priority(.high)
112113
.aspectRatio(contentMode: .fit)

0 commit comments

Comments
 (0)