From 8ee53621eeffbf1b486a6c2ea9d1291eb7f6a072 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Fri, 19 Jul 2024 11:07:39 +0200 Subject: [PATCH 01/10] Add info about bookmarks structure not being recovered --- Core/BookmarksStateValidation.swift | 11 ++++++++--- Core/PixelEvent.swift | 2 ++ DuckDuckGo/BookmarksDatabaseSetup.swift | 9 ++++++++- DuckDuckGoTests/BookmarksStateValidationTests.swift | 10 +++++----- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Core/BookmarksStateValidation.swift b/Core/BookmarksStateValidation.swift index fc02e527f8..774b1fa943 100644 --- a/Core/BookmarksStateValidation.swift +++ b/Core/BookmarksStateValidation.swift @@ -30,6 +30,7 @@ public class BookmarksStateValidation { public enum ValidationError { case bookmarksStructureLost + case bookmarksStructureNotRecovered case bookmarksStructureBroken(additionalParams: [String: String]) case validatorError(Error) } @@ -43,18 +44,22 @@ public class BookmarksStateValidation { self.errorHandler = errorHandler } - public func validateInitialState(context: NSManagedObjectContext) { - guard keyValueStore.object(forKey: Constants.bookmarksDBIsInitialized) != nil else { return } + public func validateInitialState(context: NSManagedObjectContext, + validationError: ValidationError = .bookmarksStructureLost) -> Bool { + guard keyValueStore.object(forKey: Constants.bookmarksDBIsInitialized) != nil else { return true } let fetch = BookmarkEntity.fetchRequest() do { let count = try context.count(for: fetch) if count == 0 { - errorHandler(.bookmarksStructureLost) + errorHandler(validationError) + return false } } catch { errorHandler(.validatorError(error)) } + + return true } public func validateBookmarksStructure(context: NSManagedObjectContext) { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index 5478cdfe88..e973d1fbaf 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -514,6 +514,7 @@ extension Pixel { case adAttributionLogicWrongVendorOnFailedCompilation case debugBookmarksStructureLost + case debugBookmarksStructureNotRecovered case debugBookmarksInvalidRoots case debugBookmarksValidationFailed @@ -1229,6 +1230,7 @@ extension Pixel.Event { case .emailAutofillKeychainError: return "m_email_autofill_keychain_error" case .debugBookmarksStructureLost: return "m_d_bookmarks_structure_lost" + case .debugBookmarksStructureNotRecovered: return "m_d_bookmarks_structure_not_recovered" case .debugBookmarksInvalidRoots: return "m_d_bookmarks_invalid_roots" case .debugBookmarksValidationFailed: return "m_d_bookmarks_validation_failed" diff --git a/DuckDuckGo/BookmarksDatabaseSetup.swift b/DuckDuckGo/BookmarksDatabaseSetup.swift index 93364b97ba..c6cc647e56 100644 --- a/DuckDuckGo/BookmarksDatabaseSetup.swift +++ b/DuckDuckGo/BookmarksDatabaseSetup.swift @@ -44,6 +44,8 @@ struct BookmarksDatabaseSetup { switch validationError { case .bookmarksStructureLost: DailyPixel.fire(pixel: .debugBookmarksStructureLost, includedParameters: [.appVersion]) + case .bookmarksStructureNotRecovered: + DailyPixel.fire(pixel: .debugBookmarksStructureNotRecovered, includedParameters: [.appVersion]) case .bookmarksStructureBroken(let additionalParams): DailyPixel.fire(pixel: .debugBookmarksInvalidRoots, withAdditionalParameters: additionalParams, @@ -61,10 +63,15 @@ struct BookmarksDatabaseSetup { bookmarksDatabase.loadStore { context, error in guard let context = assertContext(context, error, crashOnError) else { return } - validator.validateInitialState(context: context) + let isMissingStructure = validator.validateInitialState(context: context) self.migrateFromLegacyCoreDataStorageIfNeeded(context) migrationHappened = self.migrateToFormFactorSpecificFavorites(context, oldFavoritesOrder) + + if isMissingStructure { + _ = validator.validateInitialState(context: context, + validationError: .bookmarksStructureNotRecovered) + } // Add new migrations and set migrationHappened flag here. Only the last migration is relevant. // Also bump the int passed to the assert function below. } diff --git a/DuckDuckGoTests/BookmarksStateValidationTests.swift b/DuckDuckGoTests/BookmarksStateValidationTests.swift index d4827aef31..ffe61497ee 100644 --- a/DuckDuckGoTests/BookmarksStateValidationTests.swift +++ b/DuckDuckGoTests/BookmarksStateValidationTests.swift @@ -70,7 +70,7 @@ class BookmarksStateValidationTests: XCTestCase { let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - validator.validateInitialState(context: testContext) + XCTAssertTrue(validator.validateInitialState(context: testContext)) validator.validateBookmarksStructure(context: testContext) } } @@ -83,7 +83,7 @@ class BookmarksStateValidationTests: XCTestCase { let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - validator.validateInitialState(context: testContext) + XCTAssertTrue(validator.validateInitialState(context: testContext)) } } @@ -107,7 +107,7 @@ class BookmarksStateValidationTests: XCTestCase { let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - validator.validateInitialState(context: testContext) + XCTAssertFalse(validator.validateInitialState(context: testContext)) validator.validateBookmarksStructure(context: testContext) } @@ -148,7 +148,7 @@ class BookmarksStateValidationTests: XCTestCase { let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - validator.validateInitialState(context: testContext) + XCTAssertTrue(validator.validateInitialState(context: testContext)) validator.validateBookmarksStructure(context: testContext) } @@ -188,7 +188,7 @@ class BookmarksStateValidationTests: XCTestCase { let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - validator.validateInitialState(context: testContext) + XCTAssertTrue(validator.validateInitialState(context: testContext)) validator.validateBookmarksStructure(context: testContext) } From 36ade27da825bc60d6f8d4b72d21d4104d2707ff Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Fri, 19 Jul 2024 11:33:27 +0200 Subject: [PATCH 02/10] Remove old pixel --- Core/PixelEvent.swift | 4 ---- DuckDuckGo/AppDelegate.swift | 4 ---- 2 files changed, 8 deletions(-) diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index e973d1fbaf..d35e657a52 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -590,8 +590,6 @@ extension Pixel { case syncDeleteAccountError case syncLoginExistingAccountError - case syncWrongEnvironment - case swipeTabsUsedDaily case swipeToOpenNewTab @@ -1314,8 +1312,6 @@ extension Pixel.Event { case .syncDeleteAccountError: return "m_d_sync_delete_account_error" case .syncLoginExistingAccountError: return "m_d_sync_login_existing_account_error" - case .syncWrongEnvironment: return "m_d_sync_wrong_environment_u" - case .swipeTabsUsedDaily: return "m_swipe-tabs-used-daily" case .swipeToOpenNewTab: return "m_addressbar_swipe_new_tab" diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 69dc0b2f90..15f22fe1bd 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -476,10 +476,6 @@ import WebKit guard !testing else { return } syncService.initializeIfNeeded() - if syncService.authState == .active && - (InternalUserStore().isInternalUser == false && syncService.serverEnvironment == .development) { - UniquePixel.fire(pixel: .syncWrongEnvironment) - } syncDataProviders.setUpDatabaseCleanersIfNeeded(syncService: syncService) if !(overlayWindow?.rootViewController is AuthenticationViewController) { From faad9022ac43817cfa67195c159e3dde60032244 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 25 Jul 2024 15:37:14 +0200 Subject: [PATCH 03/10] Improve setup code, add tests --- Core/BookmarksStateValidation.swift | 12 +- DuckDuckGo.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/swiftpm/Package.resolved | 4 +- DuckDuckGo/AppDelegate.swift | 78 ++++-- DuckDuckGo/BookmarksDatabaseSetup.swift | 123 +++++---- .../BookmarksDatabaseSetupTests.swift | 253 ++++++++++++++++++ .../BookmarksStateValidationTests.swift | 28 +- 7 files changed, 398 insertions(+), 108 deletions(-) create mode 100644 DuckDuckGoTests/BookmarksDatabaseSetupTests.swift diff --git a/Core/BookmarksStateValidation.swift b/Core/BookmarksStateValidation.swift index 774b1fa943..2ca6bcab60 100644 --- a/Core/BookmarksStateValidation.swift +++ b/Core/BookmarksStateValidation.swift @@ -22,7 +22,15 @@ import CoreData import Bookmarks import Persistence -public class BookmarksStateValidation { +public protocol BookmarksStateValidation { + + func validateInitialState(context: NSManagedObjectContext, + validationError: BookmarksStateValidator.ValidationError) -> Bool + + func validateBookmarksStructure(context: NSManagedObjectContext) +} + +public class BookmarksStateValidator: BookmarksStateValidation { enum Constants { static let bookmarksDBIsInitialized = "bookmarksDBIsInitialized" @@ -45,7 +53,7 @@ public class BookmarksStateValidation { } public func validateInitialState(context: NSManagedObjectContext, - validationError: ValidationError = .bookmarksStructureLost) -> Bool { + validationError: ValidationError) -> Bool { guard keyValueStore.object(forKey: Constants.bookmarksDBIsInitialized) != nil else { return true } let fetch = BookmarkEntity.fetchRequest() diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index fdf25c9c95..89ff48abf4 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -568,6 +568,7 @@ 987130C7294AAB9F00AB05E0 /* MenuBookmarksViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C1294AAB9E00AB05E0 /* MenuBookmarksViewModelTests.swift */; }; 987130C8294AAB9F00AB05E0 /* BookmarksTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C2294AAB9E00AB05E0 /* BookmarksTestHelpers.swift */; }; 987130C9294AAB9F00AB05E0 /* BookmarkUtilsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987130C3294AAB9E00AB05E0 /* BookmarkUtilsTests.swift */; }; + 987243142C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 987243132C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift */; }; 9872D205247DCAC100CEF398 /* TabPreviewsSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9872D204247DCAC100CEF398 /* TabPreviewsSource.swift */; }; 9874F9EE2187AFCE00CAF33D /* Themable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9874F9ED2187AFCE00CAF33D /* Themable.swift */; }; 9875E00722316B8400B1373F /* Instruments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9875E00622316B8400B1373F /* Instruments.swift */; }; @@ -2161,6 +2162,7 @@ 987130C1294AAB9E00AB05E0 /* MenuBookmarksViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuBookmarksViewModelTests.swift; sourceTree = ""; }; 987130C2294AAB9E00AB05E0 /* BookmarksTestHelpers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksTestHelpers.swift; sourceTree = ""; }; 987130C3294AAB9E00AB05E0 /* BookmarkUtilsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkUtilsTests.swift; sourceTree = ""; }; + 987243132C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksDatabaseSetupTests.swift; sourceTree = ""; }; 9872D204247DCAC100CEF398 /* TabPreviewsSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabPreviewsSource.swift; sourceTree = ""; }; 9874F9ED2187AFCE00CAF33D /* Themable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Themable.swift; sourceTree = ""; }; 9875E00622316B8400B1373F /* Instruments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Instruments.swift; sourceTree = ""; }; @@ -5363,6 +5365,7 @@ 85BA58561F34F61C00C6E8CA /* AppUserDefaultsTests.swift */, 4B62C4B925B930DD008912C6 /* AppConfigurationFetchTests.swift */, 85AFA1202B45D14F0028A504 /* BookmarksMigrationAssertionTests.swift */, + 987243132C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift */, ); name = Application; sourceTree = ""; @@ -7220,6 +7223,7 @@ EEFE9C732A603CE9005B0A26 /* NetworkProtectionStatusViewModelTests.swift in Sources */, F13B4BF91F18CA0600814661 /* TabsModelTests.swift in Sources */, F1BDDBFD2C340D9C00459306 /* SubscriptionContainerViewModelTests.swift in Sources */, + 987243142C5232B5007ECC76 /* BookmarksDatabaseSetupTests.swift in Sources */, 98B31290218CCB2200E54DE1 /* MockDependencyProvider.swift in Sources */, CBDD5DDF29A6736A00832877 /* APIHeadersTests.swift in Sources */, 986B45D0299E30A50089D2D7 /* BookmarkEntityTests.swift in Sources */, @@ -10181,8 +10185,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = exactVersion; - version = 174.0.1; + branch = "bartek/data-debugging"; + kind = branch; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 32c4b77aaa..e5b15658f0 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "63195a30b02e05d1b7176c76ebc55c2cff66c9c8", - "version" : "174.0.1" + "branch" : "bartek/data-debugging", + "revision" : "49bc707ac1f947b36d0f3d74ac30a158123656bf" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 15f22fe1bd..60d541021a 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -176,7 +176,7 @@ import WebKit Pixel.isDryRun = true _ = DefaultUserAgentManager.shared Database.shared.loadStore { _, _ in } - _ = BookmarksDatabaseSetup(crashOnError: true).loadStoreAndMigrate(bookmarksDatabase: bookmarksDatabase) + _ = BookmarksDatabaseSetup().loadStoreAndMigrate(bookmarksDatabase: bookmarksDatabase) window?.rootViewController = UIStoryboard.init(name: "LaunchScreen", bundle: nil).instantiateInitialViewController() return true } @@ -215,9 +215,18 @@ import WebKit DatabaseMigration.migrate(to: context) } - if BookmarksDatabaseSetup(crashOnError: !shouldPresentInsufficientDiskSpaceAlertAndCrash) - .loadStoreAndMigrate(bookmarksDatabase: bookmarksDatabase) { - // MARK: post-Bookmarks migration logic + switch BookmarksDatabaseSetup().loadStoreAndMigrate(bookmarksDatabase: bookmarksDatabase) { + case .success: + break + case .failure(let error): + Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase, + error: error) + if error.isDiskFull { + shouldPresentInsufficientDiskSpaceAlertAndCrash = true + } else { + Thread.sleep(forTimeInterval: 1) + fatalError("Could not create database stack: \(error.localizedDescription)") + } } WidgetCenter.shared.reloadAllTimelines() @@ -309,33 +318,38 @@ import WebKit let historyManager = makeHistoryManager() let tabsModel = prepareTabsModel(previewsSource: previewsSource) - let main = MainViewController(bookmarksDatabase: bookmarksDatabase, - bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, - historyManager: historyManager, - homePageConfiguration: homePageConfiguration, - syncService: syncService, - syncDataProviders: syncDataProviders, - appSettings: AppDependencyProvider.shared.appSettings, - previewsSource: previewsSource, - tabsModel: tabsModel, - syncPausedStateManager: syncErrorHandler) - - main.loadViewIfNeeded() - syncErrorHandler.alertPresenter = main - - window = UIWindow(frame: UIScreen.main.bounds) - window?.rootViewController = main - window?.makeKeyAndVisible() - if shouldPresentInsufficientDiskSpaceAlertAndCrash { + + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = BlankSnapshotViewController(appSettings: AppDependencyProvider.shared.appSettings) + window?.makeKeyAndVisible() + presentInsufficientDiskSpaceAlert() - } + } else { + let main = MainViewController(bookmarksDatabase: bookmarksDatabase, + bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, + historyManager: historyManager, + homePageConfiguration: homePageConfiguration, + syncService: syncService, + syncDataProviders: syncDataProviders, + appSettings: AppDependencyProvider.shared.appSettings, + previewsSource: previewsSource, + tabsModel: tabsModel, + syncPausedStateManager: syncErrorHandler) + + main.loadViewIfNeeded() + syncErrorHandler.alertPresenter = main + + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = main + window?.makeKeyAndVisible() - autoClear = AutoClear(worker: main) - let applicationState = application.applicationState - Task { - await autoClear?.clearDataIfEnabled(applicationState: .init(with: applicationState)) - await vpnWorkaround.installRedditSessionWorkaround() + autoClear = AutoClear(worker: main) + let applicationState = application.applicationState + Task { + await autoClear?.clearDataIfEnabled(applicationState: .init(with: applicationState)) + await vpnWorkaround.installRedditSessionWorkaround() + } } AppDependencyProvider.shared.voiceSearchHelper.migrateSettingsFlagIfNecessary() @@ -390,7 +404,8 @@ import WebKit return NullHistoryManager() case .success(let historyManager): - return historyManager + self.presentPreemptiveCrashAlert() + return NullHistoryManager() } } @@ -1054,6 +1069,11 @@ private extension Error { if let underlyingError = nsError.userInfo["NSUnderlyingError"] as? NSError, underlyingError.code == 13 { return true } + + if nsError.userInfo["NSSQLiteErrorDomain"] as? Int == 13 { + return true + } + return false } diff --git a/DuckDuckGo/BookmarksDatabaseSetup.swift b/DuckDuckGo/BookmarksDatabaseSetup.swift index c6cc647e56..fcd83415fe 100644 --- a/DuckDuckGo/BookmarksDatabaseSetup.swift +++ b/DuckDuckGo/BookmarksDatabaseSetup.swift @@ -26,21 +26,19 @@ import Common struct BookmarksDatabaseSetup { - let crashOnError: Bool - - private let migrationAssertion = BookmarksMigrationAssertion() + enum Result { + case success + case failure(Error) + } - func loadStoreAndMigrate(bookmarksDatabase: CoreDataDatabase) -> Bool { - let preMigrationErrorHandling = createErrorHandling() + private let migrationAssertion: BookmarksMigrationAssertion - let oldFavoritesOrder = BookmarkFormFactorFavoritesMigration - .getFavoritesOrderFromPreV4Model( - dbContainerLocation: BookmarksDatabase.defaultDBLocation, - dbFileURL: BookmarksDatabase.defaultDBFileURL, - errorEvents: preMigrationErrorHandling - ) + init(migrationAssertion: BookmarksMigrationAssertion = BookmarksMigrationAssertion()) { + self.migrationAssertion = migrationAssertion + } - let validator = BookmarksStateValidation(keyValueStore: UserDefaults.app) { validationError in + static func makeValidator() -> BookmarksStateValidator { + return BookmarksStateValidator(keyValueStore: UserDefaults.app) { validationError in switch validationError { case .bookmarksStructureLost: DailyPixel.fire(pixel: .debugBookmarksStructureLost, includedParameters: [.appVersion]) @@ -58,12 +56,33 @@ struct BookmarksDatabaseSetup { includedParameters: [.appVersion]) } } + } + + func loadStoreAndMigrate(bookmarksDatabase: CoreDataStoring, + formFactorFavoritesMigrator: BookmarkFormFactorFavoritesMigrating = BookmarkFormFactorFavoritesMigration(), + validator: BookmarksStateValidation = Self.makeValidator()) -> Result { + + let oldFavoritesOrder: [String]? + do { + oldFavoritesOrder = try formFactorFavoritesMigrator.getFavoritesOrderFromPreV4Model( + dbContainerLocation: BookmarksDatabase.defaultDBLocation, + dbFileURL: BookmarksDatabase.defaultDBFileURL + ) + } catch { + return .failure(error) + } var migrationHappened = false + var loadError: Error? bookmarksDatabase.loadStore { context, error in - guard let context = assertContext(context, error, crashOnError) else { return } + guard let context = context, error == nil else { + loadError = error + return + } - let isMissingStructure = validator.validateInitialState(context: context) + // Perform pre-setup/migration validation + let isMissingStructure = !validator.validateInitialState(context: context, + validationError: .bookmarksStructureLost) self.migrateFromLegacyCoreDataStorageIfNeeded(context) migrationHappened = self.migrateToFormFactorSpecificFavorites(context, oldFavoritesOrder) @@ -72,10 +91,16 @@ struct BookmarksDatabaseSetup { _ = validator.validateInitialState(context: context, validationError: .bookmarksStructureNotRecovered) } - // Add new migrations and set migrationHappened flag here. Only the last migration is relevant. + + // Add new migrations and set migrationHappened flag above this comment. Only the last migration is relevant. // Also bump the int passed to the assert function below. } + if let loadError { + return .failure(loadError) + } + + // Perform post-setup validation let contextForValidation = bookmarksDatabase.makeContext(concurrencyType: .privateQueueConcurrencyType) contextForValidation.performAndWait { validator.validateBookmarksStructure(context: contextForValidation) @@ -90,7 +115,7 @@ struct BookmarksDatabaseSetup { } } - return migrationHappened + return .success } private func repairDeletedFlag(context: NSManagedObjectContext) { @@ -132,57 +157,37 @@ struct BookmarksDatabaseSetup { LegacyBookmarksStoreMigration.migrate(from: legacyStorage, to: context) legacyStorage?.removeStore() } - - private func assertContext(_ context: NSManagedObjectContext?, _ error: Error?, _ crashOnError: Bool) -> NSManagedObjectContext? { - guard let context = context else { - if let error = error { - Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase, - error: error) - } else { - Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase) - } +} - if !crashOnError { - return nil - } else { - Thread.sleep(forTimeInterval: 1) - fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") - } - } - return context +class BookmarksMigrationAssertion { + + enum Error: Swift.Error { + case unexpectedMigration } - private func createErrorHandling() -> EventMapping { - return EventMapping { _, error, _, _ in - if let error = error { - Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase, - error: error) - } else { - Pixel.fire(pixel: .bookmarksCouldNotLoadDatabase) - } + let store: KeyValueStoring - if !crashOnError { - return - } else { - Thread.sleep(forTimeInterval: 1) - fatalError("Could not create Bookmarks database stack: \(error?.localizedDescription ?? "err")") - } + init(store: KeyValueStoring = UserDefaults.app) { + self.store = store + } + + var lastGoodVersion: String? { + get { + return store.object(forKey: UserDefaultsWrapper.Key.bookmarksLastGoodVersion.rawValue) as? String + } + set { + store.set(newValue, forKey: UserDefaultsWrapper.Key.bookmarksLastGoodVersion.rawValue) } } - -} -class BookmarksMigrationAssertion { - - enum Error: Swift.Error { - case unexpectedMigration + var migrationVersion: Int { + get { + return (store.object(forKey: UserDefaultsWrapper.Key.bookmarksMigrationVersion.rawValue) as? Int) ?? 0 + } + set { + store.set(newValue, forKey: UserDefaultsWrapper.Key.bookmarksMigrationVersion.rawValue) + } } - - @UserDefaultsWrapper(key: .bookmarksLastGoodVersion, defaultValue: nil) - var lastGoodVersion: String? - - @UserDefaultsWrapper(key: .bookmarksMigrationVersion, defaultValue: 0) - var migrationVersion: Int // Wanted to use assertions here, but that's trick to test. func assert(migrationVersion: Int) throws { diff --git a/DuckDuckGoTests/BookmarksDatabaseSetupTests.swift b/DuckDuckGoTests/BookmarksDatabaseSetupTests.swift new file mode 100644 index 0000000000..b7ef842a95 --- /dev/null +++ b/DuckDuckGoTests/BookmarksDatabaseSetupTests.swift @@ -0,0 +1,253 @@ +// +// BookmarksDatabaseSetupTests.swift +// DuckDuckGo +// +// Copyright © 2024 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 XCTest +import Persistence +import CoreData +import Bookmarks +@testable import DuckDuckGo +@testable import Core +import TestUtils + +class DummyCoreDataStoreMock: CoreDataStoring { + + init() { + model = NSManagedObjectModel() + coordinator = NSPersistentStoreCoordinator() + } + + var isDatabaseFileInitialized: Bool = false + var model: NSManagedObjectModel + var coordinator: NSPersistentStoreCoordinator + + var onLoadStore: ((NSManagedObjectContext?, Error?) -> Void) -> Void = { _ in } + func loadStore(completion: @escaping (NSManagedObjectContext?, Error?) -> Void) { + onLoadStore(completion) + } + + var onMakeContext = { } + func makeContext(concurrencyType: NSManagedObjectContextConcurrencyType, name: String?) -> NSManagedObjectContext { + onMakeContext() + + return NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType) + } +} + +class CoreDataStoreMock: CoreDataStoring { + + let db: CoreDataDatabase + init(db: CoreDataDatabase) { + self.db = db + } + + var isDatabaseFileInitialized: Bool = false + var model: NSManagedObjectModel { + db.model + } + var coordinator: NSPersistentStoreCoordinator { + db.coordinator + } + + var onLoadStore: () -> Void = { } + func loadStore(completion: @escaping (NSManagedObjectContext?, Error?) -> Void) { + onLoadStore() + db.loadStore(completion: completion) + } + + var onMakeContext = { } + func makeContext(concurrencyType: NSManagedObjectContextConcurrencyType, name: String?) -> NSManagedObjectContext { + onMakeContext() + return db.makeContext(concurrencyType: concurrencyType, name: name) + } +} + +class FormFactorMigratingMock: BookmarkFormFactorFavoritesMigrating { + + var onGetFavs: () throws -> [String]? = { return nil } + func getFavoritesOrderFromPreV4Model(dbContainerLocation: URL, dbFileURL: URL) throws -> [String]? { + try onGetFavs() + } +} + +class BookmarksStateValidationMock: BookmarksStateValidation { + + var onValidateInitialState: () -> Bool = { return true } + func validateInitialState(context: NSManagedObjectContext, validationError: Core.BookmarksStateValidator.ValidationError) -> Bool { + onValidateInitialState() + } + + var onValidateBookmarksStructure: () -> Void = { } + func validateBookmarksStructure(context: NSManagedObjectContext) { + onValidateBookmarksStructure() + } +} + +class BookmarksDatabaseSetupTests: XCTestCase { + + let validatorMock = BookmarksStateValidationMock() + let ffMock = FormFactorMigratingMock() + + func setUpValidBookmarksDatabase() -> CoreDataDatabase? { + let location = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString) + + let bundle = Bookmarks.bundle + guard let model = CoreDataDatabase.loadModel(from: bundle, named: "BookmarksModel") else { + XCTFail("Failed to load model") + return nil + } + return CoreDataDatabase(name: type(of: self).description(), + containerLocation: location, + model: model) + } + + func testWhenDatabaseLoadsCorrectlyThenValidationIsPerformed() { + + guard let bookmarksDB = setUpValidBookmarksDatabase() else { + XCTFail("Could not create DB") + return + } + + let dbMock = CoreDataStoreMock(db: bookmarksDB) + + let storeLoaded = expectation(description: "store loaded") + dbMock.onLoadStore = { + storeLoaded.fulfill() + } + + // Used in Validation + let contextPrepared = expectation(description: "context loaded") + dbMock.onMakeContext = { + contextPrepared.fulfill() + } + + let favsObtained = expectation(description: "Favorites queried") + ffMock.onGetFavs = { + favsObtained.fulfill() + return nil + } + + let initialValidation = expectation(description: "Initial validation") + validatorMock.onValidateInitialState = { + initialValidation.fulfill() + return true + } + + let structureValidation = expectation(description: "Structure validation") + validatorMock.onValidateBookmarksStructure = { + structureValidation.fulfill() + } + + let setup = BookmarksDatabaseSetup(migrationAssertion: BookmarksMigrationAssertion(store: MockKeyValueStore())) + + switch setup.loadStoreAndMigrate(bookmarksDatabase: dbMock, + formFactorFavoritesMigrator: ffMock, + validator: validatorMock) { + case .success: + break + case .failure(let error): + XCTFail("Unexpected error: \(error)") + } + + wait(for: [storeLoaded, contextPrepared, favsObtained, initialValidation, structureValidation], timeout: 5) + } + + func testWhenFetchingOldStateFailsThenErrorIsReturned() { + + let dbMock = DummyCoreDataStoreMock() + dbMock.onLoadStore = { _ in + XCTFail("Store should not be loaded") + } + dbMock.onMakeContext = { + XCTFail("Context should not be requested") + } + + let favsObtained = expectation(description: "Favorites queried") + ffMock.onGetFavs = { + favsObtained.fulfill() + throw BookmarksModelError.bookmarkFolderExpected + } + + validatorMock.onValidateInitialState = { + XCTFail("Validation should not be called") + return true + } + + validatorMock.onValidateBookmarksStructure = { + XCTFail("Validation should not be called") + } + + let setup = BookmarksDatabaseSetup(migrationAssertion: BookmarksMigrationAssertion(store: MockKeyValueStore())) + + switch setup.loadStoreAndMigrate(bookmarksDatabase: dbMock, + formFactorFavoritesMigrator: ffMock, + validator: validatorMock) { + case .success: + XCTFail("Unexpected") + case .failure(let error): + XCTAssertEqual(error as? BookmarksModelError, BookmarksModelError.bookmarkFolderExpected) + } + + wait(for: [favsObtained], timeout: 5) + } + + func testWhenLoadingStoreFailsThenErrorIsReturned() { + + let dbMock = DummyCoreDataStoreMock() + + let onLoadStore = expectation(description: "Favorites queried") + dbMock.onLoadStore = { completion in + onLoadStore.fulfill() + completion(nil, BookmarksModelError.bookmarkFolderExpected) + } + dbMock.onMakeContext = { + XCTFail("Context should not be requested") + } + + let favsObtained = expectation(description: "Favorites queried") + ffMock.onGetFavs = { + favsObtained.fulfill() + return nil + } + + validatorMock.onValidateInitialState = { + XCTFail("Validation should not be called") + return true + } + + validatorMock.onValidateBookmarksStructure = { + XCTFail("Validation should not be called") + } + + let setup = BookmarksDatabaseSetup(migrationAssertion: BookmarksMigrationAssertion(store: MockKeyValueStore())) + + switch setup.loadStoreAndMigrate(bookmarksDatabase: dbMock, + formFactorFavoritesMigrator: ffMock, + validator: validatorMock) { + case .success: + XCTFail("Unexpected") + case .failure(let error): + XCTAssertEqual(error as? BookmarksModelError, BookmarksModelError.bookmarkFolderExpected) + } + + wait(for: [onLoadStore, favsObtained], timeout: 5) + } + +} diff --git a/DuckDuckGoTests/BookmarksStateValidationTests.swift b/DuckDuckGoTests/BookmarksStateValidationTests.swift index ffe61497ee..75d3ff5365 100644 --- a/DuckDuckGoTests/BookmarksStateValidationTests.swift +++ b/DuckDuckGoTests/BookmarksStateValidationTests.swift @@ -62,28 +62,28 @@ class BookmarksStateValidationTests: XCTestCase { BookmarkUtils.prepareFoldersStructure(in: context) } - let validator = BookmarksStateValidation(keyValueStore: mockKeyValueStore) { error in + let validator = BookmarksStateValidator(keyValueStore: mockKeyValueStore) { error in XCTFail("Did not expect error: \(error)") } - mockKeyValueStore.set(true, forKey: BookmarksStateValidation.Constants.bookmarksDBIsInitialized) + mockKeyValueStore.set(true, forKey: BookmarksStateValidator.Constants.bookmarksDBIsInitialized) let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - XCTAssertTrue(validator.validateInitialState(context: testContext)) + XCTAssertTrue(validator.validateInitialState(context: testContext, validationError: .bookmarksStructureLost)) validator.validateBookmarksStructure(context: testContext) } } func testWhenDatabaseIsEmptyButItHasNotBeenInitiatedThenThereIsNoError() { - let validator = BookmarksStateValidation(keyValueStore: mockKeyValueStore) { error in + let validator = BookmarksStateValidator(keyValueStore: mockKeyValueStore) { error in XCTFail("Did not expect error: \(error)") } let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - XCTAssertTrue(validator.validateInitialState(context: testContext)) + XCTAssertTrue(validator.validateInitialState(context: testContext, validationError: .bookmarksStructureLost)) } } @@ -92,7 +92,7 @@ class BookmarksStateValidationTests: XCTestCase { let expectation1 = expectation(description: "Lost structure Error raised") let expectation2 = expectation(description: "Broken structure Error raised") - let validator = BookmarksStateValidation(keyValueStore: mockKeyValueStore) { error in + let validator = BookmarksStateValidator(keyValueStore: mockKeyValueStore) { error in switch error { case .bookmarksStructureLost: expectation1.fulfill() @@ -103,11 +103,11 @@ class BookmarksStateValidationTests: XCTestCase { } } - mockKeyValueStore.set(true, forKey: BookmarksStateValidation.Constants.bookmarksDBIsInitialized) + mockKeyValueStore.set(true, forKey: BookmarksStateValidator.Constants.bookmarksDBIsInitialized) let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - XCTAssertFalse(validator.validateInitialState(context: testContext)) + XCTAssertFalse(validator.validateInitialState(context: testContext, validationError: .bookmarksStructureLost)) validator.validateBookmarksStructure(context: testContext) } @@ -130,7 +130,7 @@ class BookmarksStateValidationTests: XCTestCase { let expectation = expectation(description: "Broken structure Error raised") - let validator = BookmarksStateValidation(keyValueStore: mockKeyValueStore) { error in + let validator = BookmarksStateValidator(keyValueStore: mockKeyValueStore) { error in switch error { case .bookmarksStructureBroken(let errorInfo): expectation.fulfill() @@ -144,11 +144,11 @@ class BookmarksStateValidationTests: XCTestCase { } } - mockKeyValueStore.set(true, forKey: BookmarksStateValidation.Constants.bookmarksDBIsInitialized) + mockKeyValueStore.set(true, forKey: BookmarksStateValidator.Constants.bookmarksDBIsInitialized) let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - XCTAssertTrue(validator.validateInitialState(context: testContext)) + XCTAssertTrue(validator.validateInitialState(context: testContext, validationError: .bookmarksStructureLost)) validator.validateBookmarksStructure(context: testContext) } @@ -170,7 +170,7 @@ class BookmarksStateValidationTests: XCTestCase { let expectation = expectation(description: "Broken structure Error raised") - let validator = BookmarksStateValidation(keyValueStore: mockKeyValueStore) { error in + let validator = BookmarksStateValidator(keyValueStore: mockKeyValueStore) { error in switch error { case .bookmarksStructureBroken(let errorInfo): expectation.fulfill() @@ -184,11 +184,11 @@ class BookmarksStateValidationTests: XCTestCase { } } - mockKeyValueStore.set(true, forKey: BookmarksStateValidation.Constants.bookmarksDBIsInitialized) + mockKeyValueStore.set(true, forKey: BookmarksStateValidator.Constants.bookmarksDBIsInitialized) let testContext = dbStack.makeContext(concurrencyType: .privateQueueConcurrencyType) testContext.performAndWait { - XCTAssertTrue(validator.validateInitialState(context: testContext)) + XCTAssertTrue(validator.validateInitialState(context: testContext, validationError: .bookmarksStructureLost)) validator.validateBookmarksStructure(context: testContext) } From 5380dc84872a139d6814036aa768530bd81ad96a Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 25 Jul 2024 15:48:34 +0200 Subject: [PATCH 04/10] Fire event in case initial query fails --- Core/LegacyBookmarksStoreMigration.swift | 11 +++++++++-- Core/PixelEvent.swift | 4 +++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Core/LegacyBookmarksStoreMigration.swift b/Core/LegacyBookmarksStoreMigration.swift index 268a81e2e7..0095a7ccd7 100644 --- a/Core/LegacyBookmarksStoreMigration.swift +++ b/Core/LegacyBookmarksStoreMigration.swift @@ -39,7 +39,14 @@ public class LegacyBookmarksStoreMigration { } } else { // Initialize structure if needed - BookmarkUtils.prepareLegacyFoldersStructure(in: context) + do { + try BookmarkUtils.prepareLegacyFoldersStructure(in: context) + } catch { + Pixel.fire(pixel: .debugBookmarksInitialStructureQueryFailed, error: error) + Thread.sleep(forTimeInterval: 1) + fatalError("Could not prepare Bookmarks DB structure") + } + if context.hasChanges { do { try context.save(onErrorFire: .bookmarksCouldNotPrepareDatabase) @@ -178,7 +185,7 @@ public class LegacyBookmarksStoreMigration { } catch { destination.reset() - BookmarkUtils.prepareLegacyFoldersStructure(in: destination) + try? BookmarkUtils.prepareLegacyFoldersStructure(in: destination) do { try destination.save(onErrorFire: .bookmarksMigrationCouldNotPrepareDatabaseOnFailedMigration) } catch { diff --git a/Core/PixelEvent.swift b/Core/PixelEvent.swift index d35e657a52..03723e0efd 100644 --- a/Core/PixelEvent.swift +++ b/Core/PixelEvent.swift @@ -513,6 +513,7 @@ extension Pixel { case adAttributionLogicWrongVendorOnSuccessfulCompilation case adAttributionLogicWrongVendorOnFailedCompilation + case debugBookmarksInitialStructureQueryFailed case debugBookmarksStructureLost case debugBookmarksStructureNotRecovered case debugBookmarksInvalidRoots @@ -1226,7 +1227,8 @@ extension Pixel.Event { return "m_compilation_result_\(result)_time_\(waitTime)_state_\(appState)" case .emailAutofillKeychainError: return "m_email_autofill_keychain_error" - + + case .debugBookmarksInitialStructureQueryFailed: return "m_d_bookmarks-initial-structure-query-failed" case .debugBookmarksStructureLost: return "m_d_bookmarks_structure_lost" case .debugBookmarksStructureNotRecovered: return "m_d_bookmarks_structure_not_recovered" case .debugBookmarksInvalidRoots: return "m_d_bookmarks_invalid_roots" From 8b0266c089a6abb9d655620b619ccb7d4d977779 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Thu, 25 Jul 2024 16:47:18 +0200 Subject: [PATCH 05/10] Update BSK ref --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f1534ab000..cafb4c1b71 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10197,8 +10197,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - branch = "bartek/data-debugging"; - kind = branch; + kind = revision; + revision = 7eae08a34b8e5294ab40d02704f54d71498cd8cb; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 5dc48775f7..71e455d24e 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "branch" : "bartek/data-debugging", - "revision" : "49bc707ac1f947b36d0f3d74ac30a158123656bf" + "revision" : "7eae08a34b8e5294ab40d02704f54d71498cd8cb" } }, { From dad7856490a876c4c8d2262805ee365e6ea17082 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Fri, 26 Jul 2024 11:23:35 +0200 Subject: [PATCH 06/10] Clean up --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- DuckDuckGo/AppDelegate.swift | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index cafb4c1b71..43dc581a3d 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10198,7 +10198,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = revision; - revision = 7eae08a34b8e5294ab40d02704f54d71498cd8cb; + revision = 1c25c93e4bb320d032a57cdd41394dbeb687ea98; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 71e455d24e..af2879f402 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,7 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "7eae08a34b8e5294ab40d02704f54d71498cd8cb" + "revision" : "1c25c93e4bb320d032a57cdd41394dbeb687ea98" } }, { diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index bfddefa3d9..f11062d7fd 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -406,8 +406,7 @@ import WebKit return NullHistoryManager() case .success(let historyManager): - self.presentPreemptiveCrashAlert() - return NullHistoryManager() + return historyManager } } From 405e1c5aeb2672b6d73f148a68460d8026bbaa22 Mon Sep 17 00:00:00 2001 From: Bartek Waresiak Date: Fri, 26 Jul 2024 18:55:46 +0200 Subject: [PATCH 07/10] Update BSK ref --- DuckDuckGo.xcodeproj/project.pbxproj | 2 +- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 43dc581a3d..2f16efca70 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10198,7 +10198,7 @@ repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { kind = revision; - revision = 1c25c93e4bb320d032a57cdd41394dbeb687ea98; + revision = c747c5c2f1080478a61ea576575053ff06b51799; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index af2879f402..2e685a39c6 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,7 +32,7 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "1c25c93e4bb320d032a57cdd41394dbeb687ea98" + "revision" : "c747c5c2f1080478a61ea576575053ff06b51799" } }, { From 474e95ef461527bbccff165f85d19393479fbdae Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Tue, 30 Jul 2024 20:25:02 -0700 Subject: [PATCH 08/10] Set BSK branch. --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 49431c9a4e..20bebb1696 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10289,8 +10289,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - kind = revision; - revision = c747c5c2f1080478a61ea576575053ff06b51799; + branch = "bartek/data-debugging"; + kind = branch; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 2e685a39c6..46ec633168 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,7 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "revision" : "c747c5c2f1080478a61ea576575053ff06b51799" + "branch" : "bartek/data-debugging", + "revision" : "7317455687e687953e9697998c1893ba74506942" } }, { From 33986d74b9ff5390f9bfd67aeefcb87ee09d5bcd Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Sun, 4 Aug 2024 16:30:27 -0700 Subject: [PATCH 09/10] Update BSK reference. --- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 46ec633168..a2bcce81bb 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -33,7 +33,7 @@ "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { "branch" : "bartek/data-debugging", - "revision" : "7317455687e687953e9697998c1893ba74506942" + "revision" : "783a12fbb7587fede16e0d9e5b136894bacd24ef" } }, { From c4f0012b32985776e9eb7ca06f17346e6db0dbac Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Sun, 4 Aug 2024 16:52:05 -0700 Subject: [PATCH 10/10] Set BSK to 180.0.0. --- DuckDuckGo.xcodeproj/project.pbxproj | 4 ++-- .../project.xcworkspace/xcshareddata/swiftpm/Package.resolved | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 8f495929fa..35e15de26c 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -10284,8 +10284,8 @@ isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit"; requirement = { - branch = "bartek/data-debugging"; - kind = branch; + kind = exactVersion; + version = 180.0.0; }; }; 9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = { diff --git a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index a2bcce81bb..ef47d74ec4 100644 --- a/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -32,8 +32,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/DuckDuckGo/BrowserServicesKit", "state" : { - "branch" : "bartek/data-debugging", - "revision" : "783a12fbb7587fede16e0d9e5b136894bacd24ef" + "revision" : "92ecebfb4172ab9561959a07d7ef7037aea8c6e1", + "version" : "180.0.0" } }, {