Skip to content

Commit

Permalink
fix(DataStore): created and deleted model on one device appears as cr…
Browse files Browse the repository at this point in the history
…eated on the other (#3554)

* fix(DataStore): created and deleted model on one device appears as created on the other (#3553)

* fix(DataStore): created and deleted model on one device appears as created on the other #3553

Co-authored-by: Di Wu <[email protected]>

* fix(DataStore): created and deleted model on one device appears as created on the other #3553

---------

Co-authored-by: Di Wu <[email protected]>
  • Loading branch information
MuniekMg and 5d authored Mar 11, 2024
1 parent c49d836 commit 9f8e690
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,26 +33,41 @@ struct RemoteSyncReconciler {
}
}


/// Reconciles the incoming `remoteModels` against the local metadata to get the disposition
///
/// GroupBy the remoteModels by model identifier and apply only the latest version of the remoteModel
///
/// - Parameters:
/// - remoteModels: models retrieved from the remote store
/// - localMetadatas: metadata retrieved from the local store
/// - Returns: disposition of models to apply locally
static func getDispositions(_ remoteModels: [RemoteModel],
localMetadatas: [LocalMetadata]) -> [Disposition] {
guard !remoteModels.isEmpty else {
static func getDispositions(
_ remoteModels: [RemoteModel],
localMetadatas: [LocalMetadata]
) -> [Disposition] {
let remoteModelsGroupByIdentifier = remoteModels.reduce([String: [RemoteModel]]()) {
$0.merging([
$1.model.identifier: ($0[$1.model.identifier] ?? []) + [$1]
], uniquingKeysWith: { $1 })
}

let optimizedRemoteModels = remoteModelsGroupByIdentifier.values.compactMap {
$0.sorted(by: { $0.syncMetadata.version > $1.syncMetadata.version }).first
}

guard !optimizedRemoteModels.isEmpty else {
return []
}

guard !localMetadatas.isEmpty else {
return remoteModels.compactMap { getDisposition($0, localMetadata: nil) }
return optimizedRemoteModels.compactMap { getDisposition($0, localMetadata: nil) }
}

let metadataBymodelId = localMetadatas.reduce(into: [:]) { $0[$1.modelId] = $1 }
let dispositions = remoteModels.compactMap { getDisposition($0, localMetadata: metadataBymodelId[$0.model.identifier]) }


let metadataByModelId = localMetadatas.reduce(into: [:]) { $0[$1.modelId] = $1 }
let dispositions = optimizedRemoteModels.compactMap {
getDisposition($0, localMetadata: metadataByModelId[$0.model.identifier])
}

return dispositions
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,33 @@ class RemoteSyncReconcilerTests: XCTestCase {
}
waitForExpectations(timeout: 1)
}

func testGetDispositions_emptyLocal_singleModelAddedAndDeleted() {
let sameId = UUID().uuidString

let remoteModels = [
makeRemoteModel(modelId: sameId, deleted: false, version: 1),
makeRemoteModel(modelId: sameId, deleted: true, version: 2)
]

let dispositions = RemoteSyncReconciler.getDispositions(remoteModels, localMetadatas: [])

XCTAssertTrue(dispositions.isEmpty)
}

func testGetDispositions_emptyLocal_oneModelAdded_SecondModelAddedAndDeleted() {
let sameId = UUID().uuidString

let remoteModels = [
makeRemoteModel(deleted: false, version: 1),
makeRemoteModel(modelId: sameId, deleted: false, version: 1),
makeRemoteModel(modelId: sameId, deleted: true, version: 2)
]

let dispositions = RemoteSyncReconciler.getDispositions(remoteModels, localMetadatas: [])

XCTAssertTrue(dispositions.count == 1)
}

// MARK: - Utilities

Expand Down

0 comments on commit 9f8e690

Please sign in to comment.