From ae8009d040e6de6568f4d3377d541d3ad95fd862 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Fri, 20 Jan 2023 16:40:23 +0200 Subject: [PATCH] Various performance tweaks (#474) * Store and reuse room list placeholder avatars and last messages * Cache and reuse HomeScreenRooms * Reduce RoomSummaryProvider diff collection time * Promote more logs to info * Slighty tweak RustTracing to make it easier to configure * Move TimelineProvider and RoomTimelineController item processing to background queues * Prevent the timeline from stopping an ogoing decelerating scroll when starting backpaginating --- ElementX.xcodeproj/project.pbxproj | 41 +++++++++++++++ .../xcshareddata/swiftpm/Package.resolved | 9 ++++ .../Sources/Other/Logging/RustTracing.swift | 52 ++++++++++++------- .../Screens/HomeScreen/HomeScreenModels.swift | 13 +++-- .../HomeScreen/HomeScreenViewModel.swift | 38 +++++++++----- .../View/TimelineTableViewController.swift | 1 - .../RoomSummary/RoomSummaryProvider.swift | 8 +-- .../Timeline/RoomTimelineProvider.swift | 37 +++++++------ .../RoomTimelineController.swift | 14 ++--- ElementX/SupportingFiles/target.yml | 1 + IntegrationTests/SupportingFiles/target.yml | 1 + NSE/SupportingFiles/target.yml | 1 + UITests/SupportingFiles/target.yml | 1 + .../Sources/TracingConfigurationTests.swift | 40 +++++--------- project.yml | 3 ++ 15 files changed, 166 insertions(+), 94 deletions(-) diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0afa1d6adc..f6615f3c2e 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -27,6 +27,7 @@ 09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; }; 098CE03C6CC71A31F263FA33 /* ActivityCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA9D14D6F914324865C7DB9F /* ActivityCoordinator.swift */; }; 09AAF04B27732046C755D914 /* SoftLogoutViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32C5DAA1773F57653BF1C4F9 /* SoftLogoutViewModelTests.swift */; }; + 09BFDE37F0D0E586D26B17D7 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = A20EA00CCB9DBE0FFB17DD09 /* Collections */; }; 09C83DDDB07C28364F325209 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D7074991B3267B26D89B22 /* MockRoomTimelineController.swift */; }; 0AE0AB1952F186EB86719B4F /* HomeScreenRoomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED044D00F2176681CC02CD54 /* HomeScreenRoomCell.swift */; }; 0BEFE400B4802FE8C9DB39B3 /* FilePreviewViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62BDF0FF4F59AF6EA858B70B /* FilePreviewViewModel.swift */; }; @@ -136,6 +137,7 @@ 3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; }; 3ED2725734568F6B8CC87544 /* AttributedStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2A5C6FBF97B6EED3D4FA5EFF /* AttributedStringBuilder.swift */; }; 3F2148F11164C7C5609984EB /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; }; + 3F327A62D233933F54F0F33A /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = BA93CD75CCE486660C9040BD /* Collections */; }; 3F70E237CE4C3FAB02FC227F /* NotificationConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C830A64609CBD152F06E0457 /* NotificationConstants.swift */; }; 407DCE030E0F9B7C9861D38A /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; }; 414F50CFCFEEE2611127DCFB /* RestorationToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3558A15CFB934F9229301527 /* RestorationToken.swift */; }; @@ -415,6 +417,7 @@ D59F046B15AA8E971053C1A6 /* RoomDetailsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 813B198AE8833FD12E5A9C78 /* RoomDetailsCoordinator.swift */; }; D5C805F49B2C75DC3793E780 /* EmojiItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37A243E04B58DC6E41FDCD82 /* EmojiItem.swift */; }; D5EA4C6C80579279770D5804 /* ImageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */; }; + D63974A88CF2BC721F109C77 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = AD544C0FA48DFFB080920061 /* Collections */; }; D6417E5A799C3C7F14F9EC0A /* SessionVerificationViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3069ADED46D063202FE7698 /* SessionVerificationViewModelProtocol.swift */; }; D79F0F852C6A4255D5E616D2 /* UserNotificationControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ED2D2F6A137A95EA50413BE /* UserNotificationControllerProtocol.swift */; }; D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; }; @@ -475,6 +478,7 @@ FA9C427FFB11B1AA2DCC5602 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47111410B6E659A697D472B5 /* RoomProxyProtocol.swift */; }; FBCD77D557AACBE9B445133A /* MediaProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = E12C9E0B61A77C7F0EE7918C /* MediaProxy.swift */; }; FBF09B6C900415800DDF2A21 /* EmojiProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C113E0CB7E15E9765B1817A /* EmojiProvider.swift */; }; + FC10228E73323BDC09526F97 /* Collections in Frameworks */ = {isa = PBXBuildFile; productRef = 9C73F37731C9FDED1BB24C1C /* Collections */; }; FCD3F2B82CAB29A07887A127 /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 2B43F2AF7456567FE37270A7 /* KeychainAccess */; }; FE4593FC2A02AAF92E089565 /* ElementAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = EF1593DD87F974F8509BB619 /* ElementAnimations.swift */; }; FE8D76708280968F7A670852 /* MockUserNotificationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9080CDD3881D0D1B2F280A7C /* MockUserNotificationController.swift */; }; @@ -1077,6 +1081,7 @@ 3F2148F11164C7C5609984EB /* SwiftState in Frameworks */, 60ED66E63A169E47489348A8 /* GZIP in Frameworks */, EC280623A42904341363EAAF /* Sentry in Frameworks */, + 09BFDE37F0D0E586D26B17D7 /* Collections in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1088,6 +1093,7 @@ 53DEF39F0C4DE02E3FC56D91 /* SwiftyBeaver in Frameworks */, F06CE9132855E81EBB6DDC32 /* KeychainAccess in Frameworks */, 67D6E0700A9C1E676F6231F8 /* Kingfisher in Frameworks */, + D63974A88CF2BC721F109C77 /* Collections in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1108,6 +1114,7 @@ 492274DA6691EE985C2FCCAA /* GZIP in Frameworks */, F0F82C3C848C865C3098AA52 /* Sentry in Frameworks */, 3A64A93A651A3CB8774ADE8E /* SnapshotTesting in Frameworks */, + 3F327A62D233933F54F0F33A /* Collections in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1128,6 +1135,7 @@ 6298AB0906DDD3525CD78C6B /* SwiftState in Frameworks */, 407DCE030E0F9B7C9861D38A /* GZIP in Frameworks */, 8F2FAA98457750D9D664136F /* Sentry in Frameworks */, + FC10228E73323BDC09526F97 /* Collections in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2474,6 +2482,7 @@ 1BCD21310B997A6837B854D6 /* GZIP */, 67E7A6F388D3BF85767609D9 /* Sentry */, 21C83087604B154AA30E9A8F /* SnapshotTesting */, + BA93CD75CCE486660C9040BD /* Collections */, ); productName = UITests; productReference = F506C6ADB1E1DA6638078E11 /* UITests.xctest */; @@ -2528,6 +2537,7 @@ 9573B94B1C86C6DF751AF3FD /* SwiftState */, 997C7385E1A07E061D7E2100 /* GZIP */, 7731767AE437BA3BD2CC14A8 /* Sentry */, + 9C73F37731C9FDED1BB24C1C /* Collections */, ); productName = ElementX; productReference = 4CD6AC7546E8D7E5C73CEA48 /* ElementX.app */; @@ -2557,6 +2567,7 @@ 19CD5B074D7DD44AF4C58BB6 /* SwiftState */, 2B788C81F6369D164ADEB917 /* GZIP */, 886A0A498FA01E8EDD451D05 /* Sentry */, + A20EA00CCB9DBE0FFB17DD09 /* Collections */, ); productName = IntegrationTests; productReference = 9C7F7DE62D33C6A26CBFCD72 /* IntegrationTests.xctest */; @@ -2580,6 +2591,7 @@ AC5D19D7A65EB05A9704FB44 /* SwiftyBeaver */, 800631D7250B7F93195035F1 /* KeychainAccess */, 940C605265DD82DA0C655E23 /* Kingfisher */, + AD544C0FA48DFFB080920061 /* Collections */, ); productName = NSE; productReference = 0D8F620C8B314840D8602E3F /* NSE.appex */; @@ -2700,6 +2712,7 @@ packageReferences = ( AC3475112CA40C2C6E78D1EB /* XCRemoteSwiftPackageReference "matrix-analytics-events" */, 4CE94127E27181B8B72188F0 /* XCRemoteSwiftPackageReference "AppAuth-iOS" */, + F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */, C13F55E4518415CB4C278E73 /* XCRemoteSwiftPackageReference "DTCoreText" */, D5F7D47BBAAE0CF1DDEB3034 /* XCRemoteSwiftPackageReference "DeviceKit" */, 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */, @@ -4002,6 +4015,14 @@ minimumVersion = 1.10.0; }; }; + F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/apple/swift-collections"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.4; + }; + }; /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ @@ -4130,6 +4151,16 @@ package = 701C7BEF8F70F7A83E852DCC /* XCRemoteSwiftPackageReference "GZIP" */; productName = GZIP; }; + 9C73F37731C9FDED1BB24C1C /* Collections */ = { + isa = XCSwiftPackageProductDependency; + package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */; + productName = Collections; + }; + A20EA00CCB9DBE0FFB17DD09 /* Collections */ = { + isa = XCSwiftPackageProductDependency; + package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */; + productName = Collections; + }; A5A56C4F47C368EBE5C5E870 /* DesignKit */ = { isa = XCSwiftPackageProductDependency; productName = DesignKit; @@ -4164,11 +4195,21 @@ package = 25B4484A6A20B9F1705DEEDA /* XCRemoteSwiftPackageReference "SwiftyBeaver" */; productName = SwiftyBeaver; }; + AD544C0FA48DFFB080920061 /* Collections */ = { + isa = XCSwiftPackageProductDependency; + package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */; + productName = Collections; + }; B1E8B697DF78FE7F61FC6CA4 /* MatrixRustSDK */ = { isa = XCSwiftPackageProductDependency; package = 80B898A3AD2AC63F3ABFC218 /* XCRemoteSwiftPackageReference "matrix-rust-components-swift" */; productName = MatrixRustSDK; }; + BA93CD75CCE486660C9040BD /* Collections */ = { + isa = XCSwiftPackageProductDependency; + package = F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */; + productName = Collections; + }; BC01130651CB23340B899032 /* DeviceKit */ = { isa = XCSwiftPackageProductDependency; package = D5F7D47BBAAE0CF1DDEB3034 /* XCRemoteSwiftPackageReference "DeviceKit" */; diff --git a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 34adb22c64..b14bec7f2b 100644 --- a/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ElementX.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -108,6 +108,15 @@ "version" : "7.30.2" } }, + { + "identity" : "swift-collections", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-collections", + "state" : { + "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", + "version" : "1.0.4" + } + }, { "identity" : "swift-snapshot-testing", "kind" : "remoteSourceControl", diff --git a/ElementX/Sources/Other/Logging/RustTracing.swift b/ElementX/Sources/Other/Logging/RustTracing.swift index 4ef1e6bf89..c7b0be381e 100644 --- a/ElementX/Sources/Other/Logging/RustTracing.swift +++ b/ElementX/Sources/Other/Logging/RustTracing.swift @@ -14,48 +14,60 @@ // limitations under the License. // +import Collections import MatrixRustSDK // This exposes the full Rust side tracing subscriber filter for more flexibility. // We can filter by level, crate and even file. See more details here: // https://docs.rs/tracing-subscriber/0.2.7/tracing_subscriber/filter/struct.EnvFilter.html#examples struct TracingConfiguration { - static var release = TracingConfiguration(common: .info) - static var debug = TracingConfiguration() - static var full = TracingConfiguration(common: .info, - targets: [ - .hyper: .warn, - .sled: .warn, - .matrix_sdk_sled: .warn, - .matrix_sdk_http_client: .trace, - .matrix_sdk_ffi_uniffi_api: .warn, - .matrix_sdk_ffi: .warn, - .matrix_sdk_sliding_sync: .warn, - .matrix_sdk_base_sliding_sync: .warn, - .matrix_sdk_crypto: .trace - ]) + static var release = TracingConfiguration(overrides: [.common: .info]) + static var debug = TracingConfiguration(overrides: [.common: .info]) + static func custom(overrides: [Target: LogLevel]) -> TracingConfiguration { + TracingConfiguration(overrides: overrides) + } + + enum LogLevel: String { case error, warn, info, debug, trace } enum Target: String { + case common = "" + case hyper, sled, matrix_sdk_sled, matrix_sdk_ffi, matrix_sdk_crypto + case matrix_sdk_http_client = "matrix_sdk::http_client" case matrix_sdk_ffi_uniffi_api = "matrix_sdk_ffi::uniffi_api" case matrix_sdk_sliding_sync = "matrix_sdk::sliding_sync" case matrix_sdk_base_sliding_sync = "matrix_sdk_base::sliding_sync" } - enum LogLevel: String { case error, warn, info, debug, trace } - - var common = LogLevel.warn - var targets: [Target: LogLevel] = [ + let targets: OrderedDictionary = [ + .common: .warn, .hyper: .warn, .sled: .warn, .matrix_sdk_sled: .warn, .matrix_sdk_crypto: .debug, - .matrix_sdk_http_client: .debug + .matrix_sdk_http_client: .debug, + .matrix_sdk_sliding_sync: .trace, + .matrix_sdk_base_sliding_sync: .trace ] + var overrides = [Target: LogLevel]() + var filter: String { - "\(common),\(targets.map { "\($0.key.rawValue)=\($0.value.rawValue)" }.joined(separator: ","))" + var newTargets = targets + for (target, logLevel) in overrides { + newTargets.updateValue(logLevel, forKey: target) + } + + let components = newTargets.map { (target: Target, logLevel: LogLevel) in + guard !target.rawValue.isEmpty else { + return logLevel.rawValue + } + + return "\(target.rawValue)=\(logLevel.rawValue)" + } + + return components.joined(separator: ",") } } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift index e6d985abe1..1b953a6353 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenModels.swift @@ -95,6 +95,9 @@ struct HomeScreenViewStateBindings { } struct HomeScreenRoom: Identifiable, Equatable { + private static let placeholderLastMessage = AttributedString("Last message") + private static let placeholderAvatar = UIImage(systemName: "photo") + /// The list item identifier can be a real room identifier, a custom one for invalidated entries /// or a completely unique one for empty items and skeletons let id: String @@ -102,11 +105,11 @@ struct HomeScreenRoom: Identifiable, Equatable { /// The real room identifier this item points to let roomId: String? - let name: String + var name = "" - let hasUnreads: Bool + var hasUnreads = false - let timestamp: String? + var timestamp: String? var lastMessage: AttributedString? @@ -122,8 +125,8 @@ struct HomeScreenRoom: Identifiable, Equatable { name: "Placeholder room name", hasUnreads: false, timestamp: "Now", - lastMessage: AttributedString("Last message"), - avatar: UIImage(systemName: "photo"), + lastMessage: Self.placeholderLastMessage, + avatar: Self.placeholderAvatar, isPlaceholder: true) } } diff --git a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift index 70930815f6..6b61b9698a 100644 --- a/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift +++ b/ElementX/Sources/Screens/HomeScreen/HomeScreenViewModel.swift @@ -31,6 +31,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol private let visibleItemRangePublisher = CurrentValueSubject, Never>(0..<0) + private var roomsForIdentifiers = [String: HomeScreenRoom]() + var callback: ((HomeScreenViewModelAction) -> Void)? // MARK: - Setup @@ -178,7 +180,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol // MARK: - Private private func loadDataForRoomIdentifier(_ identifier: String) { - guard let room = state.rooms.first(where: { $0.roomId == identifier }), + guard let room = roomsForIdentifiers[identifier], room.avatar == nil, let avatarURL = room.avatarURL else { return @@ -206,6 +208,8 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol return } + MXLog.info("Updating rooms") + var rooms = [HomeScreenRoom]() // Try merging together results from both the visibleRoomsSummaryProvider and the allRoomsSummaryProvider @@ -235,24 +239,34 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol } state.rooms = rooms + + MXLog.info("Finished updating rooms") } private func buildRoom(with details: RoomSummaryDetails) -> HomeScreenRoom { - let avatarImage = details.avatarURL.flatMap { userSession.mediaProvider.imageFromURL($0, avatarSize: .room(on: .home)) } + var room: HomeScreenRoom! = roomsForIdentifiers[details.id] + + if room == nil { + room = HomeScreenRoom(id: details.id, + roomId: details.id, + avatarURL: details.avatarURL) + } + + room.name = details.name + room.hasUnreads = details.unreadNotificationCount > 0 + room.lastMessage = details.lastMessage + + if let avatarURL = details.avatarURL { + room.avatar = userSession.mediaProvider.imageFromURL(avatarURL, avatarSize: .room(on: .home)) + } - var timestamp: String? if let lastMessageTimestamp = details.lastMessageTimestamp { - timestamp = lastMessageTimestamp.formatted(date: .omitted, time: .shortened) + room.timestamp = lastMessageTimestamp.formatted(date: .omitted, time: .shortened) } - return HomeScreenRoom(id: details.id, - roomId: details.id, - name: details.name, - hasUnreads: details.unreadNotificationCount > 0, - timestamp: timestamp, - lastMessage: details.lastMessage, - avatarURL: details.avatarURL, - avatar: avatarImage) + roomsForIdentifiers[details.id] = room + + return room } private func updateVisibleRange(_ range: Range) { diff --git a/ElementX/Sources/Screens/RoomScreen/View/TimelineTableViewController.swift b/ElementX/Sources/Screens/RoomScreen/View/TimelineTableViewController.swift index abbcbf5309..d2bb68b0c5 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/TimelineTableViewController.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/TimelineTableViewController.swift @@ -69,7 +69,6 @@ class TimelineTableViewController: UIViewController { didSet { // Paginate again if the threshold hasn't been satisfied. paginateBackwardsPublisher.send(()) - applySnapshot() } } diff --git a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift index 332f3fba22..7a8a6bdbaa 100644 --- a/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift +++ b/ElementX/Sources/Services/Room/RoomSummary/RoomSummaryProvider.swift @@ -56,7 +56,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { .store(in: &cancellables) slidingSyncViewProxy.diffPublisher - .collect(.byTime(serialDispatchQueue, 0.1)) + .collect(.byTime(serialDispatchQueue, 0.025)) .sink { [weak self] in self?.updateRoomsWithDiffs($0) } .store(in: &cancellables) } @@ -80,7 +80,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { return } - MXLog.verbose("Updating \(identifiers.count) rooms") + MXLog.info("Updating \(identifiers.count) rooms") guard statePublisher.value == .live else { MXLog.warning("Sliding sync not live yet, ignoring update.") @@ -117,7 +117,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { } fileprivate func updateRoomsWithDiffs(_ diffs: [SlidingSyncViewRoomsListDiff]) { - MXLog.verbose("Received \(diffs.count) diffs") + MXLog.info("Received \(diffs.count) diffs") rooms = diffs .reduce(rooms) { currentItems, diff in @@ -136,7 +136,7 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol { return updatedItems } - MXLog.verbose("Finished applying \(diffs.count) diffs") + MXLog.info("Finished applying \(diffs.count) diffs") } private func buildRoomSummaryForIdentifier(_ identifier: String, invalidated: Bool) -> RoomSummary { diff --git a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift b/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift index 54184d9dcd..172ff6dc0a 100644 --- a/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift +++ b/ElementX/Sources/Services/Timeline/RoomTimelineProvider.swift @@ -29,6 +29,7 @@ private class RoomTimelineListener: TimelineListener { class RoomTimelineProvider: RoomTimelineProviderProtocol { private let roomProxy: RoomProxyProtocol private var cancellables = Set() + private let serialDispatchQueue: DispatchQueue let itemsPublisher = CurrentValueSubject<[TimelineItemProxy], Never>([]) @@ -40,6 +41,7 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol { init(roomProxy: RoomProxyProtocol) { self.roomProxy = roomProxy + serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider") itemProxies = [] Task { @@ -47,8 +49,8 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol { roomTimelineListener .itemsUpdatePublisher - .receive(on: DispatchQueue.main) - .sink { [weak self] in self?.updateItemsWithDiff($0) } + .collect(.byTime(serialDispatchQueue, 0.025)) + .sink { [weak self] in self?.updateItemsWithDiffs($0) } .store(in: &cancellables) switch await roomProxy.addTimelineListener(listener: roomTimelineListener) { @@ -63,22 +65,25 @@ class RoomTimelineProvider: RoomTimelineProviderProtocol { // MARK: - Private - private func updateItemsWithDiff(_ diff: TimelineDiff) { + private func updateItemsWithDiffs(_ diffs: [TimelineDiff]) { MXLog.verbose("Received timeline diff") - guard let collectionDiff = buildDiff(from: diff, on: itemProxies) else { - MXLog.error("Failed building CollectionDifference from \(diff)") - return - } - - guard let updatedItems = itemProxies.applying(collectionDiff) else { - MXLog.error("Failed applying diff: \(collectionDiff)") - return - } - - MXLog.verbose("Applied diff \(collectionDiff), new count: \(updatedItems.count)") - - itemProxies = updatedItems + itemProxies = diffs + .reduce(itemProxies) { currentItems, diff in + guard let collectionDiff = buildDiff(from: diff, on: currentItems) else { + MXLog.error("Failed building CollectionDifference from \(diff)") + return currentItems + } + + guard let updatedItems = currentItems.applying(collectionDiff) else { + MXLog.error("Failed applying diff: \(collectionDiff)") + return currentItems + } + + MXLog.verbose("Applied diff \(collectionDiff), new count: \(updatedItems.count)") + + return updatedItems + } MXLog.verbose("Finished applying diff") } diff --git a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift index 7a5dc1ba0b..4c3499b8a5 100644 --- a/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift +++ b/ElementX/Sources/Services/Timeline/TimelineController/RoomTimelineController.swift @@ -21,10 +21,11 @@ import UniformTypeIdentifiers class RoomTimelineController: RoomTimelineControllerProtocol { private let userId: String + private let roomProxy: RoomProxyProtocol private let timelineProvider: RoomTimelineProviderProtocol private let timelineItemFactory: RoomTimelineItemFactoryProtocol private let mediaProvider: MediaProviderProtocol - let roomProxy: RoomProxyProtocol + private let serialDispatchQueue: DispatchQueue private var cancellables = Set() private var timelineItemsUpdateTask: Task? { @@ -51,10 +52,11 @@ class RoomTimelineController: RoomTimelineControllerProtocol { self.timelineItemFactory = timelineItemFactory self.mediaProvider = mediaProvider self.roomProxy = roomProxy + serialDispatchQueue = DispatchQueue(label: "io.element.elementx.roomtimelineprovider") self.timelineProvider .itemsPublisher - .receive(on: DispatchQueue.main) + .receive(on: serialDispatchQueue) .sink { [weak self] _ in guard let self else { return } @@ -236,14 +238,8 @@ class RoomTimelineController: RoomTimelineControllerProtocol { updateTimelineItems() } - private func updateTimelineItems() { - timelineItemsUpdateTask = Task { - await asyncUpdateTimelineItems() - } - } - // swiftlint:disable:next cyclomatic_complexity - private func asyncUpdateTimelineItems() async { + private func updateTimelineItems() { var newTimelineItems = [RoomTimelineItemProtocol]() var canBackPaginate = true var isBackPaginating = false diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index 041ae6f816..0cd90dd075 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -124,6 +124,7 @@ targets: - package: SwiftState - package: GZIP - package: Sentry + - package: Collections sources: - path: ../Sources diff --git a/IntegrationTests/SupportingFiles/target.yml b/IntegrationTests/SupportingFiles/target.yml index 41ebbcb591..1aac914761 100644 --- a/IntegrationTests/SupportingFiles/target.yml +++ b/IntegrationTests/SupportingFiles/target.yml @@ -43,6 +43,7 @@ targets: - package: SwiftState - package: GZIP - package: Sentry + - package: Collections info: path: ../SupportingFiles/Info.plist diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index 99eb07cedf..cf9b1bb130 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -36,6 +36,7 @@ targets: - package: SwiftyBeaver - package: KeychainAccess - package: Kingfisher + - package: Collections info: path: ../SupportingFiles/Info.plist diff --git a/UITests/SupportingFiles/target.yml b/UITests/SupportingFiles/target.yml index 75c61574bf..a5d610b6f8 100644 --- a/UITests/SupportingFiles/target.yml +++ b/UITests/SupportingFiles/target.yml @@ -40,6 +40,7 @@ targets: - package: GZIP - package: Sentry - package: SnapshotTesting + - package: Collections info: path: ../SupportingFiles/Info.plist diff --git a/UnitTests/Sources/TracingConfigurationTests.swift b/UnitTests/Sources/TracingConfigurationTests.swift index 24e5ccd652..375f4cea6a 100644 --- a/UnitTests/Sources/TracingConfigurationTests.swift +++ b/UnitTests/Sources/TracingConfigurationTests.swift @@ -19,32 +19,18 @@ import XCTest @testable import ElementX class TracingConfigurationTests: XCTestCase { - func testReleaseConfiguration() { - let filterComponents = TracingConfiguration.release.filter.components(separatedBy: ",") - XCTAssertTrue(filterComponents.contains("info")) - XCTAssertTrue(filterComponents.contains("hyper=warn")) - XCTAssertTrue(filterComponents.contains("sled=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_sled=warn")) - } - - func testDebugConfiguration() { - let filterComponents = TracingConfiguration.debug.filter.components(separatedBy: ",") - XCTAssertTrue(filterComponents.contains("warn")) - XCTAssertTrue(filterComponents.contains("hyper=warn")) - XCTAssertTrue(filterComponents.contains("sled=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_sled=warn")) - } - - func testFullConfiguration() { - let filterComponents = TracingConfiguration.full.filter.components(separatedBy: ",") - XCTAssertTrue(filterComponents.contains("info")) - XCTAssertTrue(filterComponents.contains("hyper=warn")) - XCTAssertTrue(filterComponents.contains("sled=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_sled=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=trace")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_ffi::uniffi_api=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_ffi=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk::sliding_sync=warn")) - XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=warn")) + func testConfiguration() { + let configuration = TracingConfiguration(overrides: [.common: .trace, + .matrix_sdk_base_sliding_sync: .error, + .matrix_sdk_http_client: .warn, + .matrix_sdk_crypto: .info, + .hyper: .debug]) + + let filterComponents = configuration.filter.components(separatedBy: ",") + XCTAssertEqual(filterComponents.first, "trace") + XCTAssertTrue(filterComponents.contains("matrix_sdk_base::sliding_sync=error")) + XCTAssertTrue(filterComponents.contains("matrix_sdk::http_client=warn")) + XCTAssertTrue(filterComponents.contains("matrix_sdk_crypto=info")) + XCTAssertTrue(filterComponents.contains("hyper=debug")) } } diff --git a/project.yml b/project.yml index 38a3f8e2b4..7d5a13e679 100644 --- a/project.yml +++ b/project.yml @@ -83,3 +83,6 @@ packages: SnapshotTesting: url: https://github.com/pointfreeco/swift-snapshot-testing majorVersion: 1.10.0 + Collections: + url: https://github.com/apple/swift-collections + majorVersion: 1.0.4