Skip to content

Commit 26a0e11

Browse files
author
Di Wu
authored
fix(datastore-v1): stop datastore only stop sync engine (#2863)
* fix(datastore-v1): stop datastore only stop sync engine * resolve comments * resolve mock storage engine adapter tests
1 parent 670458a commit 26a0e11

15 files changed

+297
-183
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/AWSDataStorePlugin+DataStoreBaseBehavior.swift

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,6 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
113113
modelSchema: ModelSchema,
114114
identifier: ModelIdentifierProtocol,
115115
completion: DataStoreCallback<M?>) {
116-
initStorageEngineAndStartSync()
117116
query(modelType,
118117
modelSchema: modelSchema,
119118
where: identifier.predicate,
@@ -272,7 +271,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
272271

273272
public func start(completion: @escaping DataStoreCallback<Void>) {
274273
initStorageEngineAndStartSync { result in
275-
completion(result)
274+
self.queue.async { completion(result) }
276275
}
277276
}
278277

@@ -294,7 +293,6 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
294293
}
295294

296295
storageEngine.stopSync { result in
297-
self.storageEngine = nil
298296
self.queue.async {
299297
completion(result)
300298
}
@@ -355,6 +353,17 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
355353
guard #available(iOS 13.0, *) else {
356354
return
357355
}
356+
357+
guard let storageEngine = storageEngine else {
358+
log.info(
359+
"""
360+
StorageEngine is nil;
361+
Skip publishing the mutaitonEvent for \(mutationType) - \(modelSchema.name)
362+
"""
363+
)
364+
return
365+
}
366+
358367
let metadata = MutationSyncMetadata.keys
359368
let metadataId = MutationSyncMetadata.identifier(modelName: modelSchema.name,
360369
modelId: model.identifier(schema: modelSchema).stringValue)

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/AWSDataStorePlugin.swift

Lines changed: 31 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ import Combine
1010
import AWSPluginsCore
1111
import Foundation
1212

13-
enum InitStorageEngineResult {
14-
case successfullyInitialized
15-
case alreadyInitialized
16-
case failure(DataStoreError)
17-
}
18-
1913
final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
2014

2115
public var key: PluginKey = "awsDataStorePlugin"
@@ -117,55 +111,49 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
117111
/// Initializes the underlying storage engine
118112
/// - Returns: success if the engine is successfully initialized or
119113
/// a failure with a DataStoreError
120-
func initStorageEngine() -> InitStorageEngineResult {
121-
storageEngineInitQueue.sync {
122-
if storageEngine != nil {
123-
return .alreadyInitialized
124-
}
114+
func initStorageEngine() -> Result<StorageEngineBehavior, DataStoreError> {
115+
if let storageEngine = storageEngine {
116+
return .success(storageEngine)
117+
}
125118

126-
do {
127-
if #available(iOS 13.0, *) {
128-
if self.dataStorePublisher == nil {
129-
self.dataStorePublisher = DataStorePublisher()
130-
}
119+
do {
120+
if #available(iOS 13.0, *) {
121+
if self.dataStorePublisher == nil {
122+
self.dataStorePublisher = DataStorePublisher()
131123
}
132-
try resolveStorageEngine(dataStoreConfiguration: dataStoreConfiguration)
133-
try storageEngine.setUp(modelSchemas: ModelRegistry.modelSchemas)
134-
try storageEngine.applyModelMigrations(modelSchemas: ModelRegistry.modelSchemas)
135-
136-
return .successfullyInitialized
137-
} catch {
138-
log.error(error: error)
139-
return .failure(.invalidOperation(causedBy: error))
140124
}
141-
125+
try resolveStorageEngine(dataStoreConfiguration: dataStoreConfiguration)
126+
try storageEngine.setUp(modelSchemas: ModelRegistry.modelSchemas)
127+
try storageEngine.applyModelMigrations(modelSchemas: ModelRegistry.modelSchemas)
128+
129+
return .success(storageEngine)
130+
} catch {
131+
log.error(error: error)
132+
return .failure(.invalidOperation(causedBy: error))
142133
}
134+
143135
}
144136

145137
/// Initializes the underlying storage engine and starts the syncing process
146138
/// - Parameter completion: completion handler called with a success if the sync process started
147139
/// or with a DataStoreError in case of failure
148140
func initStorageEngineAndStartSync(completion: @escaping DataStoreCallback<Void> = { _ in }) {
149-
if storageEngine != nil {
150-
completion(.successfulVoid)
151-
return
152-
}
153-
154-
switch initStorageEngine() {
155-
case .alreadyInitialized:
156-
completion(.successfulVoid)
157-
case .successfullyInitialized:
158-
storageEngine.startSync { result in
159-
160-
self.operationQueue.operations.forEach { operation in
161-
if let operation = operation as? DataStoreObserveQueryOperation {
162-
operation.startObserveQuery(with: self.storageEngine)
141+
storageEngineInitQueue.sync {
142+
completion(initStorageEngine().flatMap { $0.startSync() }.flatMap { result in
143+
switch result {
144+
case .alreadyInitialized:
145+
return .successfulVoid
146+
case .successfullyInitialized:
147+
self.operationQueue.operations.forEach { operation in
148+
if let operation = operation as? DataStoreObserveQueryOperation {
149+
operation.startObserveQuery(with: self.storageEngine)
150+
}
163151
}
152+
return .successfulVoid
153+
case let .failure(error):
154+
return .failure(error)
164155
}
165-
completion(result)
166-
}
167-
case .failure(let error):
168-
completion(.failure(causedBy: error))
156+
})
169157
}
170158
}
171159

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/StorageEngine+SyncRequirement.swift

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,57 @@ import AWSPluginsCore
1212

1313
extension StorageEngine {
1414

15-
func startSync(completion: @escaping DataStoreCallback<Void>) {
16-
guard let api = tryGetAPIPlugin() else {
17-
log.info("Unable to find suitable API plugin for syncEngine. syncEngine will not be started")
18-
completion(.failure(.configuration(
19-
"Unable to find suitable API plugin for syncEngine. syncEngine will not be started",
20-
"Ensure the API category has been setup and configured for your project",
21-
nil
22-
)))
23-
return
24-
}
15+
func startSync() -> Result<SyncEngineInitResult, DataStoreError> {
16+
let (result, syncEngine) = initalizeSyncEngine()
17+
18+
if let syncEngine = syncEngine, !syncEngine.isSyncing() {
19+
guard let api = tryGetAPIPlugin() else {
20+
log.info("Unable to find suitable API plugin for syncEngine. syncEngine will not be started")
21+
return .failure(.configuration(
22+
"Unable to find suitable API plugin for syncEngine. syncEngine will not be started",
23+
"Ensure the API category has been setup and configured for your project",
24+
nil
25+
))
26+
}
2527

26-
let authPluginRequired = StorageEngine.requiresAuthPlugin(api)
28+
let authPluginRequired = StorageEngine.requiresAuthPlugin(api)
29+
30+
guard authPluginRequired else {
31+
syncEngine.start(api: api, auth: nil)
32+
return .success(.successfullyInitialized)
33+
}
2734

28-
guard authPluginRequired else {
29-
syncEngine?.start(api: api, auth: nil)
30-
completion(.successfulVoid)
31-
return
35+
guard let auth = tryGetAuthPlugin() else {
36+
log.warn("Unable to find suitable Auth plugin for syncEngine. Models require auth")
37+
return .failure(.configuration(
38+
"Unable to find suitable Auth plugin for syncEngine. Models require auth",
39+
"Ensure the Auth category has been setup and configured for your project",
40+
nil
41+
))
42+
}
43+
syncEngine.start(api: api, auth: auth)
3244
}
3345

34-
guard let auth = tryGetAuthPlugin() else {
35-
log.warn("Unable to find suitable Auth plugin for syncEngine. Models require auth")
36-
completion(.failure(.configuration(
37-
"Unable to find suitable Auth plugin for syncEngine. Models require auth",
38-
"Ensure the Auth category has been setup and configured for your project",
39-
nil
40-
)))
41-
return
46+
return .success(result)
47+
}
48+
49+
private func initalizeSyncEngine() -> (SyncEngineInitResult, RemoteSyncEngineBehavior?) {
50+
if let syncEngine = syncEngine {
51+
return (.alreadyInitialized, syncEngine)
52+
} else {
53+
if #available(iOS 13.0, *), isSyncEnabled, syncEngine == nil {
54+
self.syncEngine = try? RemoteSyncEngine(
55+
storageAdapter: storageAdapter,
56+
dataStoreConfiguration: dataStoreConfiguration
57+
)
58+
59+
self.syncEngineSink = syncEngine?.publisher.sink(
60+
receiveCompletion: onReceiveCompletion(receiveCompletion:),
61+
receiveValue: onReceive(receiveValue:)
62+
)
63+
}
64+
return (.successfullyInitialized, syncEngine)
4265
}
43-
syncEngine?.start(api: api, auth: auth)
44-
completion(.successfulVoid)
4566
}
4667

4768
private func tryGetAPIPlugin() -> APICategoryPlugin? {

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/StorageEngine.swift

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ final class StorageEngine: StorageEngineBehavior {
2727
let validAPIPluginKey: String
2828
let validAuthPluginKey: String
2929
var signInListener: UnsubscribeToken?
30+
let isSyncEnabled: Bool
3031

31-
private let dataStoreConfiguration: DataStoreConfiguration
32+
let dataStoreConfiguration: DataStoreConfiguration
3233
private let operationQueue: OperationQueue
3334

3435
var iSyncEngineSink: Any?
@@ -79,12 +80,15 @@ final class StorageEngine: StorageEngineBehavior {
7980
dataStoreConfiguration: DataStoreConfiguration,
8081
syncEngine: RemoteSyncEngineBehavior?,
8182
validAPIPluginKey: String,
82-
validAuthPluginKey: String) {
83+
validAuthPluginKey: String,
84+
isSyncEnabled: Bool = false
85+
) {
8386
self.storageAdapter = storageAdapter
8487
self.dataStoreConfiguration = dataStoreConfiguration
8588
self.syncEngine = syncEngine
8689
self.validAPIPluginKey = validAPIPluginKey
8790
self.validAuthPluginKey = validAuthPluginKey
91+
self.isSyncEnabled = isSyncEnabled
8892

8993
let operationQueue = OperationQueue()
9094
operationQueue.name = "com.amazonaws.StorageEngine"
@@ -105,27 +109,28 @@ final class StorageEngine: StorageEngineBehavior {
105109

106110
try storageAdapter.setUp(modelSchemas: StorageEngine.systemModelSchemas)
107111
if #available(iOS 13.0, *) {
108-
let syncEngine = isSyncEnabled ? try? RemoteSyncEngine(storageAdapter: storageAdapter,
109-
dataStoreConfiguration: dataStoreConfiguration) : nil
110112
self.init(storageAdapter: storageAdapter,
111113
dataStoreConfiguration: dataStoreConfiguration,
112-
syncEngine: syncEngine,
114+
syncEngine: nil,
113115
validAPIPluginKey: validAPIPluginKey,
114-
validAuthPluginKey: validAuthPluginKey)
116+
validAuthPluginKey: validAuthPluginKey,
117+
isSyncEnabled: isSyncEnabled
118+
)
115119
self.storageEnginePublisher = PassthroughSubject<StorageEngineEvent, DataStoreError>()
116-
syncEngineSink = syncEngine?.publisher.sink(receiveCompletion: onReceiveCompletion(receiveCompletion:),
117-
receiveValue: onReceive(receiveValue:))
120+
118121
} else {
119122
self.init(storageAdapter: storageAdapter,
120123
dataStoreConfiguration: dataStoreConfiguration,
121124
syncEngine: nil,
122125
validAPIPluginKey: validAPIPluginKey,
123-
validAuthPluginKey: validAuthPluginKey)
126+
validAuthPluginKey: validAuthPluginKey,
127+
isSyncEnabled: isSyncEnabled
128+
)
124129
}
125130
}
126131

127132
@available(iOS 13.0, *)
128-
private func onReceiveCompletion(receiveCompletion: Subscribers.Completion<DataStoreError>) {
133+
func onReceiveCompletion(receiveCompletion: Subscribers.Completion<DataStoreError>) {
129134
switch receiveCompletion {
130135
case .failure(let dataStoreError):
131136
storageEnginePublisher.send(completion: .failure(dataStoreError))
@@ -328,6 +333,7 @@ final class StorageEngine: StorageEngineBehavior {
328333
func stopSync(completion: @escaping DataStoreCallback<Void>) {
329334
if let syncEngine = syncEngine {
330335
syncEngine.stop { _ in
336+
self.syncEngine = nil
331337
completion(.successfulVoid)
332338
}
333339
} else {

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/StorageEngineBehavior.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,19 @@ enum StorageEngineEvent {
1717
case readyEvent
1818
}
1919

20+
enum SyncEngineInitResult {
21+
case alreadyInitialized
22+
case successfullyInitialized
23+
case failure(DataStoreError)
24+
}
25+
2026
protocol StorageEngineBehavior: AnyObject, ModelStorageBehavior {
2127

2228
@available(iOS 13.0, *)
2329
var publisher: AnyPublisher<StorageEngineEvent, DataStoreError> { get }
2430

2531
/// start remote sync, based on if sync is enabled and/or authentication is required
26-
func startSync(completion: @escaping DataStoreCallback<Void>)
32+
func startSync() -> Result<SyncEngineInitResult, DataStoreError>
2733
func stopSync(completion: @escaping DataStoreCallback<Void>)
2834
func clear(completion: @escaping DataStoreCallback<Void>)
2935
}

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/RemoteSyncEngine.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,13 @@ class RemoteSyncEngine: RemoteSyncEngineBehavior {
232232
stateMachine.notify(action: .finished)
233233
}
234234

235+
func isSyncing() -> Bool {
236+
if case .notStarted = stateMachine.state {
237+
return false
238+
}
239+
return true
240+
}
241+
235242
func terminate() {
236243
remoteSyncTopicPublisher.send(completion: .finished)
237244
cleanup()

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Sync/RemoteSyncEngineBehavior.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ protocol RemoteSyncEngineBehavior: AnyObject {
4444

4545
func stop(completion: @escaping DataStoreCallback<Void>)
4646

47+
func isSyncing() -> Bool
48+
4749
/// Submits a new mutation for synchronization to the remote API. The response will be handled by the appropriate
4850
/// reconciliation queue
4951
@available(iOS 13.0, *)

0 commit comments

Comments
 (0)