diff --git a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/RemoteSyncReconciler.swift b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/RemoteSyncReconciler.swift index 1bfc0852b1..df95b21a8a 100644 --- a/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/RemoteSyncReconciler.swift +++ b/AmplifyPlugins/DataStore/Sources/AWSDataStorePlugin/Sync/SubscriptionSync/ReconcileAndLocalSave/RemoteSyncReconciler.swift @@ -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 } diff --git a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/RemoteSyncReconcilerTests.swift b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/RemoteSyncReconcilerTests.swift index 612947d04d..1584ed3897 100644 --- a/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/RemoteSyncReconcilerTests.swift +++ b/AmplifyPlugins/DataStore/Tests/AWSDataStorePluginTests/Sync/SubscriptionSync/RemoteSyncReconcilerTests.swift @@ -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