-
Notifications
You must be signed in to change notification settings - Fork 428
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Sync Engine with support for syncing bookmarks (#1724)
Task/Issue URL: https://app.asana.com/0/0/1204606874917765/f Description: Add support for syncing bookmarks
- Loading branch information
Showing
32 changed files
with
584 additions
and
129 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// | ||
// BookmarksCleanupErrorHandling.swift | ||
// DuckDuckGo | ||
// | ||
// Copyright © 2023 DuckDuckGo. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
import Foundation | ||
import Bookmarks | ||
import Common | ||
import Persistence | ||
|
||
public class BookmarksCleanupErrorHandling: EventMapping<BookmarksCleanupError> { | ||
|
||
public init() { | ||
super.init { event, _, _, _ in | ||
let domainEvent = Pixel.Event.bookmarksCleanupFailed | ||
let processedErrors = CoreDataErrorsParser.parse(error: event.coreDataError as NSError) | ||
let params = processedErrors.errorPixelParameters | ||
|
||
Pixel.fire(pixel: domainEvent, error: event.coreDataError, withAdditionalParameters: params) | ||
} | ||
} | ||
|
||
override init(mapping: @escaping EventMapping<BookmarksCleanupError>.Mapping) { | ||
fatalError("Use init()") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
// | ||
// SyncBookmarksAdapter.swift | ||
// DuckDuckGo | ||
// | ||
// Copyright © 2023 DuckDuckGo. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
import Combine | ||
import Common | ||
import DDGSync | ||
import Foundation | ||
import Persistence | ||
import SyncDataProviders | ||
|
||
public final class SyncBookmarksAdapter { | ||
|
||
public private(set) var provider: BookmarksProvider? | ||
|
||
public let syncDidCompletePublisher: AnyPublisher<Void, Never> | ||
|
||
public init() { | ||
syncDidCompletePublisher = syncDidCompleteSubject.eraseToAnyPublisher() | ||
} | ||
|
||
public func setUpProviderIfNeeded(database: CoreDataDatabase, metadataStore: SyncMetadataStore) { | ||
guard provider == nil else { | ||
return | ||
} | ||
do { | ||
let provider = try BookmarksProvider( | ||
database: database, | ||
metadataStore: metadataStore, | ||
reloadBookmarksAfterSync: { [syncDidCompleteSubject] in | ||
syncDidCompleteSubject.send() | ||
} | ||
) | ||
|
||
syncErrorCancellable = provider.syncErrorPublisher | ||
.sink { error in | ||
switch error { | ||
case let syncError as SyncError: | ||
Pixel.fire(pixel: .syncBookmarksFailed, error: syncError) | ||
default: | ||
let nsError = error as NSError | ||
if nsError.domain != NSURLErrorDomain { | ||
let processedErrors = CoreDataErrorsParser.parse(error: error as NSError) | ||
let params = processedErrors.errorPixelParameters | ||
Pixel.fire(pixel: .syncBookmarksFailed, error: error, withAdditionalParameters: params) | ||
} | ||
} | ||
os_log(.error, log: OSLog.syncLog, "Bookmarks Sync error: %{public}s", String(reflecting: error)) | ||
} | ||
self.provider = provider | ||
} catch let error as NSError { | ||
let processedErrors = CoreDataErrorsParser.parse(error: error) | ||
let params = processedErrors.errorPixelParameters | ||
Pixel.fire(pixel: .syncBookmarksProviderInitializationFailed, error: error, withAdditionalParameters: params) | ||
} | ||
} | ||
|
||
private var syncDidCompleteSubject = PassthroughSubject<Void, Never>() | ||
private var syncErrorCancellable: AnyCancellable? | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// | ||
// SyncDataProviders.swift | ||
// DuckDuckGo | ||
// | ||
// Copyright © 2023 DuckDuckGo. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
import Common | ||
import DDGSync | ||
import Persistence | ||
import SyncDataProviders | ||
|
||
public class SyncDataProviders: DataProvidersSource { | ||
public let bookmarksAdapter: SyncBookmarksAdapter | ||
|
||
public func makeDataProviders() -> [DataProviding] { | ||
initializeMetadataDatabaseIfNeeded() | ||
guard let syncMetadata else { | ||
assertionFailure("Sync Metadata not initialized") | ||
return [] | ||
} | ||
|
||
bookmarksAdapter.setUpProviderIfNeeded(database: bookmarksDatabase, metadataStore: syncMetadata) | ||
return [bookmarksAdapter.provider].compactMap { $0 } | ||
} | ||
|
||
public init(bookmarksDatabase: CoreDataDatabase) { | ||
self.bookmarksDatabase = bookmarksDatabase | ||
bookmarksAdapter = SyncBookmarksAdapter() | ||
} | ||
|
||
private func initializeMetadataDatabaseIfNeeded() { | ||
guard !isSyncMetadaDatabaseLoaded else { | ||
return | ||
} | ||
|
||
syncMetadataDatabase.loadStore { context, error in | ||
guard context != nil else { | ||
if let error = error { | ||
Pixel.fire(pixel: .syncMetadataCouldNotLoadDatabase, error: error) | ||
} else { | ||
Pixel.fire(pixel: .syncMetadataCouldNotLoadDatabase) | ||
} | ||
|
||
Thread.sleep(forTimeInterval: 1) | ||
fatalError("Could not create Sync Metadata database stack: \(error?.localizedDescription ?? "err")") | ||
} | ||
} | ||
syncMetadata = LocalSyncMetadataStore(database: syncMetadataDatabase) | ||
isSyncMetadaDatabaseLoaded = true | ||
} | ||
|
||
private var isSyncMetadaDatabaseLoaded: Bool = false | ||
private var syncMetadata: SyncMetadataStore? | ||
|
||
private let syncMetadataDatabase: CoreDataDatabase = SyncMetadataDatabase.make() | ||
private let bookmarksDatabase: CoreDataDatabase | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
// | ||
// SyncMetadataDatabase.swift | ||
// DuckDuckGo | ||
// | ||
// Copyright © 2023 DuckDuckGo. All rights reserved. | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// http://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
// | ||
|
||
import Foundation | ||
import CoreData | ||
import DDGSync | ||
import Persistence | ||
import Common | ||
|
||
public final class SyncMetadataDatabase { | ||
|
||
private init() { } | ||
|
||
public static var defaultDBLocation: URL = { | ||
guard let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first else { | ||
os_log("SyncMetadataDatabase.make - OUT, failed to get location") | ||
fatalError("Failed to get location") | ||
} | ||
return url | ||
}() | ||
|
||
public static func make(location: URL = defaultDBLocation, readOnly: Bool = false) -> CoreDataDatabase { | ||
os_log("SyncMetadataDatabase.make - IN - %s", location.absoluteString) | ||
let bundle = DDGSync.bundle | ||
guard let model = CoreDataDatabase.loadModel(from: bundle, named: "SyncMetadata") else { | ||
os_log("SyncMetadataDatabase.make - OUT, failed to loadModel") | ||
fatalError("Failed to load model") | ||
} | ||
|
||
let db = CoreDataDatabase(name: "SyncMetadata", | ||
containerLocation: location, | ||
model: model, | ||
readOnly: readOnly) | ||
os_log("SyncMetadataDatabase.make - OUT") | ||
return db | ||
} | ||
|
||
} |
Oops, something went wrong.