Skip to content

Commit 2b52849

Browse files
authored
fix(DataStore): Clear API should delete local store (#1685)
* fix(DataStore): clear should be successful if engine is not running * fix(DataStore): Clear API should delete local store
1 parent 8e69c54 commit 2b52849

File tree

4 files changed

+112
-61
lines changed

4 files changed

+112
-61
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/AWSDataStorePlugin+DataStoreBaseBehavior.swift

Lines changed: 45 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
2121
where condition: QueryPredicate? = nil,
2222
completion: @escaping DataStoreCallback<M>) {
2323
log.verbose("Saving: \(model) with condition: \(String(describing: condition))")
24-
reinitStorageEngineIfNeeded()
24+
initStorageEngineAndStartSync()
2525

2626
// TODO: Refactor this into a proper request/result where the result includes metadata like the derived
2727
// mutation type
@@ -63,7 +63,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
6363
public func query<M: Model>(_ modelType: M.Type,
6464
byId id: String,
6565
completion: DataStoreCallback<M?>) {
66-
reinitStorageEngineIfNeeded()
66+
initStorageEngineAndStartSync()
6767
let predicate: QueryPredicate = field("id") == id
6868
query(modelType, where: predicate, paginate: .firstResult) {
6969
switch $0 {
@@ -99,7 +99,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
9999
sort sortInput: [QuerySortDescriptor]? = nil,
100100
paginate paginationInput: QueryPaginationInput? = nil,
101101
completion: DataStoreCallback<[M]>) {
102-
reinitStorageEngineIfNeeded()
102+
initStorageEngineAndStartSync()
103103
storageEngine.query(modelType,
104104
modelSchema: modelSchema,
105105
predicate: predicate,
@@ -120,7 +120,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
120120
withId id: String,
121121
where predicate: QueryPredicate? = nil,
122122
completion: @escaping DataStoreCallback<Void>) {
123-
reinitStorageEngineIfNeeded()
123+
initStorageEngineAndStartSync()
124124
storageEngine.delete(modelType, modelSchema: modelSchema, withId: id, predicate: predicate) { result in
125125
self.onDeleteCompletion(result: result, modelSchema: modelSchema, completion: completion)
126126
}
@@ -136,7 +136,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
136136
modelSchema: ModelSchema,
137137
where predicate: QueryPredicate? = nil,
138138
completion: @escaping DataStoreCallback<Void>) {
139-
reinitStorageEngineIfNeeded()
139+
initStorageEngineAndStartSync()
140140
storageEngine.delete(type(of: model),
141141
modelSchema: modelSchema,
142142
withId: model.id,
@@ -155,7 +155,7 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
155155
modelSchema: ModelSchema,
156156
where predicate: QueryPredicate,
157157
completion: @escaping DataStoreCallback<Void>) {
158-
reinitStorageEngineIfNeeded()
158+
initStorageEngineAndStartSync()
159159
let onCompletion: DataStoreCallback<[M]> = { result in
160160
switch result {
161161
case .success(let models):
@@ -174,52 +174,57 @@ extension AWSDataStorePlugin: DataStoreBaseBehavior {
174174
}
175175

176176
public func start(completion: @escaping DataStoreCallback<Void>) {
177-
reinitStorageEngineIfNeeded { result in
177+
initStorageEngineAndStartSync { result in
178178
completion(result)
179179
}
180180
}
181181

182182
public func stop(completion: @escaping DataStoreCallback<Void>) {
183-
storageEngineInitSemaphore.wait()
184-
operationQueue.operations.forEach { operation in
185-
if let operation = operation as? DataStoreObserveQueryOperation {
186-
operation.resetState()
183+
storageEngineInitQueue.sync {
184+
operationQueue.operations.forEach { operation in
185+
if let operation = operation as? DataStoreObserveQueryOperation {
186+
operation.resetState()
187+
}
188+
}
189+
dispatchedModelSyncedEvents.forEach { _, dispatchedModelSynced in
190+
dispatchedModelSynced.set(false)
191+
}
192+
if storageEngine == nil {
193+
194+
completion(.successfulVoid)
195+
return
196+
}
197+
198+
storageEngine.stopSync { result in
199+
self.storageEngine = nil
200+
completion(result)
187201
}
188-
}
189-
dispatchedModelSyncedEvents.forEach { _, dispatchedModelSynced in
190-
dispatchedModelSynced.set(false)
191-
}
192-
if storageEngine == nil {
193-
storageEngineInitSemaphore.signal()
194-
completion(.successfulVoid)
195-
return
196-
}
197-
storageEngineInitSemaphore.signal()
198-
storageEngine.stopSync { result in
199-
self.storageEngine = nil
200-
completion(result)
201202
}
202203
}
203204

204205
public func clear(completion: @escaping DataStoreCallback<Void>) {
205-
storageEngineInitSemaphore.wait()
206-
operationQueue.operations.forEach { operation in
207-
if let operation = operation as? DataStoreObserveQueryOperation {
208-
operation.resetState()
209-
}
210-
}
211-
dispatchedModelSyncedEvents.forEach { _, dispatchedModelSynced in
212-
dispatchedModelSynced.set(false)
213-
}
214-
if storageEngine == nil {
215-
storageEngineInitSemaphore.signal()
216-
completion(.successfulVoid)
206+
if case let .failure(error) = initStorageEngine() {
207+
completion(.failure(causedBy: error))
217208
return
218209
}
219-
storageEngineInitSemaphore.signal()
220-
storageEngine.clear { result in
221-
self.storageEngine = nil
222-
completion(result)
210+
211+
storageEngineInitQueue.sync {
212+
operationQueue.operations.forEach { operation in
213+
if let operation = operation as? DataStoreObserveQueryOperation {
214+
operation.resetState()
215+
}
216+
}
217+
dispatchedModelSyncedEvents.forEach { _, dispatchedModelSynced in
218+
dispatchedModelSynced.set(false)
219+
}
220+
if storageEngine == nil {
221+
completion(.successfulVoid)
222+
return
223+
}
224+
storageEngine.clear { result in
225+
self.storageEngine = nil
226+
completion(result)
227+
}
223228
}
224229
}
225230

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/AWSDataStorePlugin+DataStoreSubscribeBehavior.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ extension AWSDataStorePlugin: DataStoreSubscribeBehavior {
1212

1313
@available(iOS 13.0, *)
1414
public var publisher: AnyPublisher<MutationEvent, DataStoreError> {
15-
reinitStorageEngineIfNeeded()
15+
initStorageEngineAndStartSync()
1616
// Force-unwrapping: The optional 'dataStorePublisher' is expected
1717
// to exist for deployment targets >=iOS13.0
1818
return dataStorePublisher!.publisher
@@ -45,7 +45,7 @@ extension AWSDataStorePlugin: DataStoreSubscribeBehavior {
4545
where predicate: QueryPredicate? = nil,
4646
sort sortInput: [QuerySortDescriptor]? = nil)
4747
-> AnyPublisher<DataStoreQuerySnapshot<M>, DataStoreError> {
48-
reinitStorageEngineIfNeeded()
48+
initStorageEngineAndStartSync()
4949

5050
guard let dataStorePublisher = dataStorePublisher else {
5151
return Fail(error: DataStoreError.unknown(

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/AWSDataStorePlugin.swift

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
3939
let validAuthPluginKey: String
4040

4141
var storageEngine: StorageEngineBehavior!
42-
var storageEngineInitSemaphore: DispatchSemaphore
42+
var storageEngineInitQueue = DispatchQueue(label: "AWSDataStorePlugin.storageEngineInitQueue")
4343
var storageEngineBehaviorFactory: StorageEngineBehaviorFactory
4444

4545
var iStorageEngineSink: Any?
@@ -73,7 +73,6 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
7373
self.dataStorePublisher = nil
7474
}
7575
self.dispatchedModelSyncedEvents = [:]
76-
self.storageEngineInitSemaphore = DispatchSemaphore(value: 1)
7776
}
7877

7978
/// Internal initializer for testing
@@ -94,7 +93,6 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
9493
self.dispatchedModelSyncedEvents = [:]
9594
self.validAPIPluginKey = validAPIPluginKey
9695
self.validAuthPluginKey = validAuthPluginKey
97-
self.storageEngineInitSemaphore = DispatchSemaphore(value: 1)
9896
}
9997

10098
/// By the time this method gets called, DataStore will already have invoked
@@ -109,23 +107,44 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
109107
ModelListDecoderRegistry.registerDecoder(DataStoreListDecoder.self)
110108
}
111109

112-
func reinitStorageEngineIfNeeded(completion: @escaping DataStoreCallback<Void> = {_ in}) {
113-
storageEngineInitSemaphore.wait()
110+
/// Initializes the underlying storage engine
111+
/// - Returns: success if the engine is successfully initialized or
112+
/// a failure with a DataStoreError
113+
func initStorageEngine() -> DataStoreResult<Void> {
114+
storageEngineInitQueue.sync {
115+
if storageEngine != nil {
116+
return .successfulVoid
117+
}
118+
var result: DataStoreResult<Void>
119+
do {
120+
if #available(iOS 13.0, *) {
121+
if self.dataStorePublisher == nil {
122+
self.dataStorePublisher = DataStorePublisher()
123+
}
124+
}
125+
try resolveStorageEngine(dataStoreConfiguration: dataStoreConfiguration)
126+
try storageEngine.setUp(modelSchemas: ModelRegistry.modelSchemas)
127+
try storageEngine.applyModelMigrations(modelSchemas: ModelRegistry.modelSchemas)
128+
result = .successfulVoid
129+
} catch {
130+
result = .failure(causedBy: error)
131+
log.error(error: error)
132+
}
133+
return result
134+
}
135+
}
136+
137+
/// Initializes the underlying storage engine and starts the syncing process
138+
/// - Parameter completion: completion handler called with a success if the sync process started
139+
/// or with a DataStoreError in case of failure
140+
func initStorageEngineAndStartSync(completion: @escaping DataStoreCallback<Void> = { _ in }) {
114141
if storageEngine != nil {
115-
storageEngineInitSemaphore.signal()
116142
completion(.successfulVoid)
117143
return
118144
}
119-
do {
120-
if #available(iOS 13.0, *) {
121-
if self.dataStorePublisher == nil {
122-
self.dataStorePublisher = DataStorePublisher()
123-
}
124-
}
125-
try resolveStorageEngine(dataStoreConfiguration: dataStoreConfiguration)
126-
try storageEngine.setUp(modelSchemas: ModelRegistry.modelSchemas)
127-
try storageEngine.applyModelMigrations(modelSchemas: ModelRegistry.modelSchemas)
128-
storageEngineInitSemaphore.signal()
145+
146+
switch initStorageEngine() {
147+
case .success:
129148
storageEngine.startSync { result in
130149

131150
self.operationQueue.operations.forEach { operation in
@@ -135,10 +154,8 @@ final public class AWSDataStorePlugin: DataStoreCategoryPlugin {
135154
}
136155
completion(result)
137156
}
138-
} catch {
139-
storageEngineInitSemaphore.signal()
157+
case .failure(let error):
140158
completion(.failure(causedBy: error))
141-
log.error(error: error)
142159
}
143160
}
144161

AmplifyPlugins/DataStore/AWSDataStoreCategoryPluginTests/Core/AWSAPICategoryPluginTests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,4 +460,33 @@ class AWSAPICategoryPluginTests: XCTestCase {
460460
XCTFail("DataStore configuration should not fail with nil configuration. \(error)")
461461
}
462462
}
463+
464+
/// - Given: Datastore plugin is NOT initialized
465+
/// - When:
466+
/// - plugin.clear() is called
467+
/// - Then: StorageEngine.clear is called
468+
func testClearStorageWhenEngineIsNotStarted() {
469+
let storageEngine = MockStorageEngineBehavior()
470+
let pluginClearExpectation = expectation(description: "DataStore plugin .clear should called")
471+
let storageClearExpectation = expectation(description: "StorageEngine .clear should be called")
472+
storageEngine.responders[.clear] = ClearResponder { _ in
473+
storageClearExpectation.fulfill()
474+
}
475+
let storageEngineBehaviorFactory: StorageEngineBehaviorFactory = {_, _, _, _, _, _ throws in
476+
return storageEngine
477+
}
478+
let dataStorePublisher = DataStorePublisher()
479+
let plugin = AWSDataStorePlugin(modelRegistration: TestModelRegistration(),
480+
storageEngineBehaviorFactory: storageEngineBehaviorFactory,
481+
dataStorePublisher: dataStorePublisher,
482+
validAPIPluginKey: "MockAPICategoryPlugin",
483+
validAuthPluginKey: "MockAuthCategoryPlugin")
484+
485+
plugin.clear {
486+
if case .success = $0 {
487+
pluginClearExpectation.fulfill()
488+
}
489+
}
490+
waitForExpectations(timeout: 1.0)
491+
}
463492
}

0 commit comments

Comments
 (0)