diff --git a/SUPLA.xcodeproj/project.pbxproj b/SUPLA.xcodeproj/project.pbxproj index cb2a9853..5f441c68 100644 --- a/SUPLA.xcodeproj/project.pbxproj +++ b/SUPLA.xcodeproj/project.pbxproj @@ -425,6 +425,7 @@ A52BFEF62B18883600A2F64C /* AuthInfo+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BFEF52B18883600A2F64C /* AuthInfo+Mock.swift */; }; A52BFEF82B189ECC00A2F64C /* ActivateProfileUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BFEF72B189ECC00A2F64C /* ActivateProfileUseCaseTests.swift */; }; A52BFEFA2B18A43000A2F64C /* DeleteProfileUseCaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52BFEF92B18A43000A2F64C /* DeleteProfileUseCaseTests.swift */; }; + A52D13B12C8F0F3000134961 /* SuplaClient+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = A52D13B02C8F0F3000134961 /* SuplaClient+Ext.swift */; }; A530EDEB2A53F60400F8DAEE /* SwitchDetailVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A530EDEA2A53F60400F8DAEE /* SwitchDetailVM.swift */; }; A530EDED2A53F6A800F8DAEE /* SwitchDetailVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A530EDEC2A53F6A800F8DAEE /* SwitchDetailVC.swift */; }; A530EDF02A53F8E100F8DAEE /* SuplaTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A530EDEF2A53F8E100F8DAEE /* SuplaTabBarController.swift */; }; @@ -1800,6 +1801,7 @@ A52BFEF52B18883600A2F64C /* AuthInfo+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AuthInfo+Mock.swift"; sourceTree = ""; }; A52BFEF72B189ECC00A2F64C /* ActivateProfileUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivateProfileUseCaseTests.swift; sourceTree = ""; }; A52BFEF92B18A43000A2F64C /* DeleteProfileUseCaseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteProfileUseCaseTests.swift; sourceTree = ""; }; + A52D13B02C8F0F3000134961 /* SuplaClient+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SuplaClient+Ext.swift"; sourceTree = ""; }; A530EDEA2A53F60400F8DAEE /* SwitchDetailVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchDetailVM.swift; sourceTree = ""; }; A530EDEC2A53F6A800F8DAEE /* SwitchDetailVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwitchDetailVC.swift; sourceTree = ""; }; A530EDEF2A53F8E100F8DAEE /* SuplaTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuplaTabBarController.swift; sourceTree = ""; }; @@ -2801,6 +2803,7 @@ 011021B325CC386D00621D41 /* SAVersionError.h */, 011021B425CC386D00621D41 /* SAVersionError.m */, 401CA1F11BA0A28A00117AF4 /* SuplaClient.m */, + A52D13B02C8F0F3000134961 /* SuplaClient+Ext.swift */, ); name = "supla-client-lib"; sourceTree = ""; @@ -6546,6 +6549,7 @@ AEF79D8D2712FF8D00D7554B /* AppSettingsVC.swift in Sources */, A55A8D9F2BAC263000C540D4 /* ExecuteRollerShutterActionUseCase.swift in Sources */, A5F14B7429DED41000682FA6 /* BaseViewModel.swift in Sources */, + A52D13B12C8F0F3000134961 /* SuplaClient+Ext.swift in Sources */, 01215A7324C8AECC00429947 /* SALightsourceLifespanSettingsDialog.m in Sources */, A5074BDB2BCFE22E0081B6B1 /* SuplaChannelShadingSystemBaseConfig.swift in Sources */, AEED87DF276F1FCE005D76CF /* LocationOrderingVM.swift in Sources */, @@ -7567,7 +7571,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = SUPLA/SUPLA.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 195; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = T6ZPGSWA75; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -7586,7 +7590,7 @@ "$(inherited)", "$(PROJECT_DIR)/SUPLA/lib", ); - MARKETING_VERSION = 24.08.01; + MARKETING_VERSION = 24.09; OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS -D DEBUG"; PRODUCT_BUNDLE_IDENTIFIER = com.acsoftware.ios.supla; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -7606,7 +7610,7 @@ CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = SUPLA/SUPLA.entitlements; CODE_SIGN_IDENTITY = "iPhone Developer"; - CURRENT_PROJECT_VERSION = 194; + CURRENT_PROJECT_VERSION = 195; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = T6ZPGSWA75; GCC_PREPROCESSOR_DEFINITIONS = ( @@ -7625,7 +7629,7 @@ "$(inherited)", "$(PROJECT_DIR)/SUPLA/lib", ); - MARKETING_VERSION = 24.08.01; + MARKETING_VERSION = 24.09; PRODUCT_BUNDLE_IDENTIFIER = com.acsoftware.ios.supla; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "SUPLA/SUPLA-Bridging-Header.h"; diff --git a/SUPLA/AddWizardVC.m b/SUPLA/AddWizardVC.m index dbf39780..e78d27b2 100644 --- a/SUPLA/AddWizardVC.m +++ b/SUPLA/AddWizardVC.m @@ -639,12 +639,12 @@ -(NSString*)cloudHostName { - (void)onRegistrationEnabled:(NSNotification *)notification { - SARegistrationEnabled *reg_enabled = - [SARegistrationEnabled notificationToRegistrationEnabled:notification]; + SARegistrationEnabled *reg_enabled = [SARegistrationEnabled notificationToRegistrationEnabled:notification]; if ( reg_enabled != nil ) { if ( [reg_enabled isIODeviceRegistrationEnabled] ) { + [DisconnectUseCaseLegacyWrapper cancelWithAddWizardStartedReason]; [self showPage:PAGE_STEP_3]; } else { [self setStep:STEP_SUPERUSER_AUTHORIZATION]; @@ -658,6 +658,7 @@ - (void)onSetRegistrationEnabledResult:(NSNotification *)notification { if ( notification.userInfo != nil ) { NSNumber *code = (NSNumber *)[notification.userInfo objectForKey:@"code"]; if (code && [code intValue] == SUPLA_RESULTCODE_TRUE) { + [DisconnectUseCaseLegacyWrapper cancelWithAddWizardStartedReason]; [self showPage:PAGE_STEP_3]; } } @@ -784,6 +785,7 @@ - (IBAction)cancelOrBackTouch:(nullable id)sender { [self cleanUp]; [self.OpQueue cancelAllOperations]; [self savePrefs]; + [SuplaAppStateHolderProxy addWizardFinished]; [SuplaAppCoordinatorLegacyWrapper dismissWithAnimated: true]; } diff --git a/SUPLA/AppDelegate.swift b/SUPLA/AppDelegate.swift index aeecad2c..d5893224 100644 --- a/SUPLA/AppDelegate.swift +++ b/SUPLA/AppDelegate.swift @@ -103,8 +103,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD var settings = settings settings.backgroundEntryTime = dateProvider.currentTimestamp() - disconnectUseCase.invokeSynchronous() - suplaAppStateHolder.handle(event: .finish(reason: .appInBackground)) + disconnectUseCase.invokeSynchronous(reason: .appInBackground) } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { diff --git a/SUPLA/Core/State/SuplaAppEvent.swift b/SUPLA/Core/State/SuplaAppEvent.swift index 2f313d56..be663ab1 100644 --- a/SUPLA/Core/State/SuplaAppEvent.swift +++ b/SUPLA/Core/State/SuplaAppEvent.swift @@ -23,12 +23,13 @@ enum SuplaAppEvent: Equatable { case noAccount case connecting case connected - case cancel case lock case unlock case onStart case networkConnected + case addWizardFinished + case cancel(reason: SuplaAppState.Reason? = nil) case finish(reason: SuplaAppState.Reason? = nil) case error(reason: SuplaAppState.Reason) } diff --git a/SUPLA/Core/State/SuplaAppState.swift b/SUPLA/Core/State/SuplaAppState.swift index d07ca556..beb9b00b 100644 --- a/SUPLA/Core/State/SuplaAppState.swift +++ b/SUPLA/Core/State/SuplaAppState.swift @@ -23,7 +23,7 @@ enum SuplaAppState: Equatable { case firstProfileCreation case connecting(reason: Reason? = nil) case connected - case disconnecting + case disconnecting(reason: Reason? = nil) case locking case finished(reason: Reason? = nil) @@ -33,6 +33,7 @@ enum SuplaAppState: Equatable { case versionError case noNetwork case appInBackground + case addWizardStarted var shouldAuthorize: Bool { switch (self) { @@ -54,7 +55,7 @@ enum SuplaAppState: Equatable { case .firstProfileCreation: firstProfileCreationNextState(for: event) case .connecting(let reason): connectingNextState(for: event, previousReason: reason) case .connected: try! connectedNextState(for: event) - case .disconnecting: disconnectingNextState(for: event) + case .disconnecting(let reason): disconnectingNextState(for: event, previousReason: reason) case .locking: lockingNextState(for: event) case .finished(let reason): finishedNextState(for: event, previousReason: reason) } @@ -71,6 +72,7 @@ enum SuplaAppState: Equatable { case .cancel: try! illegalCancelEvent() case .unlock: try! illegalUnlockEvent() case .error: try! illegalErrorEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } @@ -84,6 +86,7 @@ enum SuplaAppState: Equatable { case .connected: try! illegalConnectedEvent() case .cancel: try! illegalCancelEvent() case .error: try! illegalErrorEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } @@ -98,6 +101,7 @@ enum SuplaAppState: Equatable { case .initialized: try! illegalInitializedEvent() case .noAccount: try! illegalNoAccountEvent() case .lock: try! illegalLockEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } @@ -106,21 +110,22 @@ enum SuplaAppState: Equatable { case .connecting, .initialized, .onStart: nil case .connected: .connected case .lock: .locked - case .cancel: .disconnecting + case .cancel(let reason): .disconnecting(reason: reason) case .networkConnected: .connecting() case .error(let reason): .connecting(reason: reason) case .finish(let reason): .finished(reason: reason == nil ? previousReason : reason) case .noAccount: try! illegalNoAccountEvent() case .unlock: try! illegalUnlockEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } private func connectedNextState(for event: SuplaAppEvent) throws -> SuplaAppState? { switch (event) { - case .onStart, .networkConnected: nil + case .onStart, .networkConnected, .addWizardFinished: nil case .connecting: .connecting() case .lock: .locked - case .cancel: .disconnecting + case .cancel(let reason): .disconnecting(reason: reason) case .finish(let reason): .finished(reason: reason) case .error(let reason): .finished(reason: reason) case .initialized: try! illegalInitializedEvent() @@ -130,15 +135,16 @@ enum SuplaAppState: Equatable { } } - private func disconnectingNextState(for event: SuplaAppEvent) -> SuplaAppState? { + private func disconnectingNextState(for event: SuplaAppEvent, previousReason: Reason?) -> SuplaAppState? { switch (event) { case .onStart, .cancel, .networkConnected, .connecting, .connected: nil case .lock: .locking - case .finish(let reason): .finished(reason: reason) + case .finish(let reason): .finished(reason: previousReason ?? reason) case .error(let reason): .finished(reason: reason) case .initialized: try! illegalInitializedEvent() case .noAccount: try! illegalNoAccountEvent() case .unlock: try! illegalUnlockEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } @@ -152,18 +158,32 @@ enum SuplaAppState: Equatable { case .unlock: try! illegalUnlockEvent() case .connecting: try! illegalConnectingEvent() case .error(_): try! illegalErrorEvent() + case .addWizardFinished: try! illegalAddWizardFinishedEvent() } } private func finishedNextState(for event: SuplaAppEvent, previousReason: Reason?) -> SuplaAppState? { switch (event) { case .cancel, .networkConnected: nil - case .initialized, .connecting: .connecting() + case .initialized, .addWizardFinished: .connecting() + case .connecting: previousReason == .addWizardStarted ? nil : .connecting() case .lock: .locked - case .onStart: previousReason == .noNetwork ? .connecting(reason: previousReason) : .connecting() + case .onStart: + if (previousReason == .addWizardStarted) { + nil + } else if (previousReason == .noNetwork) { + .connecting(reason: previousReason) + } else { + .connecting() + } case .noAccount: .firstProfileCreation case .error(let reason): reason != previousReason ? .finished(reason: reason) : nil - case .finish(let reason): reason != previousReason ? .finished(reason: reason ?? previousReason) : nil + case .finish(let reason): + if (previousReason == .addWizardStarted || previousReason == reason) { + nil + } else { + .finished(reason: reason ?? previousReason) + } case .connected: try! illegalConnectedEvent() case .unlock: try! illegalUnlockEvent() } @@ -204,4 +224,8 @@ enum SuplaAppState: Equatable { private func illegalLockEvent() throws -> SuplaAppState? { throw Error.illegalEvent(message: "Unexpected lock event!") } + + private func illegalAddWizardFinishedEvent() throws -> SuplaAppState? { + throw Error.illegalEvent(message: "Unexpected add wizard finished event!") + } } diff --git a/SUPLA/Core/State/SuplaAppStateHolder.swift b/SUPLA/Core/State/SuplaAppStateHolder.swift index 466d3d1f..1fc5cb07 100644 --- a/SUPLA/Core/State/SuplaAppStateHolder.swift +++ b/SUPLA/Core/State/SuplaAppStateHolder.swift @@ -79,6 +79,12 @@ final class SuplaAppStateHolderProxy: NSObject { @Singleton var stateHolder stateHolder.handle(event: .connecting) } + + @objc + static func addWizardFinished() { + @Singleton var stateHolder + stateHolder.handle(event: .addWizardFinished) + } @objc static func connectionError(code: Int32) { @@ -101,7 +107,7 @@ final class SuplaAppStateHolderProxy: NSObject { @objc static func cancel() { @Singleton var stateHolder - stateHolder.handle(event: .cancel) + stateHolder.handle(event: .cancel()) } @objc diff --git a/SUPLA/Features/Status/StatusVM.swift b/SUPLA/Features/Status/StatusVM.swift index 005373c8..391228b6 100644 --- a/SUPLA/Features/Status/StatusVM.swift +++ b/SUPLA/Features/Status/StatusVM.swift @@ -88,7 +88,7 @@ extension StatusFeature { case .connectionError(let code): code == SUPLA_RESULT_HOST_NOT_FOUND ? Strings.Status.errorHostNotFound : nil case .registerError(let code): SuplaResultCode.from(value: code).getTextMessage(authDialog: true) - case .noNetwork, .versionError, .appInBackground, .none: nil + case .noNetwork, .versionError, .appInBackground, .addWizardStarted, .none: nil } } } diff --git a/SUPLA/Resources/Base.lproj/AddWizardVC.xib b/SUPLA/Resources/Base.lproj/AddWizardVC.xib index fcf208b1..ceb66517 100644 --- a/SUPLA/Resources/Base.lproj/AddWizardVC.xib +++ b/SUPLA/Resources/Base.lproj/AddWizardVC.xib @@ -570,7 +570,7 @@ When the connection has been set, go back to the application and continue addin - + - + @@ -754,6 +754,9 @@ When the connection has been set, go back to the application and continue addin + + + @@ -766,8 +769,8 @@ When the connection has been set, go back to the application and continue addin - - + + diff --git a/SUPLA/SuplaClient+Ext.swift b/SUPLA/SuplaClient+Ext.swift new file mode 100644 index 00000000..39633d0c --- /dev/null +++ b/SUPLA/SuplaClient+Ext.swift @@ -0,0 +1,36 @@ +// +/* + 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. + */ + + +import Foundation + +extension SuplaClientProtocol { + func cancel(reason: SuplaAppState.Reason?) { + (self as? SASuplaClient)?.cancel(reason: reason) + } +} + +extension SASuplaClient { + func cancel(reason: SuplaAppState.Reason? = nil) { + @Singleton var suplaAppStateHolder: SuplaAppStateHolder + + super.cancel() + suplaAppStateHolder.handle(event: .cancel(reason: reason)) + } +} diff --git a/SUPLA/UseCase/Client/DisconnectUseCase.swift b/SUPLA/UseCase/Client/DisconnectUseCase.swift index 2477d39f..b758b6ff 100644 --- a/SUPLA/UseCase/Client/DisconnectUseCase.swift +++ b/SUPLA/UseCase/Client/DisconnectUseCase.swift @@ -20,8 +20,18 @@ import RxSwift protocol DisconnectUseCase { - func invoke() -> Completable - func invokeSynchronous() + func invoke(reason: SuplaAppState.Reason?) -> Completable + func invokeSynchronous(reason: SuplaAppState.Reason?) +} + +extension DisconnectUseCase { + func invoke() -> Completable { + self.invoke(reason: nil) + } + + func invokeSynchronous() { + invokeSynchronous(reason: nil) + } } final class DisconnectUseCaseImpl: DisconnectUseCase { @@ -30,21 +40,21 @@ final class DisconnectUseCaseImpl: DisconnectUseCase { @Singleton private var suplaAppProvider @Singleton private var updateEventsManager - func invoke() -> Completable { + func invoke(reason: SuplaAppState.Reason? = nil) -> Completable { Completable.create { completable in - self.invokeSynchronous() + self.invokeSynchronous(reason: reason) completable(.completed) return Disposables.create() } } - func invokeSynchronous() { + func invokeSynchronous(reason: SuplaAppState.Reason? = nil) { let suplaApp = suplaAppProvider.provide() if (suplaApp.isClientWorking()) { let suplaClient = suplaClientProvider.provide() - suplaClient.cancel() + suplaClient.cancel(reason: reason) while (!suplaClient.isFinished()) { usleep(1000) @@ -60,3 +70,10 @@ final class DisconnectUseCaseImpl: DisconnectUseCase { updateEventsManager.emitScenesUpdate() } } + +@objc class DisconnectUseCaseLegacyWrapper: NSObject { + @objc static func cancelWithAddWizardStartedReason() { + @Singleton var disconnectUseCase: DisconnectUseCase + disconnectUseCase.invokeSynchronous(reason: .addWizardStarted) + } +} diff --git a/SUPLATests/Mocks/UseCase/ClientUseCasesMocks.swift b/SUPLATests/Mocks/UseCase/ClientUseCasesMocks.swift index f1608747..5e9bf937 100644 --- a/SUPLATests/Mocks/UseCase/ClientUseCasesMocks.swift +++ b/SUPLATests/Mocks/UseCase/ClientUseCasesMocks.swift @@ -85,13 +85,13 @@ final class AuthorizeUseCaseMock: AuthorizeUseCase { final class DisconnectUseCaseMock: DisconnectUseCase { var invokeCounter = 0 var invokeReturns: Completable = .empty() - func invoke() -> Completable { + func invoke(reason: SuplaAppState.Reason?) -> Completable { invokeCounter += 1 return invokeReturns } var invokeSynchronousCounter = 0 - func invokeSynchronous() { + func invokeSynchronous(reason: SuplaAppState.Reason?) { invokeSynchronousCounter += 1 } } diff --git a/SUPLATests/Tests/Features/Status/StatusVMTests.swift b/SUPLATests/Tests/Features/Status/StatusVMTests.swift index 6af703c6..7aea5f4b 100644 --- a/SUPLATests/Tests/Features/Status/StatusVMTests.swift +++ b/SUPLATests/Tests/Features/Status/StatusVMTests.swift @@ -93,7 +93,7 @@ final class StatusVMTests: XCTestCase { func test_shouldShowDisconnecting() { // given - stateHolder.stateReturns = .just(.disconnecting) + stateHolder.stateReturns = .just(.disconnecting()) // when viewModel.onViewWillAppear()