Skip to content

Commit

Permalink
Turn on background mapping (#3250)
Browse files Browse the repository at this point in the history
  • Loading branch information
laevandus authored Jun 19, 2024
1 parent d61a7c4 commit cdf927b
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 61 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix notifications muted state for the current user in channel members [#3236](https://github.com/GetStream/stream-chat-swift/pull/3236)
- Reset channel members and watchers state when fetching the initial state of the channel [#3245](https://github.com/GetStream/stream-chat-swift/pull/3245)
- Fix inconsistent message text when extremely quickly updating it [#3242](https://github.com/GetStream/stream-chat-swift/pull/3242)
### 🔄 Changed
- Enable background mapping by default, which improves performance overall [#3250](https://github.com/GetStream/stream-chat-swift/pull/3250)

## StreamChatUI
### 🐞 Fixed
Expand Down
2 changes: 1 addition & 1 deletion Sources/StreamChat/Config/StreamRuntimeCheck.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public enum StreamRuntimeCheck {
/// For *internal use* only
///
/// Enables background mapping of DB models
public static var _isBackgroundMappingEnabled = false
public static var _isBackgroundMappingEnabled = true

/// For *internal use* only
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,16 @@ final class ChannelController_Tests: XCTestCase {
func test_channel_accessible_initially() throws {
let payload = dummyPayload(with: channelId)

// Save two channels to DB (only one matching the query) and wait for completion
try client.databaseContainer.writeSynchronously { session in
// Channel with the id matching the query
try session.saveChannel(payload: payload)
// Other channel
try session.saveChannel(payload: self.dummyPayload(with: .unique))
waitForMessagesUpdate(count: 1) {
// Save two channels to DB (only one matching the query) and wait for completion
try client.databaseContainer.writeSynchronously { session in
// Channel with the id matching the query
try session.saveChannel(payload: payload)
// Other channel
try session.saveChannel(payload: self.dummyPayload(with: .unique))
}
}

// Assert the channel and messages are loaded
XCTAssertEqual(controller.channel?.cid, channelId)
XCTAssertEqual(Set(controller.messages.map(\.id)), Set(payload.messages.map(\.id)))
Expand Down Expand Up @@ -543,6 +545,7 @@ final class ChannelController_Tests: XCTestCase {
)
let token = Token(rawValue: "", userId: userId, expiration: nil)
controller.client.authenticationRepository.setMockToken(token)
try client.mockDatabaseContainer.createCurrentUser(id: userId)

let messages: [MessagePayload] = [
.dummy(messageId: notOwnMessageId, authorUserId: .unique, createdAt: Date().addingTimeInterval(-1000)),
Expand All @@ -552,6 +555,7 @@ final class ChannelController_Tests: XCTestCase {
]

createChannel(messages: messages, channelReads: [channelRead])

writeAndWaitForMessageUpdates(count: messages.count) {
try $0.saveCurrentUser(payload: .dummy(userId: userId, role: .user))
}
Expand Down Expand Up @@ -636,13 +640,14 @@ final class ChannelController_Tests: XCTestCase {

let payload = dummyPayload(with: channelId)
assert(!payload.messages.isEmpty)

// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
waitForMessagesUpdate(count: payload.messages.count) {
// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
}
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))
}
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))


XCTAssertEqual(controller.channel?.cid, channelId)
XCTAssertEqual(controller.messages.count, payload.messages.count)
}
Expand All @@ -657,13 +662,14 @@ final class ChannelController_Tests: XCTestCase {

let payload = dummyPayload(with: channelId)
assert(!payload.messages.isEmpty)

// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
waitForMessagesUpdate(count: payload.messages.count) {
// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
}
env.channelUpdater?.update_onChannelCreated?(channelId)
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))
}
env.channelUpdater?.update_onChannelCreated?(channelId)
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))

XCTAssertEqual(controller.channel?.cid, channelId)
XCTAssertEqual(controller.messages.count, payload.messages.count)
Expand All @@ -680,12 +686,14 @@ final class ChannelController_Tests: XCTestCase {
let payload = dummyPayload(with: channelId)
assert(!payload.messages.isEmpty)

// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
waitForMessagesUpdate(count: payload.messages.count) {
// Simulate successful updater response
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
}
env.channelUpdater?.update_onChannelCreated?(channelId)
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))
}
env.channelUpdater?.update_onChannelCreated?(channelId)
env.channelUpdater?.update_completion?(.success(dummyPayload(with: .unique)))

