From 62b4de360e04745af2123c1fa916f7de2d613240 Mon Sep 17 00:00:00 2001 From: Michal Polanski Date: Thu, 18 Jul 2024 10:16:08 +0200 Subject: [PATCH] Issue #394: Database initialization restored --- SUPLA.xcodeproj/project.pbxproj | 8 --- SUPLA/AppDelegate.swift | 13 ++-- SUPLA/Core/DI/DiContainer.swift | 1 - SUPLA/Core/Infrastructure/DatabaseProxy.swift | 29 --------- SUPLA/CoreDataManager.swift | 59 +++++++++++++------ SUPLA/UseCase/App/InitializationUseCase.swift | 4 -- .../Infrastructure/DatabaseProxyMock.swift | 27 --------- .../App/InitializationUseCaseTests.swift | 6 -- 8 files changed, 49 insertions(+), 98 deletions(-) delete mode 100644 SUPLA/Core/Infrastructure/DatabaseProxy.swift delete mode 100644 SUPLATests/Mocks/Infrastructure/DatabaseProxyMock.swift diff --git a/SUPLA.xcodeproj/project.pbxproj b/SUPLA.xcodeproj/project.pbxproj index 0f4ff3e8..fb3f5d52 100644 --- a/SUPLA.xcodeproj/project.pbxproj +++ b/SUPLA.xcodeproj/project.pbxproj @@ -871,8 +871,6 @@ A5D712752C3E708900A8EF52 /* CheckPinUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712742C3E708900A8EF52 /* CheckPinUseCaseTests.swift */; }; A5D712772C3E769300A8EF52 /* SingleTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712762C3E769300A8EF52 /* SingleTestCase.swift */; }; A5D7127A2C3E8F8500A8EF52 /* InitializationUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712792C3E8F8500A8EF52 /* InitializationUseCaseTests.swift */; }; - A5D7127C2C3E916C00A8EF52 /* DatabaseProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D7127B2C3E916C00A8EF52 /* DatabaseProxy.swift */; }; - A5D7127E2C3E9D6D00A8EF52 /* DatabaseProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D7127D2C3E9D6D00A8EF52 /* DatabaseProxyMock.swift */; }; A5D712812C3EA2E300A8EF52 /* LockScreenVMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712802C3EA2E300A8EF52 /* LockScreenVMTests.swift */; }; A5D712852C3EA3A700A8EF52 /* SuplaAppCoordinatorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712842C3EA3A700A8EF52 /* SuplaAppCoordinatorMock.swift */; }; A5D712882C3EB24900A8EF52 /* PinSetupVMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A5D712872C3EB24900A8EF52 /* PinSetupVMTests.swift */; }; @@ -2257,8 +2255,6 @@ A5D712742C3E708900A8EF52 /* CheckPinUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckPinUseCaseTests.swift; sourceTree = ""; }; A5D712762C3E769300A8EF52 /* SingleTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SingleTestCase.swift; sourceTree = ""; }; A5D712792C3E8F8500A8EF52 /* InitializationUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitializationUseCaseTests.swift; sourceTree = ""; }; - A5D7127B2C3E916C00A8EF52 /* DatabaseProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseProxy.swift; sourceTree = ""; }; - A5D7127D2C3E9D6D00A8EF52 /* DatabaseProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseProxyMock.swift; sourceTree = ""; }; A5D712802C3EA2E300A8EF52 /* LockScreenVMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LockScreenVMTests.swift; sourceTree = ""; }; A5D712842C3EA3A700A8EF52 /* SuplaAppCoordinatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuplaAppCoordinatorMock.swift; sourceTree = ""; }; A5D712872C3EB24900A8EF52 /* PinSetupVMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinSetupVMTests.swift; sourceTree = ""; }; @@ -4274,7 +4270,6 @@ A54A06622AF50E8A00C03DBC /* RequestHelperMock.swift */, A55A8DC02BB2F46F00C540D4 /* ThreadHandlerMock.swift */, A55A8DC22BB2F4E000C540D4 /* NotificationCenterWrapperMock.swift */, - A5D7127D2C3E9D6D00A8EF52 /* DatabaseProxyMock.swift */, ); path = Infrastructure; sourceTree = ""; @@ -5354,7 +5349,6 @@ A54A064B2AF3E55700C03DBC /* SuplaSchedulers.swift */, A55A8DA72BAC50B100C540D4 /* NotificationCenterWrapper.swift */, A55A8DBA2BB2E30900C540D4 /* ThreadHandler.swift */, - A5D7127B2C3E916C00A8EF52 /* DatabaseProxy.swift */, A5D712972C411B2E00A8EF52 /* BuildInfo.swift */, ); path = Infrastructure; @@ -6487,7 +6481,6 @@ A553862D29E021AE00B5CF3F /* DiContainer.swift in Sources */, A5477DBF2AA5B40C00220B4A /* SuplaHvacMode.swift in Sources */, A503ABC02B6BD27F008CDA1F /* ChartEntryDetails.swift in Sources */, - A5D7127C2C3E916C00A8EF52 /* DatabaseProxy.swift in Sources */, A50B5D062BEA4DA700918D18 /* RollerShutterWindowState.swift in Sources */, A5F5C3FC2C3698040058E255 /* PinSetupVC.swift in Sources */, A58316FA2B64507D006113F8 /* ThermometerValueProvider.swift in Sources */, @@ -7028,7 +7021,6 @@ A59AB8C32A308AF700D91F1F /* ProvideDetailTypeUseCaseTests.swift in Sources */, A54A06692AF527DB00C03DBC /* UserStateHolderMock.swift in Sources */, A5D7129C2C4123EF00A8EF52 /* MockReturns.swift in Sources */, - A5D7127E2C3E9D6D00A8EF52 /* DatabaseProxyMock.swift in Sources */, A59AB8B82A307F3200D91F1F /* MainVMTests.swift in Sources */, A58B2CB62AC197E300764388 /* ChannelBaseUseCasesMocks.swift in Sources */, A5E9CE392C35385700509702 /* SuplaAppStateHolderMock.swift in Sources */, diff --git a/SUPLA/AppDelegate.swift b/SUPLA/AppDelegate.swift index 8d48e4ab..aeecad2c 100644 --- a/SUPLA/AppDelegate.swift +++ b/SUPLA/AppDelegate.swift @@ -49,6 +49,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + SALog.debug("Application did finish launching with options") + #if DEBUG // Short-circuit starting app if running unit tests if (ProcessInfo.processInfo.environment["XCTestConfigurationFilePath"] != nil) { @@ -63,12 +65,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD coordinator.start(animated: true) } - registerForNotifications() - - DispatchQueue.global(qos: .userInitiated).async { - InitializationUseCase.invoke() + CoreDataManager.shared.setup { + DispatchQueue.global(qos: .userInitiated).async { + InitializationUseCase.invoke() + } } + registerForNotifications() return true } @@ -120,6 +123,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + SALog.debug("Application did receive remote notification") + do { try insertNotificationUseCase.invoke(userInfo: userInfo).subscribeSynchronous() } catch { diff --git a/SUPLA/Core/DI/DiContainer.swift b/SUPLA/Core/DI/DiContainer.swift index 45c50196..1ac98ce1 100644 --- a/SUPLA/Core/DI/DiContainer.swift +++ b/SUPLA/Core/DI/DiContainer.swift @@ -79,7 +79,6 @@ extension DiContainer { register(SuplaSchedulers.self, SuplaSchedulersImpl()) register(ThreadHandler.self, ThreadHandlerImpl()) register(SuplaAppStateHolder.self, SuplaAppStateHolderImpl()) - register(DatabaseProxy.self, DatabaseProxyImpl()) register(BuildInfo.self, BuildInfoImpl()) // Managers diff --git a/SUPLA/Core/Infrastructure/DatabaseProxy.swift b/SUPLA/Core/Infrastructure/DatabaseProxy.swift deleted file mode 100644 index ad405672..00000000 --- a/SUPLA/Core/Infrastructure/DatabaseProxy.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -/* - Copyright (C) AC SOFTWARE SP. Z O.O. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - - -protocol DatabaseProxy { - func setup() -} - -final class DatabaseProxyImpl: DatabaseProxy { - func setup() { - CoreDataManager.shared.setup() - } -} diff --git a/SUPLA/CoreDataManager.swift b/SUPLA/CoreDataManager.swift index ad802f5f..865ce678 100644 --- a/SUPLA/CoreDataManager.swift +++ b/SUPLA/CoreDataManager.swift @@ -69,7 +69,7 @@ class CoreDataManager: NSObject { self.migrator = migrator } - @objc func setup() { + @objc func setup(completion: @escaping () -> Void) { removeOldDatabases() ValueTransformer.setValueTransformer( @@ -77,39 +77,60 @@ class CoreDataManager: NSObject { forName: NSValueTransformerName("GroupTotalValueTransformer") ) - migrateStoreIfNeeded() - - persistentContainer.loadPersistentStores { _, error in - guard error == nil else { - fatalError("Not able to load store \(error!)") - } + loadPersistentStore { + completion() + } + } + + private func loadPersistentStore(completion: @escaping () -> Void) { + migrateStoreIfNeeded { + self.persistentContainer.loadPersistentStores { _, error in + guard error == nil else { + fatalError("Not able to load store \(error!)") + } - if (self.tryRecreateAccount) { - if (SAApp.profileManager().restoreProfileFromDefaults()) { - var settings = self.settings - settings.anyAccountRegistered = true + if (self.tryRecreateAccount) { + DispatchQueue.global(qos: .userInitiated).async { + if (SAApp.profileManager().restoreProfileFromDefaults()) { + var settings = self.settings + settings.anyAccountRegistered = true + } + DispatchQueue.main.async { + completion() + } + } + } else { + completion() } } } } - private func migrateStoreIfNeeded() { + private func migrateStoreIfNeeded(completion: @escaping () -> Void) { guard let storeUrl = persistentContainer.persistentStoreDescriptions.first?.url else { fatalError("persistentContainer was not set up properly") } if migrator.requiresMigration(at: storeUrl, toVersion: CoreDataMigrationVersion.current) { - do { - try migrator.migrateStore(at: storeUrl, toVersion: CoreDataMigrationVersion.current) - } catch { + DispatchQueue.global(qos: .userInitiated).async { + do { + try self.migrator.migrateStore(at: storeUrl, toVersion: CoreDataMigrationVersion.current) + } catch { #if DEBUG - fatalError("Migration failed with error \(error)") + fatalError("Migration failed with error \(error)") #else - // If migration fails in production we want to delete the database so the - // user is able to create account again - removeCurrentDatabase() + // If migration fails in production we want to delete the database so the + // user is able to create account again + self.removeCurrentDatabase() #endif + } + + DispatchQueue.main.async { + completion() + } } + } else { + completion() } } diff --git a/SUPLA/UseCase/App/InitializationUseCase.swift b/SUPLA/UseCase/App/InitializationUseCase.swift index fa654fec..8d3bbd49 100644 --- a/SUPLA/UseCase/App/InitializationUseCase.swift +++ b/SUPLA/UseCase/App/InitializationUseCase.swift @@ -25,14 +25,10 @@ enum InitializationUseCase { @Singleton var profileRepository @Singleton var stateHolder @Singleton var settings - @Singleton var databaseProxy @Singleton var threadHandler let initializationStartTime = dateProvider.currentTimestamp() - // Migrate database if needed - databaseProxy.setup() - // Check if there is an active profile let profileFound = (try? profileRepository.getActiveProfile().subscribeSynchronous()?.isActive) ?? false diff --git a/SUPLATests/Mocks/Infrastructure/DatabaseProxyMock.swift b/SUPLATests/Mocks/Infrastructure/DatabaseProxyMock.swift deleted file mode 100644 index 9b9b6f73..00000000 --- a/SUPLATests/Mocks/Infrastructure/DatabaseProxyMock.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -/* - Copyright (C) AC SOFTWARE SP. Z O.O. - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -@testable import SUPLA - -final class DatabaseProxyMock: DatabaseProxy { - var setupCalls = 0 - func setup() { - setupCalls += 1 - } -} diff --git a/SUPLATests/Tests/UseCase/App/InitializationUseCaseTests.swift b/SUPLATests/Tests/UseCase/App/InitializationUseCaseTests.swift index b1993d35..3af16f3b 100644 --- a/SUPLATests/Tests/UseCase/App/InitializationUseCaseTests.swift +++ b/SUPLATests/Tests/UseCase/App/InitializationUseCaseTests.swift @@ -27,7 +27,6 @@ final class InitializationUseCaseTests: XCTestCase { private lazy var stateHolder: SuplaAppStateHolderMock! = SuplaAppStateHolderMock() private lazy var settings: GlobalSettingsMock! = GlobalSettingsMock() private lazy var threadHandler: ThreadHandlerMock! = ThreadHandlerMock() - private lazy var databaseProxy: DatabaseProxyMock! = DatabaseProxyMock() override func setUp() { DiContainer.shared.register(type: DateProvider.self, dateProvider!) @@ -35,7 +34,6 @@ final class InitializationUseCaseTests: XCTestCase { DiContainer.shared.register(type: SuplaAppStateHolder.self, stateHolder!) DiContainer.shared.register(type: GlobalSettings.self, settings!) DiContainer.shared.register(type: ThreadHandler.self, threadHandler!) - DiContainer.shared.register(type: DatabaseProxy.self, databaseProxy!) } override func tearDown() { @@ -44,7 +42,6 @@ final class InitializationUseCaseTests: XCTestCase { stateHolder = nil settings = nil threadHandler = nil - databaseProxy = nil } func test_shouldRequirePin() { @@ -59,7 +56,6 @@ final class InitializationUseCaseTests: XCTestCase { XCTAssertEqual(stateHolder.handleParameters, [.lock]) XCTAssertEqual(dateProvider.currentTimestampCalls, 2) XCTAssertEqual(threadHandler.usleepParameters, [800_000]) - XCTAssertEqual(databaseProxy.setupCalls, 1) XCTAssertEqual(profileRepository.activeProfileCalls, 1) } @@ -75,7 +71,6 @@ final class InitializationUseCaseTests: XCTestCase { XCTAssertEqual(stateHolder.handleParameters, [.noAccount]) XCTAssertEqual(dateProvider.currentTimestampCalls, 2) XCTAssertEqual(threadHandler.usleepParameters, [800_000]) - XCTAssertEqual(databaseProxy.setupCalls, 1) XCTAssertEqual(profileRepository.activeProfileCalls, 1) } @@ -94,7 +89,6 @@ final class InitializationUseCaseTests: XCTestCase { XCTAssertEqual(stateHolder.handleParameters, [.initialized]) XCTAssertEqual(dateProvider.currentTimestampCalls, 2) XCTAssertEqual(threadHandler.usleepParameters, []) - XCTAssertEqual(databaseProxy.setupCalls, 1) XCTAssertEqual(profileRepository.activeProfileCalls, 1) } }