XCTAssertEqual(controller.channel?.cid, channelId)
XCTAssertEqual(controller.messages.count, payload.messages.count)
Expand Down Expand Up @@ -951,9 +959,9 @@ final class ChannelController_Tests: XCTestCase {

// Check if the message ordering is correct
// First message should be the newest message
XCTAssertEqual(controller.messages[0].id, newerMessagePayload.id)
AssertAsync.willBeEqual(controller.messages[0].id, newerMessagePayload.id)
// Third message is the failed one
XCTAssertEqual(controller.messages[2].id, oldMessageId)
AssertAsync.willBeEqual(controller.messages[2].id, oldMessageId)
}

func test_channelControllerForNewDirectMessagesChannel_throwsError_ifCurrentUserDoesNotExist() {
Expand Down Expand Up @@ -1541,37 +1549,34 @@ final class ChannelController_Tests: XCTestCase {
}

func test_channelUpdateDelegate_isCalled_whenChannelReadsAreUpdated() throws {
let delegate = ChannelController_Delegate(expectedQueueId: controllerCallbackQueueID)
controller.delegate = delegate

let userId: UserId = .unique

let originalReadDate: Date = .unique

// Create a channel in the DB
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: self.dummyPayload(with: self.channelId), query: nil, cache: nil)
// Create a read for the channel
try $0.saveChannelRead(
payload: ChannelReadPayload(
user: self.dummyUser(id: userId),
lastReadAt: originalReadDate,
lastReadMessageId: .unique,
unreadMessagesCount: .unique // This value doesn't matter at all. It's not updated by events. We cam ignore it.
),
for: self.channelId,
cache: nil
)
let payload = dummyPayload(with: channelId)

waitForMessagesUpdate(count: payload.messages.count) {
// Create a channel in the DB
try client.databaseContainer.writeSynchronously {
try $0.saveChannel(payload: payload, query: nil, cache: nil)
// Create a read for the channel
try $0.saveChannelRead(
payload: ChannelReadPayload(
user: self.dummyUser(id: userId),
lastReadAt: originalReadDate,
lastReadMessageId: .unique,
unreadMessagesCount: .unique // This value doesn't matter at all. It's not updated by events. We cam ignore it.
),
for: self.channelId,
cache: nil
)
}
}

XCTAssertEqual(
controller.channel?.reads.first(where: { $0.user.id == userId })?.lastReadAt,
originalReadDate
)

// Simulate `synchronize()` call
controller.synchronize()

let newReadDate: Date = .unique

// Update the read
Expand All @@ -1581,12 +1586,10 @@ final class ChannelController_Tests: XCTestCase {
}

// Assert the value is updated and the delegate is called
XCTAssertEqual(
AssertAsync.willBeEqual(
controller.channel?.reads.first(where: { $0.user.id == userId })?.lastReadAt,
newReadDate
)

AssertAsync.willBeEqual(delegate.didUpdateChannel_channel, .update(controller.channel!))
}

// MARK: - New direct message channel creation tests
Expand Down Expand Up @@ -3770,6 +3773,7 @@ final class ChannelController_Tests: XCTestCase {
try session.saveChannel(payload: self.dummyPayload(with: query.cid!, ownCapabilities: [ChannelCapability.readEvents.rawValue]))
}
env.channelUpdater!.update_onChannelCreated?(query.cid!)
AssertAsync.willBeTrue(controller.channel != nil)

// Simulate `markRead` call and assert no error is returned
error = try waitFor { [callbackQueueID] completion in
Expand Down Expand Up @@ -5442,7 +5446,7 @@ extension ChannelController_Tests {
do {
try client.databaseContainer.writeSynchronously(actions)
} catch {
XCTFail()
XCTFail(error.localizedDescription)
}
}
}
Expand Down Expand Up @@ -5512,15 +5516,23 @@ extension ChannelController_Tests {
XCTAssertEqual(controller.firstUnreadMessageId, oldestRegularMessage.id, file: file, line: line)
}

private func waitForMessagesUpdate(count: Int, file: StaticString = #file, line: UInt = #line, block: () -> Void) {
private func waitForMessagesUpdate(count: Int, file: StaticString = #file, line: UInt = #line, block: () throws -> Void) {
guard StreamRuntimeCheck._isBackgroundMappingEnabled else {
controller.delegate = MessagesUpdateWaiter(messagesCount: count, messagesExpectation: nil)
block()
do {
try block()
} catch {
XCTFail(error.localizedDescription)
}
return
}
let expectation = self.expectation(description: "Messages update")
controller.delegate = MessagesUpdateWaiter(messagesCount: count, messagesExpectation: expectation)
block()
do {
try block()
} catch {
XCTFail(error.localizedDescription)
}
wait(for: [expectation], timeout: defaultTimeout)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ final class ChannelListController_Tests: XCTestCase {
env.channelListUpdater?.update_completion?(.success([]))

// Check if state changed after successful network call.
XCTAssertEqual(controller.state, .remoteDataFetched)
AssertAsync.willBeEqual(controller.state, .remoteDataFetched)
}

func test_channelsAccess_changesControllerState() {
Expand Down
2 changes: 1 addition & 1 deletion Tests/StreamChatTests/StateLayer/Chat_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1583,7 +1583,7 @@ final class Chat_Tests: XCTestCase {
.map { $0 + offset }
.map {
.dummy(
messageId: String(format: "%03d", $0),
messageId: String(format: "reply_%03d", $0),
parentId: parentMessageId,
createdAt: Date(timeIntervalSinceReferenceDate: TimeInterval($0)),
cid: channelId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ final class CoreDataLazy_Tests: StressTestCase {
}, nil)

// Assert the value hasn't been evaluated yet
XCTAssertEqual(counter, 0)
if !StreamRuntimeCheck._isBackgroundMappingEnabled {
XCTAssertEqual(counter, 0)
}

// Evaluate the value and check the result is correct a couple of times
XCTAssertEqual(value, result)
Expand All @@ -48,7 +50,10 @@ final class CoreDataLazy_Tests: StressTestCase {
XCTAssertEqual(counter, 1)
}

func test_theClosureIsEvaluatedOnTheContextQueue() {
func test_theClosureIsEvaluatedOnTheContextQueue() throws {
if StreamRuntimeCheck._isBackgroundMappingEnabled {
throw XCTSkip("Does not apply to background mapping")
}
var context: SpyContext! = SpyContext(persistenStore: database.persistentStoreCoordinator)
let result = Int.random(in: Int.min...Int.max)

Expand Down
10 changes: 8 additions & 2 deletions Tests/StreamChatTests/Utils/LazyCachedMapCollection_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import XCTest

final class LazyCachedMapCollection_Tests: XCTestCase {
func test_mapIsLazy() {
func test_mapIsLazy() throws {
if StreamRuntimeCheck._isBackgroundMappingEnabled {
throw XCTSkip("Does not apply to background mapping")
}
// Arrange: Prepare sequence that records transformations
var mapped: Set<Int> = []
var transformationCount = 0
Expand All @@ -30,7 +33,10 @@ final class LazyCachedMapCollection_Tests: XCTestCase {
XCTAssertEqual(transformationCount, 2)
}

func test_creatingCollection_doesntEvaluateSourceLazyCollection() {
func test_creatingCollection_doesntEvaluateSourceLazyCollection() throws {
if StreamRuntimeCheck._isBackgroundMappingEnabled {
throw XCTSkip("Does not apply to background mapping")
}
// Create source collection that is lazy and record when it's evaluated
let source = [0, 1, 2]
var lazyMappedEvaluatedValues: [Int] = []
Expand Down

0 comments on commit cdf927b

Please sign in to comment.