From 66a36a76bfe23668c3e879dc3c90a00ff62f8e62 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Mon, 22 Apr 2024 18:10:24 +0300 Subject: [PATCH] Cleanup following the AppMediator introduction (#2723) - stop using multiple background task, the appCoordinator sync one is enough for the whole app - move the AppMeditor to the MainActor - expose the WindowManager through the AppMediator - hide sensitive WindowManager API behind a different protocol - remove the now unnecessary `BackgroundTaskService` --- ElementX.xcodeproj/project.pbxproj | 36 ---- .../Sources/Application/AppCoordinator.swift | 36 ++-- .../Application/AppCoordinatorProtocol.swift | 2 +- .../Sources/Application/AppMediator.swift | 11 +- .../Application/AppMediatorProtocol.swift | 7 +- .../Application/Windowing/SceneDelegate.swift | 2 +- .../Application/Windowing/WindowManager.swift | 4 +- .../Windowing/WindowManagerProtocol.swift | 28 +-- .../AuthenticationFlowCoordinator.swift | 7 +- .../RoomFlowCoordinator.swift | 12 +- .../UserSessionFlowCoordinator.swift | 18 +- ElementX/Sources/Mocks/AppMediatorMock.swift | 41 +---- .../Mocks/Generated/GeneratedMocks.swift | 160 +++--------------- .../BackgroundTaskProtocol.swift | 37 ---- .../BackgroundTaskServiceProtocol.swift | 45 ----- .../BackgroundTasks/UIKitBackgroundTask.swift | 95 ----------- .../UIKitBackgroundTaskService.swift | 98 ----------- .../Sources/Services/Client/ClientProxy.swift | 12 +- .../Media/Provider/MediaProvider.swift | 13 +- .../NotificationSettingsProxy.swift | 25 +-- .../Sources/Services/Room/RoomProxy.swift | 41 +---- .../Services/Timeline/TimelineProxy.swift | 73 +------- .../UserSession/UserSessionStore.swift | 11 +- .../VoiceMessageMediaManager.swift | 8 +- .../UITests/UITestsAppCoordinator.swift | 14 +- .../UnitTests/UnitTestsAppCoordinator.swift | 2 +- NSE/Sources/Other/NSEUserSession.swift | 3 +- NSE/SupportingFiles/target.yml | 2 - UnitTests/Sources/BackgroundTaskTests.swift | 160 ------------------ .../MediaProvider/MediaProviderTests.swift | 4 +- .../MockBackgroundTaskService.swift | 23 --- .../Sources/RoomFlowCoordinatorTests.swift | 3 +- .../UserSessionFlowCoordinatorTests.swift | 1 - .../VoiceMessageMediaManagerTests.swift | 12 +- 34 files changed, 117 insertions(+), 929 deletions(-) delete mode 100644 ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift delete mode 100644 ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift delete mode 100644 ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift delete mode 100644 ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift delete mode 100644 UnitTests/Sources/BackgroundTaskTests.swift delete mode 100644 UnitTests/Sources/MediaProvider/MockBackgroundTaskService.swift diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index 0ef69cd88..8479158c1 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -50,7 +50,6 @@ 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; }; 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9104846487244648D32C6D /* AudioPlayerProtocol.swift */; }; 08CB4BD12CEEDE6AAE4A18DD /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035177BCD8E8308B098AC3C2 /* WindowManager.swift */; }; - 08E02C56E8B277A22C5E5BA5 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */; }; 095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; }; 095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; }; 09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; }; @@ -81,7 +80,6 @@ 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; }; 0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; }; 0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.swift */; }; - 0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */; }; 10D60D287025B71F4743A425 /* RoomDirectorySearchProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */; }; 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; }; 119AE9A3FC6E0606C1146528 /* NotificationSettingsEditScreenRoomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97F8963B14EB0AF3940DDBF /* NotificationSettingsEditScreenRoomCell.swift */; }; @@ -330,7 +328,6 @@ 4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; }; 4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; }; 4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.swift */; }; - 4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */; }; 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; }; 4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; }; 4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.swift */; }; @@ -751,7 +748,6 @@ B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */; }; B245583C63F8F90357B87FAE /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = A2AE110B053B55E38F8D10C7 /* KZFileWatchers */; }; B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */; }; - B3066502FA56D9199846C5E7 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */; }; B3D652AA1654270742072FB3 /* DeveloperOptionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */; }; B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A1CCDEE545CB6453B084BF /* FormButtonStyles.swift */; }; B402708F8728DD0DB7C324E2 /* StartChatScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */; }; @@ -850,7 +846,6 @@ C9F5B48D15B9BCAE1F8D564E /* RoomNotificationModeProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */; }; CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; }; CA5BFF0C2EF5A8EF40CA2D69 /* VoiceMessageRecordingComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB6F36CCE44A29A06FCAF1C /* VoiceMessageRecordingComposer.swift */; }; - CB0C53B67DA62F7E30DDD720 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */; }; CB137BFB3E083C33E398A6CB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; }; CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; CB6BCBF28E4B76EA08C2926D /* StateRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16048D30F0438731C41F775 /* StateRoomTimelineItem.swift */; }; @@ -910,11 +905,9 @@ D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; }; D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; }; D6661A94DBD97658B2ADBD6A /* MapTilerStaticMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */; }; - D798A150BB6569FC07CBEB25 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */; }; D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; }; D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; }; D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; }; - D8ED2FCF46D63A7357E9D502 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */; }; D9473FC9B077A6EDB7A12001 /* LocationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 772334731A8BF8E6D90B194D /* LocationRoomTimelineView.swift */; }; D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; }; @@ -955,7 +948,6 @@ E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; }; E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; }; E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; }; - E3E3D645C7F80DD64A86D936 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */; }; E45C9FA22BC13B477FD3B4AC /* EmojiDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */; }; E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; }; E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; }; @@ -1339,7 +1331,6 @@ 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerTests.swift; sourceTree = ""; }; 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceipt.swift; sourceTree = ""; }; 317F41A4B5C4F457AF710666 /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = ""; }; - 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = ""; }; 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillContextTests.swift; sourceTree = ""; }; 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = ""; }; 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsUserDefinedScreen.swift; sourceTree = ""; }; @@ -1369,7 +1360,6 @@ 38345442415E07A931197C55 /* AppLockScreenPINKeypad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenPINKeypad.swift; sourceTree = ""; }; 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenViewModel.swift; sourceTree = ""; }; 38E521D6C2BF8DF0DFB35146 /* DeveloperOptionsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreen.swift; sourceTree = ""; }; - 3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBackgroundTaskService.swift; sourceTree = ""; }; 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenScreenModelProtocol.swift; sourceTree = ""; }; 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = ""; }; 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = ""; }; @@ -1561,7 +1551,6 @@ 6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreen.swift; sourceTree = ""; }; 6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatViewModelTests.swift; sourceTree = ""; }; 6DF81D7F2A6BA9DE3F6F8D9D /* InvitesScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModel.swift; sourceTree = ""; }; - 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskTests.swift; sourceTree = ""; }; 6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleRoomTimelineView.swift; sourceTree = ""; }; 6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelTests.swift; sourceTree = ""; }; 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = ""; }; @@ -1645,7 +1634,6 @@ 8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = ""; }; 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = ""; }; 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = ""; }; - 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskProtocol.swift; sourceTree = ""; }; 84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = ""; }; 848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = ""; }; @@ -1965,7 +1953,6 @@ D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = ""; }; D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = ""; }; D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenCoordinator.swift; sourceTree = ""; }; - D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = ""; }; D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = ""; }; D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformViewVersionPredicate.swift; sourceTree = ""; }; D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFlowCoordinator.swift; sourceTree = ""; }; @@ -2067,7 +2054,6 @@ EA880E78AF4BD24E45A7808C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSection.swift; sourceTree = ""; }; EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; - EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = ""; }; EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenModels.swift; sourceTree = ""; }; EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenViewModel.swift; sourceTree = ""; }; EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = ""; }; @@ -2327,7 +2313,6 @@ 7803E03F759061C948D66B7E /* AppLock */, FCE7249621F507F34A8122FB /* Audio */, AAFDD509929A0CCF8BCE51EB /* Authentication */, - C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */, 0ED3F5C21537519389C07644 /* BugReport */, 8039515BAA53B7C3275AC64A /* Client */, 8C3BAE06B336D97DABBE2509 /* CreateRoom */, @@ -3551,7 +3536,6 @@ C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */, 2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */, 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */, - 6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */, 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */, @@ -3689,7 +3673,6 @@ children = ( 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */, 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */, - 3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */, AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */, 4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */, ); @@ -4672,17 +4655,6 @@ path = RoomDirectorySearchScreen; sourceTree = ""; }; - C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */ = { - isa = PBXGroup; - children = ( - 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */, - D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */, - EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */, - 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */, - ); - path = BackgroundTasks; - sourceTree = ""; - }; C844840F3DD48A154C65AE0C /* View */ = { isa = PBXGroup; children = ( @@ -5661,8 +5633,6 @@ CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */, BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */, 968A5B890004526AB58A217C /* AvatarSize.swift in Sources */, - 08E02C56E8B277A22C5E5BA5 /* BackgroundTaskProtocol.swift in Sources */, - D8ED2FCF46D63A7357E9D502 /* BackgroundTaskServiceProtocol.swift in Sources */, 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */, B5618E3C948584E5C1F67033 /* DTHTMLElement+AttributedStringBuilder.swift in Sources */, DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */, @@ -5736,7 +5706,6 @@ 3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */, 192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */, 8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */, - 0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */, CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */, 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */, C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */, @@ -5772,7 +5741,6 @@ B9A8C34A00D03094C0CF56F3 /* MediaUploadPreviewScreenViewModelTests.swift in Sources */, 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */, F777C6FEE7D106136E2ED2B2 /* MessageForwardingScreenViewModelTests.swift in Sources */, - 4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */, 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */, DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */, A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */, @@ -5944,8 +5912,6 @@ 6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */, 4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */, D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */, - B3066502FA56D9199846C5E7 /* BackgroundTaskProtocol.swift in Sources */, - D798A150BB6569FC07CBEB25 /* BackgroundTaskServiceProtocol.swift in Sources */, A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */, 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */, EB9F4688006B52E69DF5358F /* BlankFormCoordinator.swift in Sources */, @@ -6541,8 +6507,6 @@ 36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */, A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */, 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */, - E3E3D645C7F80DD64A86D936 /* UIKitBackgroundTask.swift in Sources */, - CB0C53B67DA62F7E30DDD720 /* UIKitBackgroundTaskService.swift in Sources */, E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */, 384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */, 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index 31fdc121c..df11d67b0 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -20,7 +20,7 @@ import MatrixRustSDK import SwiftUI import Version -class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, WindowManagerDelegate { +class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, SecureWindowManagerDelegate { private let stateMachine: AppCoordinatorStateMachine private let navigationRootCoordinator: NavigationRootCoordinator private let userSessionStore: UserSessionStoreProtocol @@ -29,7 +29,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private let appDelegate: AppDelegate /// Common background task to continue long-running tasks in the background. - private var backgroundTask: BackgroundTaskProtocol? + private var backgroundTask: UIBackgroundTaskIdentifier? private var isSuspended = false @@ -50,15 +50,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator? private var userSessionFlowCoordinator: UserSessionFlowCoordinator? private var softLogoutCoordinator: SoftLogoutScreenCoordinator? - - private let backgroundTaskService: BackgroundTaskServiceProtocol - private var appDelegateObserver: AnyCancellable? private var userSessionObserver: AnyCancellable? private var clientProxyObserver: AnyCancellable? private var cancellables = Set() - let windowManager: WindowManagerProtocol + let windowManager: SecureWindowManagerProtocol let notificationManager: NotificationManagerProtocol private let appRouteURLParser: AppRouteURLParser @@ -103,12 +100,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator()) - backgroundTaskService = UIKitBackgroundTaskService(appMediator: appMediator) - let keychainController = KeychainController(service: .sessions, accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) - userSessionStore = UserSessionStore(keychainController: keychainController, - backgroundTaskService: backgroundTaskService) + userSessionStore = UserSessionStore(keychainController: keychainController) let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings) let appLockNavigationCoordinator = NavigationRootCoordinator() @@ -227,7 +221,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg // MARK: - WindowManagerDelegate - func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) { + func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) { windowManager.alternateWindow.rootViewController = UIHostingController(rootView: appLockFlowCoordinator.toPresentable()) ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow } @@ -413,8 +407,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg appMediator: appMediator, appSettings: appSettings, analytics: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - orientationManager: windowManager) + userIndicatorController: ServiceLocator.shared.userIndicatorController) authenticationFlowCoordinator?.delegate = self authenticationFlowCoordinator?.start() @@ -469,7 +462,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, navigationRootCoordinator: navigationRootCoordinator, - windowManager: windowManager, appLockService: appLockFlowCoordinator.appLockService, bugReportService: ServiceLocator.shared.bugReportService, roomTimelineControllerFactory: RoomTimelineControllerFactory(), @@ -770,14 +762,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg guard backgroundTask == nil else { return } - - backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in + + backgroundTask = appMediator.beginBackgroundTask { [weak self] in guard let self else { return } stopSync() - backgroundTask?.stop() - backgroundTask = nil + if let backgroundTask { + appMediator.endBackgroundTask(backgroundTask) + self.backgroundTask = nil + } } isSuspended = true @@ -791,8 +785,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private func applicationDidBecomeActive() { MXLog.info("Application did become active") - backgroundTask?.stop() - backgroundTask = nil + if let backgroundTask { + appMediator.endBackgroundTask(backgroundTask) + self.backgroundTask = nil + } if isSuspended { startSync() diff --git a/ElementX/Sources/Application/AppCoordinatorProtocol.swift b/ElementX/Sources/Application/AppCoordinatorProtocol.swift index 5ac3381c5..2419526da 100644 --- a/ElementX/Sources/Application/AppCoordinatorProtocol.swift +++ b/ElementX/Sources/Application/AppCoordinatorProtocol.swift @@ -17,6 +17,6 @@ import Foundation protocol AppCoordinatorProtocol: CoordinatorProtocol { - var windowManager: WindowManagerProtocol { get } + var windowManager: SecureWindowManagerProtocol { get } @discardableResult func handleDeepLink(_ url: URL, isExternalURL: Bool) -> Bool } diff --git a/ElementX/Sources/Application/AppMediator.swift b/ElementX/Sources/Application/AppMediator.swift index 720fcabbc..2d5ad199f 100644 --- a/ElementX/Sources/Application/AppMediator.swift +++ b/ElementX/Sources/Application/AppMediator.swift @@ -17,7 +17,7 @@ import UIKit class AppMediator: AppMediatorProtocol { - private let windowManager: WindowManagerProtocol + let windowManager: WindowManagerProtocol init(windowManager: WindowManagerProtocol) { self.windowManager = windowManager @@ -28,7 +28,6 @@ class AppMediator: AppMediatorProtocol { UIApplication.shared } - @MainActor var appState: UIApplication.State { switch application.applicationState { case .active: @@ -42,12 +41,8 @@ class AppMediator: AppMediatorProtocol { } } - var backgroundTimeRemaining: TimeInterval { - application.backgroundTimeRemaining - } - - func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier { - application.beginBackgroundTask(withName: taskName, expirationHandler: handler) + func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier { + application.beginBackgroundTask(expirationHandler: handler) } func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) { diff --git a/ElementX/Sources/Application/AppMediatorProtocol.swift b/ElementX/Sources/Application/AppMediatorProtocol.swift index 7812d2d8d..bff568e14 100644 --- a/ElementX/Sources/Application/AppMediatorProtocol.swift +++ b/ElementX/Sources/Application/AppMediatorProtocol.swift @@ -18,12 +18,13 @@ import Foundation import UIKit // sourcery: AutoMockable +@MainActor protocol AppMediatorProtocol { + var windowManager: WindowManagerProtocol { get } + var appState: UIApplication.State { get } - var backgroundTimeRemaining: TimeInterval { get } - - func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier + func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) diff --git a/ElementX/Sources/Application/Windowing/SceneDelegate.swift b/ElementX/Sources/Application/Windowing/SceneDelegate.swift index df9a5fb9b..b5e5bf331 100644 --- a/ElementX/Sources/Application/Windowing/SceneDelegate.swift +++ b/ElementX/Sources/Application/Windowing/SceneDelegate.swift @@ -20,7 +20,7 @@ import SwiftUI /// /// We don't support multiple scenes right now, so the implementation is pretty basic. class SceneDelegate: NSObject, UIWindowSceneDelegate { - weak static var windowManager: WindowManagerProtocol! + weak static var windowManager: SecureWindowManagerProtocol! func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = scene as? UIWindowScene else { return } diff --git a/ElementX/Sources/Application/Windowing/WindowManager.swift b/ElementX/Sources/Application/Windowing/WindowManager.swift index 7c5873171..884128d2e 100644 --- a/ElementX/Sources/Application/Windowing/WindowManager.swift +++ b/ElementX/Sources/Application/Windowing/WindowManager.swift @@ -17,10 +17,10 @@ import Combine import SwiftUI -class WindowManager: WindowManagerProtocol { +class WindowManager: SecureWindowManagerProtocol { private let appDelegate: AppDelegate weak var windowScene: UIWindowScene? - weak var delegate: WindowManagerDelegate? + weak var delegate: SecureWindowManagerDelegate? private(set) var mainWindow: UIWindow! private(set) var overlayWindow: UIWindow! diff --git a/ElementX/Sources/Application/Windowing/WindowManagerProtocol.swift b/ElementX/Sources/Application/Windowing/WindowManagerProtocol.swift index 8a1c9452b..eef5d573a 100644 --- a/ElementX/Sources/Application/Windowing/WindowManagerProtocol.swift +++ b/ElementX/Sources/Application/Windowing/WindowManagerProtocol.swift @@ -16,18 +16,29 @@ import SwiftUI -protocol WindowManagerDelegate: AnyObject { +protocol SecureWindowManagerDelegate: AnyObject { /// The window manager has configured its windows. - func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) + func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) } @MainActor +protocol SecureWindowManagerProtocol: WindowManagerProtocol { + var delegate: SecureWindowManagerDelegate? { get set } + + /// Configures the window manager to operate on the supplied scene. + func configure(with windowScene: UIWindowScene) + + /// Shows the main and overlay window combo, hiding the alternate window. + func switchToMain() + + /// Shows the alternate window, hiding the main and overlay combo. + func switchToAlternate() +} + /// A window manager that supports switching between a main app window with an overlay and /// an alternate window to switch contexts whilst also preserving the main view hierarchy. /// Heavily inspired by https://www.fivestars.blog/articles/swiftui-windows/ protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol { - var delegate: WindowManagerDelegate? { get set } - /// The app's main window (we only support a single scene). var mainWindow: UIWindow! { get } /// Presented on top of the main window, to display e.g. user indicators. @@ -40,15 +51,6 @@ protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol { /// All the windows being managed var windows: [UIWindow] { get } - /// Configures the window manager to operate on the supplied scene. - func configure(with windowScene: UIWindowScene) - - /// Shows the main and overlay window combo, hiding the alternate window. - func switchToMain() - - /// Shows the alternate window, hiding the main and overlay combo. - func switchToAlternate() - /// Makes the global search window key. Used to get automatic text field focus. func showGlobalSearch() diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index 568832489..3d6283d6d 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -31,7 +31,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { private let appSettings: AppSettings private let analytics: AnalyticsService private let userIndicatorController: UserIndicatorControllerProtocol - private let orientationManager: OrientationManagerProtocol private var cancellables = Set() @@ -48,8 +47,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { appMediator: AppMediatorProtocol, appSettings: AppSettings, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol, - orientationManager: WindowManagerProtocol) { + userIndicatorController: UserIndicatorControllerProtocol) { self.authenticationService = authenticationService self.bugReportService = bugReportService self.navigationRootCoordinator = navigationRootCoordinator @@ -57,7 +55,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { self.appSettings = appSettings self.analytics = analytics self.userIndicatorController = userIndicatorController - self.orientationManager = orientationManager navigationStackCoordinator = NavigationStackCoordinator() } @@ -110,7 +107,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { private func startQRCodeLogin() { let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(), - orientationManager: orientationManager, + orientationManager: appMediator.windowManager, appMediator: appMediator)) coordinator.actionsPublisher.sink { [weak self] action in guard let self else { diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index 6fe0c1b63..08c23e8d6 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -50,7 +50,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private let appSettings: AppSettings private let analytics: AnalyticsService private let userIndicatorController: UserIndicatorControllerProtocol - private let orientationManager: OrientationManagerProtocol private var roomProxy: RoomProxyProtocol! @@ -81,8 +80,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { appMediator: AppMediatorProtocol, appSettings: AppSettings, analytics: AnalyticsService, - userIndicatorController: UserIndicatorControllerProtocol, - orientationManager: OrientationManagerProtocol) async { + userIndicatorController: UserIndicatorControllerProtocol) async { self.roomID = roomID self.userSession = userSession self.isChildFlow = isChildFlow @@ -93,7 +91,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.appSettings = appSettings self.analytics = analytics self.userIndicatorController = userIndicatorController - self.orientationManager = orientationManager setupStateMachine() @@ -695,7 +692,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { mediaProvider: userSession.mediaProvider, navigationStackCoordinator: stackCoordinator, userIndicatorController: userIndicatorController, - orientationManager: orientationManager) + orientationManager: appMediator.windowManager) let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters) roomDetailsEditCoordinator.actions.sink { [weak self] action in @@ -752,7 +749,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, source: source, - orientationManager: orientationManager) { [weak self] action in + orientationManager: appMediator.windowManager) { [weak self] action in guard let self else { return } @@ -1239,8 +1236,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { appMediator: appMediator, appSettings: appSettings, analytics: analytics, - userIndicatorController: userIndicatorController, - orientationManager: orientationManager) + userIndicatorController: userIndicatorController) coordinator.actions.sink { [weak self] action in guard let self else { return } diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index a73b61c99..ab8bf3bb3 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -28,7 +28,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { private let userSession: UserSessionProtocol private let navigationRootCoordinator: NavigationRootCoordinator private let navigationSplitCoordinator: NavigationSplitCoordinator - private let windowManager: WindowManagerProtocol private let bugReportService: BugReportServiceProtocol private let appMediator: AppMediatorProtocol private let appSettings: AppSettings @@ -68,7 +67,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { init(userSession: UserSessionProtocol, navigationRootCoordinator: NavigationRootCoordinator, - windowManager: WindowManagerProtocol, appLockService: AppLockServiceProtocol, bugReportService: BugReportServiceProtocol, roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, @@ -80,7 +78,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { stateMachine = UserSessionFlowCoordinatorStateMachine() self.userSession = userSession self.navigationRootCoordinator = navigationRootCoordinator - self.windowManager = windowManager self.bugReportService = bugReportService self.roomTimelineControllerFactory = roomTimelineControllerFactory self.appMediator = appMediator @@ -96,7 +93,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator) settingsFlowCoordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession, - windowManager: windowManager, + windowManager: appMediator.windowManager, appLockService: appLockService, bugReportService: bugReportService, notificationSettings: userSession.clientProxy.notificationSettings, @@ -427,8 +424,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { appMediator: appMediator, appSettings: appSettings, analytics: analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - orientationManager: windowManager) + userIndicatorController: ServiceLocator.shared.userIndicatorController) coordinator.actions.sink { [weak self] action in guard let self else { return } @@ -480,7 +476,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { let startChatNavigationStackCoordinator = NavigationStackCoordinator() let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy) - let parameters = StartChatScreenCoordinatorParameters(orientationManager: windowManager, + let parameters = StartChatScreenCoordinatorParameters(orientationManager: appMediator.windowManager, userSession: userSession, userIndicatorController: ServiceLocator.shared.userIndicatorController, navigationStackCoordinator: startChatNavigationStackCoordinator, @@ -609,14 +605,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { let hostingController = UIHostingController(rootView: coordinator.toPresentable()) hostingController.view.backgroundColor = .clear - windowManager.globalSearchWindow.rootViewController = hostingController + appMediator.windowManager.globalSearchWindow.rootViewController = hostingController - windowManager.showGlobalSearch() + appMediator.windowManager.showGlobalSearch() } private func dismissGlobalSearch() { - windowManager.globalSearchWindow.rootViewController = nil - windowManager.hideGlobalSearch() + appMediator.windowManager.globalSearchWindow.rootViewController = nil + appMediator.windowManager.hideGlobalSearch() globalSearchScreenCoordinator = nil } diff --git a/ElementX/Sources/Mocks/AppMediatorMock.swift b/ElementX/Sources/Mocks/AppMediatorMock.swift index fea1aa74b..1e1a2db3c 100644 --- a/ElementX/Sources/Mocks/AppMediatorMock.swift +++ b/ElementX/Sources/Mocks/AppMediatorMock.swift @@ -18,44 +18,11 @@ import UIKit extension AppMediatorMock { static var `default`: AppMediatorProtocol { - AppMediatorMock(withState: .active, - backgroundTimeRemaining: 10, - allowTasks: true) - } - - static var mockBroken: AppMediatorProtocol { - AppMediatorMock(withState: .inactive, - backgroundTimeRemaining: 0, - allowTasks: false) - } - - static var mockAboutToSuspend: AppMediatorProtocol { - AppMediatorMock(withState: .background, - backgroundTimeRemaining: 2, - allowTasks: false) - } - - private static var bgTaskIdentifier = 0 - - convenience init(withState applicationState: UIApplication.State, - backgroundTimeRemaining: TimeInterval, - allowTasks: Bool) { - self.init() + let mock = AppMediatorMock() - underlyingAppState = applicationState - underlyingBackgroundTimeRemaining = backgroundTimeRemaining + mock.underlyingAppState = .active + mock.underlyingWindowManager = WindowManagerMock() - beginBackgroundTaskWithNameExpirationHandlerClosure = { _, handler in - guard allowTasks else { - return .invalid - } - Self.bgTaskIdentifier += 1 - - let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier) - DispatchQueue.main.asyncAfter(deadline: .now() + 2) { - handler?() - } - return identifier - } + return mock } } diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index e4de14ff6..a797ef1a6 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -748,28 +748,28 @@ class AppLockServiceMock: AppLockServiceProtocol { } } class AppMediatorMock: AppMediatorProtocol { + var windowManager: WindowManagerProtocol { + get { return underlyingWindowManager } + set(value) { underlyingWindowManager = value } + } + var underlyingWindowManager: WindowManagerProtocol! var appState: UIApplication.State { get { return underlyingAppState } set(value) { underlyingAppState = value } } var underlyingAppState: UIApplication.State! - var backgroundTimeRemaining: TimeInterval { - get { return underlyingBackgroundTimeRemaining } - set(value) { underlyingBackgroundTimeRemaining = value } - } - var underlyingBackgroundTimeRemaining: TimeInterval! //MARK: - beginBackgroundTask - var beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = 0 - var beginBackgroundTaskWithNameExpirationHandlerCallsCount: Int { + var beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = 0 + var beginBackgroundTaskExpirationHandlerCallsCount: Int { get { if Thread.isMainThread { - return beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount + return beginBackgroundTaskExpirationHandlerUnderlyingCallsCount } else { var returnValue: Int? = nil DispatchQueue.main.sync { - returnValue = beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount + returnValue = beginBackgroundTaskExpirationHandlerUnderlyingCallsCount } return returnValue! @@ -777,27 +777,27 @@ class AppMediatorMock: AppMediatorProtocol { } set { if Thread.isMainThread { - beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = newValue + beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = newValue } else { DispatchQueue.main.sync { - beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = newValue + beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = newValue } } } } - var beginBackgroundTaskWithNameExpirationHandlerCalled: Bool { - return beginBackgroundTaskWithNameExpirationHandlerCallsCount > 0 + var beginBackgroundTaskExpirationHandlerCalled: Bool { + return beginBackgroundTaskExpirationHandlerCallsCount > 0 } - var beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue: UIBackgroundTaskIdentifier! - var beginBackgroundTaskWithNameExpirationHandlerReturnValue: UIBackgroundTaskIdentifier! { + var beginBackgroundTaskExpirationHandlerUnderlyingReturnValue: UIBackgroundTaskIdentifier! + var beginBackgroundTaskExpirationHandlerReturnValue: UIBackgroundTaskIdentifier! { get { if Thread.isMainThread { - return beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue + return beginBackgroundTaskExpirationHandlerUnderlyingReturnValue } else { var returnValue: UIBackgroundTaskIdentifier? = nil DispatchQueue.main.sync { - returnValue = beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue + returnValue = beginBackgroundTaskExpirationHandlerUnderlyingReturnValue } return returnValue! @@ -805,22 +805,22 @@ class AppMediatorMock: AppMediatorProtocol { } set { if Thread.isMainThread { - beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue = newValue + beginBackgroundTaskExpirationHandlerUnderlyingReturnValue = newValue } else { DispatchQueue.main.sync { - beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue = newValue + beginBackgroundTaskExpirationHandlerUnderlyingReturnValue = newValue } } } } - var beginBackgroundTaskWithNameExpirationHandlerClosure: ((String?, (() -> Void)?) -> UIBackgroundTaskIdentifier)? + var beginBackgroundTaskExpirationHandlerClosure: (((() -> Void)?) -> UIBackgroundTaskIdentifier)? - func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier { - beginBackgroundTaskWithNameExpirationHandlerCallsCount += 1 - if let beginBackgroundTaskWithNameExpirationHandlerClosure = beginBackgroundTaskWithNameExpirationHandlerClosure { - return beginBackgroundTaskWithNameExpirationHandlerClosure(taskName, handler) + func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier { + beginBackgroundTaskExpirationHandlerCallsCount += 1 + if let beginBackgroundTaskExpirationHandlerClosure = beginBackgroundTaskExpirationHandlerClosure { + return beginBackgroundTaskExpirationHandlerClosure(handler) } else { - return beginBackgroundTaskWithNameExpirationHandlerReturnValue + return beginBackgroundTaskExpirationHandlerReturnValue } } //MARK: - endBackgroundTask @@ -12976,122 +12976,12 @@ class VoiceMessageRecorderMock: VoiceMessageRecorderProtocol { } } class WindowManagerMock: WindowManagerProtocol { - weak var delegate: WindowManagerDelegate? var mainWindow: UIWindow! var overlayWindow: UIWindow! var globalSearchWindow: UIWindow! var alternateWindow: UIWindow! var windows: [UIWindow] = [] - //MARK: - configure - - var configureWithUnderlyingCallsCount = 0 - var configureWithCallsCount: Int { - get { - if Thread.isMainThread { - return configureWithUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = configureWithUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - configureWithUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - configureWithUnderlyingCallsCount = newValue - } - } - } - } - var configureWithCalled: Bool { - return configureWithCallsCount > 0 - } - var configureWithReceivedWindowScene: UIWindowScene? - var configureWithReceivedInvocations: [UIWindowScene] = [] - var configureWithClosure: ((UIWindowScene) -> Void)? - - func configure(with windowScene: UIWindowScene) { - configureWithCallsCount += 1 - configureWithReceivedWindowScene = windowScene - configureWithReceivedInvocations.append(windowScene) - configureWithClosure?(windowScene) - } - //MARK: - switchToMain - - var switchToMainUnderlyingCallsCount = 0 - var switchToMainCallsCount: Int { - get { - if Thread.isMainThread { - return switchToMainUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = switchToMainUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - switchToMainUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - switchToMainUnderlyingCallsCount = newValue - } - } - } - } - var switchToMainCalled: Bool { - return switchToMainCallsCount > 0 - } - var switchToMainClosure: (() -> Void)? - - func switchToMain() { - switchToMainCallsCount += 1 - switchToMainClosure?() - } - //MARK: - switchToAlternate - - var switchToAlternateUnderlyingCallsCount = 0 - var switchToAlternateCallsCount: Int { - get { - if Thread.isMainThread { - return switchToAlternateUnderlyingCallsCount - } else { - var returnValue: Int? = nil - DispatchQueue.main.sync { - returnValue = switchToAlternateUnderlyingCallsCount - } - - return returnValue! - } - } - set { - if Thread.isMainThread { - switchToAlternateUnderlyingCallsCount = newValue - } else { - DispatchQueue.main.sync { - switchToAlternateUnderlyingCallsCount = newValue - } - } - } - } - var switchToAlternateCalled: Bool { - return switchToAlternateCallsCount > 0 - } - var switchToAlternateClosure: (() -> Void)? - - func switchToAlternate() { - switchToAlternateCallsCount += 1 - switchToAlternateClosure?() - } //MARK: - showGlobalSearch var showGlobalSearchUnderlyingCallsCount = 0 diff --git a/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift b/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift deleted file mode 100644 index aae57196d..000000000 --- a/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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 - -typealias BackgroundTaskExpirationHandler = (BackgroundTaskProtocol) -> Void - -/// BackgroundTaskProtocol is the protocol describing a background task regardless of the platform used. -protocol BackgroundTaskProtocol: AnyObject { - /// Name of the background task for debug. - var name: String { get } - - /// `true` if the background task is currently running. - var isRunning: Bool { get } - - /// Flag indicating the background task is reusable. If reusable, `name` is the key to distinguish background tasks. - var isReusable: Bool { get } - - /// Method to be called when a task reused one more time. Should only be valid for reusable tasks. - func reuse() - - /// Stop the background task. Cannot be started anymore. For reusable tasks, should be called same number of times `reuse` called. - func stop() -} diff --git a/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift b/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift deleted file mode 100644 index 568fcee14..000000000 --- a/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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 - -@MainActor -protocol BackgroundTaskServiceProtocol { - func startBackgroundTask(withName name: String, - isReusable: Bool, - expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? -} - -extension BackgroundTaskServiceProtocol { - func startBackgroundTask(withName name: String) -> BackgroundTaskProtocol? { - startBackgroundTask(withName: name, - expirationHandler: nil) - } - - func startBackgroundTask(withName name: String, - isReusable: Bool) -> BackgroundTaskProtocol? { - startBackgroundTask(withName: name, - isReusable: isReusable, - expirationHandler: nil) - } - - func startBackgroundTask(withName name: String, - expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? { - startBackgroundTask(withName: name, - isReusable: false, - expirationHandler: expirationHandler) - } -} diff --git a/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift deleted file mode 100644 index 6f8e73b34..000000000 --- a/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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 UIKit - -class UIKitBackgroundTask: BackgroundTaskProtocol { - let name: String - var isRunning: Bool { - identifier != .invalid - } - - let isReusable: Bool - let expirationHandler: BackgroundTaskExpirationHandler? - var elapsedTime: TimeInterval { - Date().timeIntervalSince(startDate) * 1000 - } - - private let appMediator: AppMediatorProtocol - private var identifier: UIBackgroundTaskIdentifier = .invalid - private var useCounter = 0 - private let startDate = Date() - - init?(name: String, - isReusable: Bool, - appMediator: AppMediatorProtocol, - expirationHandler: BackgroundTaskExpirationHandler?) { - self.name = name - self.isReusable = isReusable - self.appMediator = appMediator - self.expirationHandler = expirationHandler - - identifier = appMediator.beginBackgroundTask(withName: name) { [weak self] in - guard let self else { return } - self.stop() - self.expirationHandler?(self) - } - - if identifier == .invalid { - MXLog.error("Do not start background task: \(name), as OS declined") - expirationHandler?(self) - return nil - } - - if isReusable { - reuse() - } - - MXLog.verbose("Start background task #\(identifier.rawValue) - \(name)") - } - - func reuse() { - guard isReusable else { - return - } - useCounter += 1 - } - - func stop() { - if isReusable { - useCounter -= 1 - if useCounter <= 0 { - endTask() - } - } else { - endTask() - } - } - - private func endTask() { - if identifier != .invalid { - MXLog.verbose("End background task #\(identifier.rawValue) - \(name) after \(readableElapsedTime)") - - appMediator.endBackgroundTask(identifier) - identifier = .invalid - } - } - - private var readableElapsedTime: String { - String(format: "%.3fms", elapsedTime) - } -} diff --git a/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift deleted file mode 100644 index 42b3e9e75..000000000 --- a/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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 UIKit - -/// /// UIKitBackgroundTaskService is a concrete implementation of BackgroundTaskServiceProtocol using a given `ApplicationProtocol` instance. -class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { - private let appMediator: AppMediatorProtocol - private var reusableTasks = NSMapTable(keyOptions: .strongMemory, valueOptions: .weakMemory) - - init(appMediator: AppMediatorProtocol) { - self.appMediator = appMediator - } - - func startBackgroundTask(withName name: String, - isReusable: Bool, - expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? { - if shouldAvoidStartingNewTasks() { - MXLog.error("Do not start background task: \(name), as not enough time exists") - // call expiration handler immediately - expirationHandler?() - return nil - } - var created = false - var result: BackgroundTaskProtocol? - - if isReusable { - if let oldTask = reusableTasks.object(forKey: name as NSString), oldTask.isRunning { - oldTask.reuse() - result = oldTask - } else { - if let newTask = UIKitBackgroundTask(name: name, - isReusable: isReusable, - appMediator: appMediator, - expirationHandler: { [weak self] task in - guard let self else { return } - self.reusableTasks.removeObject(forKey: task.name as NSString) - expirationHandler?() - }) { - created = true - reusableTasks.setObject(newTask, forKey: name as NSString) - result = newTask - } - } - } else { - if let newTask = UIKitBackgroundTask(name: name, - isReusable: isReusable, - appMediator: appMediator, - expirationHandler: { _ in - expirationHandler?() - }) { - result = newTask - created = true - } - } - - let appState = appMediator.appState - let remainingTime = readableBackgroundTimeRemaining(appMediator.backgroundTimeRemaining) - - MXLog.verbose("Background task \(name) \(created ? "started" : "reused") with app state: \(appState) and estimated background time remaining: \(remainingTime)") - - return result - } - - private func readableBackgroundTimeRemaining(_ backgroundTimeRemaining: TimeInterval) -> String { - if backgroundTimeRemaining == .greatestFiniteMagnitude { - return "undetermined" - } else { - return String(format: "%.0f seconds", backgroundTimeRemaining) - } - } - - private func shouldAvoidStartingNewTasks() -> Bool { - if appMediator.appState == .background, - appMediator.backgroundTimeRemaining < .backgroundTimeRemainingThresholdToStartTasks { - return true - } - return false - } -} - -private extension TimeInterval { - static let backgroundTimeRemainingThresholdToStartTasks: TimeInterval = 5 -} diff --git a/ElementX/Sources/Services/Client/ClientProxy.swift b/ElementX/Sources/Services/Client/ClientProxy.swift index 10bdccf70..379f19a80 100644 --- a/ElementX/Sources/Services/Client/ClientProxy.swift +++ b/ElementX/Sources/Services/Client/ClientProxy.swift @@ -23,7 +23,6 @@ import MatrixRustSDK class ClientProxy: ClientProxyProtocol { private let client: ClientProtocol - private let backgroundTaskService: BackgroundTaskServiceProtocol private let appSettings: AppSettings private let networkMonitor: NetworkMonitorProtocol @@ -122,11 +121,9 @@ class ClientProxy: ClientProxyProtocol { } init(client: ClientProtocol, - backgroundTaskService: BackgroundTaskServiceProtocol, appSettings: AppSettings, networkMonitor: NetworkMonitorProtocol) async { self.client = client - self.backgroundTaskService = backgroundTaskService self.appSettings = appSettings self.networkMonitor = networkMonitor @@ -134,8 +131,7 @@ class ClientProxy: ClientProxyProtocol { mediaLoader = MediaLoader(client: client) - notificationSettings = NotificationSettingsProxy(notificationSettings: client.getNotificationSettings(), - backgroundTaskService: backgroundTaskService) + notificationSettings = NotificationSettingsProxy(notificationSettings: client.getNotificationSettings()) secureBackupController = SecureBackupController(encryption: client.encryption()) @@ -425,8 +421,7 @@ class ClientProxy: ClientProxyProtocol { if let roomListItem, let room { return await RoomProxy(roomListItem: roomListItem, - room: room, - backgroundTaskService: backgroundTaskService) + room: room) } // Else wait for the visible rooms list to go into fully loaded @@ -453,8 +448,7 @@ class ClientProxy: ClientProxyProtocol { } return await RoomProxy(roomListItem: roomListItem, - room: room, - backgroundTaskService: backgroundTaskService) + room: room) } func roomPreviewForIdentifier(_ identifier: String) async -> Result { diff --git a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift index 930171dea..55877f2a4 100644 --- a/ElementX/Sources/Services/Media/Provider/MediaProvider.swift +++ b/ElementX/Sources/Services/Media/Provider/MediaProvider.swift @@ -20,14 +20,11 @@ import UIKit struct MediaProvider: MediaProviderProtocol { private let mediaLoader: MediaLoaderProtocol private let imageCache: Kingfisher.ImageCache - private let backgroundTaskService: BackgroundTaskServiceProtocol? init(mediaLoader: MediaLoaderProtocol, - imageCache: Kingfisher.ImageCache, - backgroundTaskService: BackgroundTaskServiceProtocol?) { + imageCache: Kingfisher.ImageCache) { self.mediaLoader = mediaLoader self.imageCache = imageCache - self.backgroundTaskService = backgroundTaskService } // MARK: Images @@ -45,11 +42,6 @@ struct MediaProvider: MediaProviderProtocol { return .success(image) } - let loadImageBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadImage: \(source.url.hashValue)") - defer { - loadImageBgTask?.stop() - } - let cacheKey = cacheKeyForURL(source.url, size: size) if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey), @@ -92,9 +84,6 @@ struct MediaProvider: MediaProviderProtocol { // MARK: Files func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result { - let loadFileBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadFile: \(source.url.hashValue)") - defer { loadFileBgTask?.stop() } - do { let file = try await mediaLoader.loadMediaFileForSource(source, body: body) return .success(file) diff --git a/ElementX/Sources/Services/NotificationSettings/NotificationSettingsProxy.swift b/ElementX/Sources/Services/NotificationSettings/NotificationSettingsProxy.swift index b1fbcbfa6..f5245d03f 100644 --- a/ElementX/Sources/Services/NotificationSettings/NotificationSettingsProxy.swift +++ b/ElementX/Sources/Services/NotificationSettings/NotificationSettingsProxy.swift @@ -36,13 +36,11 @@ private final class WeakNotificationSettingsProxy: NotificationSettingsDelegate final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { private(set) var notificationSettings: MatrixRustSDK.NotificationSettingsProtocol - private let backgroundTaskService: BackgroundTaskServiceProtocol? let callbacks = PassthroughSubject() - init(notificationSettings: MatrixRustSDK.NotificationSettingsProtocol, backgroundTaskService: BackgroundTaskServiceProtocol?) { + init(notificationSettings: MatrixRustSDK.NotificationSettingsProtocol) { self.notificationSettings = notificationSettings - self.backgroundTaskService = backgroundTaskService notificationSettings.setDelegate(delegate: WeakNotificationSettingsProxy(proxy: self)) } @@ -52,9 +50,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func setNotificationMode(roomId: String, mode: RoomNotificationModeProxy) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setNotificationMode") - defer { backgroundTask?.stop() } - try await notificationSettings.setRoomNotificationMode(roomId: roomId, mode: mode.roomNotificationMode) await updatedSettings() } @@ -70,9 +65,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func setDefaultRoomNotificationMode(isEncrypted: Bool, isOneToOne: Bool, mode: RoomNotificationModeProxy) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setDefaultRoomNotificationMode") - defer { backgroundTask?.stop() } - do { try await notificationSettings.setDefaultRoomNotificationMode(isEncrypted: isEncrypted, isOneToOne: isOneToOne, mode: mode.roomNotificationMode) } catch NotificationSettingsError.RuleNotFound(let ruleId) { @@ -86,17 +78,11 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func restoreDefaultNotificationMode(roomId: String) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "restoreDefaultNotificationMode") - defer { backgroundTask?.stop() } - try await notificationSettings.restoreDefaultRoomNotificationMode(roomId: roomId) await updatedSettings() } func unmuteRoom(roomId: String, isEncrypted: Bool, isOneToOne: Bool) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "unmuteRoom") - defer { backgroundTask?.stop() } - try await notificationSettings.unmuteRoom(roomId: roomId, isEncrypted: isEncrypted, isOneToOne: isOneToOne) await updatedSettings() } @@ -106,9 +92,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func setRoomMentionEnabled(enabled: Bool) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setRoomMentionEnabled") - defer { backgroundTask?.stop() } - try await notificationSettings.setRoomMentionEnabled(enabled: enabled) await updatedSettings() } @@ -118,9 +101,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func setCallEnabled(enabled: Bool) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setCallEnabled") - defer { backgroundTask?.stop() } - try await notificationSettings.setCallEnabled(enabled: enabled) await updatedSettings() } @@ -130,9 +110,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { } func setInviteForMeEnabled(enabled: Bool) async throws { - let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setInviteForMeEnabled") - defer { backgroundTask?.stop() } - try await notificationSettings.setInviteForMeEnabled(enabled: enabled) await updatedSettings() } diff --git a/ElementX/Sources/Services/Room/RoomProxy.swift b/ElementX/Sources/Services/Room/RoomProxy.swift index aa0ae28b6..a06e25004 100644 --- a/ElementX/Sources/Services/Room/RoomProxy.swift +++ b/ElementX/Sources/Services/Room/RoomProxy.swift @@ -26,13 +26,9 @@ class RoomProxy: RoomProxyProtocol { private let roomListItem: RoomListItemProtocol private let room: RoomProtocol let timeline: TimelineProxyProtocol - private let backgroundTaskService: BackgroundTaskServiceProtocol - private let backgroundTaskName = "SendRoomEvent" private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", qos: .userInitiated) - private var sendMessageBackgroundTask: BackgroundTaskProtocol? - // periphery:ignore - required for instance retention in the rust codebase private var roomInfoObservationToken: TaskHandle? // periphery:ignore - required for instance retention in the rust codebase @@ -60,13 +56,12 @@ class RoomProxy: RoomProxyProtocol { } init?(roomListItem: RoomListItemProtocol, - room: RoomProtocol, - backgroundTaskService: BackgroundTaskServiceProtocol) async { + room: RoomProtocol) async { self.roomListItem = roomListItem self.room = room - self.backgroundTaskService = backgroundTaskService + do { - timeline = try await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService) + timeline = try await TimelineProxy(timeline: room.timeline()) } catch { MXLog.error("Failed creating timeline with error: \(error)") return nil @@ -165,12 +160,7 @@ class RoomProxy: RoomProxyProtocol { } func redact(_ eventID: String) async -> Result { - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - - return await Task.dispatch(on: userInitiatedDispatchQueue) { + await Task.dispatch(on: userInitiatedDispatchQueue) { do { try self.room.redact(eventId: eventID, reason: nil) return .success(()) @@ -180,14 +170,9 @@ class RoomProxy: RoomProxyProtocol { } } } - + func reportContent(_ eventID: String, reason: String?) async -> Result { - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - - return await Task.dispatch(on: userInitiatedDispatchQueue) { + await Task.dispatch(on: userInitiatedDispatchQueue) { do { try self.room.reportContent(eventId: eventID, score: nil, reason: reason) return .success(()) @@ -226,11 +211,6 @@ class RoomProxy: RoomProxyProtocol { return .success(member) } - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - do { let member = try await room.member(userId: userID) return .success(RoomMemberProxy(member: member)) @@ -239,14 +219,9 @@ class RoomProxy: RoomProxyProtocol { return .failure(.sdkError(error)) } } - + func leaveRoom() async -> Result { - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - - return await Task.dispatch(on: .global()) { + await Task.dispatch(on: .global()) { do { try self.room.leave() return .success(()) diff --git a/ElementX/Sources/Services/Timeline/TimelineProxy.swift b/ElementX/Sources/Services/Timeline/TimelineProxy.swift index e42e20c30..fc74b6f02 100644 --- a/ElementX/Sources/Services/Timeline/TimelineProxy.swift +++ b/ElementX/Sources/Services/Timeline/TimelineProxy.swift @@ -20,10 +20,7 @@ import MatrixRustSDK final class TimelineProxy: TimelineProxyProtocol { private let timeline: Timeline - private var sendMessageBackgroundTask: BackgroundTaskProtocol? - private let backgroundTaskService: BackgroundTaskServiceProtocol - private let backgroundTaskName = "SendRoomEvent" private let lowPriorityDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.low_priority", qos: .utility) private let messageSendingDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.message_sending", qos: .userInitiated) private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", qos: .userInitiated) @@ -54,9 +51,8 @@ final class TimelineProxy: TimelineProxyProtocol { roomTimelineObservationToken?.cancel() } - init(timeline: Timeline, backgroundTaskService: BackgroundTaskServiceProtocol) { + init(timeline: Timeline) { self.timeline = timeline - self.backgroundTaskService = backgroundTaskService } func subscribeForUpdates() async { @@ -84,11 +80,6 @@ final class TimelineProxy: TimelineProxyProtocol { func cancelSend(transactionID: String) async { MXLog.info("Cancelling sending for transaction ID: \(transactionID)") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - return await Task.dispatch(on: messageSendingDispatchQueue) { self.timeline.cancelSend(txnId: transactionID) MXLog.info("Finished cancelling sending for transaction ID: \(transactionID)") @@ -101,11 +92,6 @@ final class TimelineProxy: TimelineProxyProtocol { intentionalMentions: IntentionalMentions) async -> Result { MXLog.info("Editing message with original event ID: \(eventID)") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let messageContent = buildMessageContentFor(message, html: html, intentionalMentions: intentionalMentions.toRustMentions()) @@ -198,11 +184,6 @@ final class TimelineProxy: TimelineProxyProtocol { func retrySend(transactionID: String) async { MXLog.info("Retrying sending for transactionID: \(transactionID)") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - return await Task.dispatch(on: messageSendingDispatchQueue) { self.timeline.retrySend(txnId: transactionID) MXLog.info("Finished retrying sending for transactionID: \(transactionID)") @@ -215,11 +196,6 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending audio") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let handle = timeline.sendAudio(url: url.path(percentEncoded: false), audioInfo: audioInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) }) @@ -245,11 +221,6 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending file") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let handle = timeline.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) }) @@ -276,11 +247,6 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending image") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let handle = timeline.sendImage(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), imageInfo: imageInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) }) @@ -307,11 +273,6 @@ final class TimelineProxy: TimelineProxyProtocol { assetType: AssetType?) async -> Result { MXLog.info("Sending location") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - return await Task.dispatch(on: messageSendingDispatchQueue) { self.timeline.sendLocation(body: body, geoUri: geoURI.string, @@ -334,11 +295,6 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending video") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let handle = timeline.sendVideo(url: url.path(percentEncoded: false), thumbnailUrl: thumbnailURL.path(percentEncoded: false), videoInfo: videoInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) }) @@ -365,11 +321,6 @@ final class TimelineProxy: TimelineProxyProtocol { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result { MXLog.info("Sending voice message") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let handle = timeline.sendVoiceMessage(url: url.path(percentEncoded: false), audioInfo: audioInfo, waveform: waveform, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in progressSubject?.send(progress) }) @@ -399,11 +350,6 @@ final class TimelineProxy: TimelineProxyProtocol { MXLog.info("Sending message") } - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - let messageContent = buildMessageContentFor(message, html: html, intentionalMentions: intentionalMentions.toRustMentions()) @@ -436,12 +382,7 @@ final class TimelineProxy: TimelineProxyProtocol { func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result { MXLog.info("Sending message content") - - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - + return await Task.dispatch(on: messageSendingDispatchQueue) { self.timeline.send(msg: messageContent) @@ -456,11 +397,6 @@ final class TimelineProxy: TimelineProxyProtocol { func sendReadReceipt(for eventID: String, type: ReceiptType) async -> Result { MXLog.verbose("Sending read receipt for eventID: \(eventID)") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - return await Task.dispatch(on: lowPriorityDispatchQueue) { do { try self.timeline.sendReadReceipt(receiptType: type, eventId: eventID) @@ -476,11 +412,6 @@ final class TimelineProxy: TimelineProxyProtocol { func toggleReaction(_ reaction: String, to eventID: String) async -> Result { MXLog.info("Toggling reaction for eventID: \(eventID)") - sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) - defer { - sendMessageBackgroundTask?.stop() - } - return await Task.dispatch(on: userInitiatedDispatchQueue) { do { try self.timeline.toggleReaction(eventId: eventID, key: reaction) diff --git a/ElementX/Sources/Services/UserSession/UserSessionStore.swift b/ElementX/Sources/Services/UserSession/UserSessionStore.swift index b52628562..96995ab74 100644 --- a/ElementX/Sources/Services/UserSession/UserSessionStore.swift +++ b/ElementX/Sources/Services/UserSession/UserSessionStore.swift @@ -20,7 +20,6 @@ import MatrixRustSDK class UserSessionStore: UserSessionStoreProtocol { private let keychainController: KeychainControllerProtocol - private let backgroundTaskService: BackgroundTaskServiceProtocol private let matrixSDKStateKey = "matrix-sdk-state" /// Whether or not there are sessions in the store. @@ -33,9 +32,8 @@ class UserSessionStore: UserSessionStoreProtocol { var clientSessionDelegate: ClientSessionDelegate { keychainController } - init(keychainController: KeychainControllerProtocol, backgroundTaskService: BackgroundTaskServiceProtocol) { + init(keychainController: KeychainControllerProtocol) { self.keychainController = keychainController - self.backgroundTaskService = backgroundTaskService baseDirectory = .sessionsBaseDirectory MXLog.info("Setup base directory at: \(baseDirectory)") } @@ -100,11 +98,9 @@ class UserSessionStore: UserSessionStoreProtocol { private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) -> UserSessionProtocol { let mediaProvider = MediaProvider(mediaLoader: clientProxy, - imageCache: .onlyInMemory, - backgroundTaskService: backgroundTaskService) + imageCache: .onlyInMemory) - let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, - backgroundTaskService: backgroundTaskService) + let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider) return UserSession(clientProxy: clientProxy, mediaProvider: mediaProvider, @@ -150,7 +146,6 @@ class UserSessionStore: UserSessionStoreProtocol { private func setupProxyForClient(_ client: Client) async -> ClientProxyProtocol { await ClientProxy(client: client, - backgroundTaskService: backgroundTaskService, appSettings: ServiceLocator.shared.settings, networkMonitor: ServiceLocator.shared.networkMonitor) } diff --git a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift index 49aa29eab..6c8ae5ff6 100644 --- a/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift +++ b/ElementX/Sources/Services/VoiceMessage/VoiceMessageMediaManager.swift @@ -30,7 +30,6 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol { private let voiceMessageCache: VoiceMessageCacheProtocol private let audioConverter: AudioConverterProtocol - private let backgroundTaskService: BackgroundTaskServiceProtocol? private let processingQueue: DispatchQueue private var conversionRequests = [MediaSourceProxy: VoiceMessageConversionRequest]() @@ -39,13 +38,11 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol { init(mediaProvider: MediaProviderProtocol, voiceMessageCache: VoiceMessageCacheProtocol = VoiceMessageCache(), audioConverter: AudioConverterProtocol = AudioConverter(), - processingQueue: DispatchQueue = .global(), - backgroundTaskService: BackgroundTaskServiceProtocol?) { + processingQueue: DispatchQueue = .global()) { self.mediaProvider = mediaProvider self.voiceMessageCache = voiceMessageCache self.audioConverter = audioConverter self.processingQueue = processingQueue - self.backgroundTaskService = backgroundTaskService } deinit { @@ -53,9 +50,6 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol { } func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL { - let loadFileBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadFile: \(source.url.hashValue)") - defer { loadFileBgTask?.stop() } - guard let mimeType = source.mimeType, mimeType.starts(with: supportedVoiceMessageMimeType) else { throw VoiceMessageMediaManagerError.unsupportedMimeTye } diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index d5b2f8374..0a5072584 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -19,7 +19,7 @@ import MatrixRustSDK import SwiftUI import UIKit -class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate { +class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate { private let navigationRootCoordinator: NavigationRootCoordinator // periphery:ignore - retaining purpose @@ -28,7 +28,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate { // periphery:ignore - retaining purpose private var alternateWindowMockScreen: MockScreen? - let windowManager: WindowManagerProtocol + let windowManager: SecureWindowManagerProtocol init(appDelegate: AppDelegate) { windowManager = WindowManager(appDelegate: appDelegate) @@ -70,7 +70,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate { fatalError("Not implemented.") } - func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) { + func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) { ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow // Set up the alternate window for the App Lock flow coordinator tests. @@ -92,14 +92,14 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate { @MainActor class MockScreen: Identifiable { let id: UITestsScreenIdentifier - let windowManager: WindowManagerProtocol + let windowManager: SecureWindowManagerProtocol let navigationRootCoordinator: NavigationRootCoordinator private var retainedState = [Any]() private var cancellables = Set() init(id: UITestsScreenIdentifier, - windowManager: WindowManagerProtocol, + windowManager: SecureWindowManagerProtocol, navigationRootCoordinator: NavigationRootCoordinator) { self.id = id self.windowManager = windowManager @@ -129,8 +129,7 @@ class MockScreen: Identifiable { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - orientationManager: windowManager) + userIndicatorController: ServiceLocator.shared.userIndicatorController) flowCoordinator.start() retainedState.append(flowCoordinator) return nil @@ -468,7 +467,6 @@ class MockScreen: Identifiable { let flowCoordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()), navigationRootCoordinator: navigationRootCoordinator, - windowManager: windowManager, appLockService: AppLockService(keychainController: KeychainControllerMock(), appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(), diff --git a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift index 6940f5b2c..b866605ef 100644 --- a/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift +++ b/ElementX/Sources/UnitTests/UnitTestsAppCoordinator.swift @@ -17,7 +17,7 @@ import SwiftUI class UnitTestsAppCoordinator: AppCoordinatorProtocol { - let windowManager: WindowManagerProtocol + let windowManager: SecureWindowManagerProtocol init(appDelegate: AppDelegate) { windowManager = WindowManager(appDelegate: appDelegate) diff --git a/NSE/Sources/Other/NSEUserSession.swift b/NSE/Sources/Other/NSEUserSession.swift index 68aa878ee..4da12cdfb 100644 --- a/NSE/Sources/Other/NSEUserSession.swift +++ b/NSE/Sources/Other/NSEUserSession.swift @@ -22,8 +22,7 @@ final class NSEUserSession { private let notificationClient: NotificationClient private let userID: String private(set) lazy var mediaProvider: MediaProviderProtocol = MediaProvider(mediaLoader: MediaLoader(client: baseClient), - imageCache: .onlyOnDisk, - backgroundTaskService: nil) + imageCache: .onlyOnDisk) private let delegateHandle: TaskHandle? init(credentials: KeychainCredentials, clientSessionDelegate: ClientSessionDelegate) async throws { diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index 4ecca46cf..83f0e1f69 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -97,8 +97,6 @@ targets: - path: ../../ElementX/Sources/Other/TestablePreview.swift - path: ../../ElementX/Sources/Other/Pills/PlainMentionBuilder.swift - path: ../../ElementX/Sources/Other/Pills/PillConstants.swift - - path: ../../ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift - - path: ../../ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift - path: ../../ElementX/Sources/Services/Keychain/KeychainController.swift - path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift - path: ../../ElementX/Sources/Services/Media/Provider diff --git a/UnitTests/Sources/BackgroundTaskTests.swift b/UnitTests/Sources/BackgroundTaskTests.swift deleted file mode 100644 index 3d4892ac6..000000000 --- a/UnitTests/Sources/BackgroundTaskTests.swift +++ /dev/null @@ -1,160 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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 XCTest - -@testable import ElementX - -@MainActor -class BackgroundTaskTests: XCTestCase { - private enum Constants { - static let bgTaskName = "test" - } - - func testInitAndStop() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - guard let task = service.startBackgroundTask(withName: Constants.bgTaskName) else { - XCTFail("Failed to setup test conditions") - return - } - - XCTAssertEqual(task.name, Constants.bgTaskName, "Task name should be persisted") - XCTAssertFalse(task.isReusable, "Task should be not reusable by default") - XCTAssertTrue(task.isRunning, "Task should be running") - - task.stop() - - XCTAssertFalse(task.isRunning, "Task should be stopped") - } - - func testNotReusableInit() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - // create two not reusable task with the same name - guard let task1 = service.startBackgroundTask(withName: Constants.bgTaskName), - let task2 = service.startBackgroundTask(withName: Constants.bgTaskName) else { - XCTFail("Failed to setup test conditions") - return - } - - // task1 & task2 should be different instances - XCTAssertFalse(task1 === task2, - "Handler should create different tasks when reusability disabled") - } - - func testReusableInit() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - // create two reusable task with the same name - guard let task1 = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true), - let task2 = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true) else { - XCTFail("Failed to setup test conditions") - return - } - - // task1 and task2 should be the same instance - XCTAssertTrue(task1 === task2, - "Handler should create different tasks when reusability disabled") - - XCTAssertEqual(task1.name, Constants.bgTaskName, "Task name should be persisted") - XCTAssertTrue(task1.isReusable, "Task should be reusable") - XCTAssertTrue(task1.isRunning, "Task should be running") - } - - func testMultipleStops() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - // create two reusable task with the same name - guard let task = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true), - service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true) != nil else { - XCTFail("Failed to setup test conditions") - return - } - - XCTAssertTrue(task.isRunning, "Task should be running") - - task.stop() - - XCTAssertTrue(task.isRunning, "Task should be still running after one stop call") - - task.stop() - - XCTAssertFalse(task.isRunning, "Task should be stopped after two stop calls") - } - - func testNotValidReuse() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - // create two reusable task with the same name - guard let task = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true) else { - XCTFail("Failed to setup test conditions") - return - } - - XCTAssertTrue(task.isRunning, "Task should be running") - - task.stop() - - XCTAssertFalse(task.isRunning, "Task should be stopped after stop") - - task.reuse() - - XCTAssertFalse(task.isRunning, "Task should be stopped after one stop call, even if reuse is called after") - } - - func testValidReuse() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) - - // create two reusable task with the same name - guard let task = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true) else { - XCTFail("Failed to setup test conditions") - return - } - - XCTAssertTrue(task.isRunning, "Task should be running") - - task.reuse() - - XCTAssertTrue(task.isRunning, "Task should be still running") - - task.stop() - - XCTAssertTrue(task.isRunning, "Task should be still running after one stop call") - - task.stop() - - XCTAssertFalse(task.isRunning, "Task should be stopped after two stop calls") - } - - func testBrokenApp() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.mockBroken) - - // create two reusable task with the same name - let task = service.startBackgroundTask(withName: Constants.bgTaskName) - - XCTAssertNil(task, "Task should not be created") - } - - func testNoTimeApp() { - let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.mockAboutToSuspend) - - // create two reusable task with the same name - let task = service.startBackgroundTask(withName: Constants.bgTaskName) - - XCTAssertNil(task, "Task should not be created") - } -} diff --git a/UnitTests/Sources/MediaProvider/MediaProviderTests.swift b/UnitTests/Sources/MediaProvider/MediaProviderTests.swift index 4dd30f93c..fd45c2874 100644 --- a/UnitTests/Sources/MediaProvider/MediaProviderTests.swift +++ b/UnitTests/Sources/MediaProvider/MediaProviderTests.swift @@ -22,15 +22,13 @@ import XCTest final class MediaProviderTests: XCTestCase { private let mediaLoader = MockMediaLoader() private var imageCache: MockImageCache! - private var backgroundTaskService = MockBackgroundTaskService() var mediaProvider: MediaProvider! override func setUp() { imageCache = MockImageCache(name: "Test") mediaProvider = MediaProvider(mediaLoader: mediaLoader, - imageCache: imageCache, - backgroundTaskService: backgroundTaskService) + imageCache: imageCache) } func test_whenImageFromSourceWithSourceNil_nilReturned() throws { diff --git a/UnitTests/Sources/MediaProvider/MockBackgroundTaskService.swift b/UnitTests/Sources/MediaProvider/MockBackgroundTaskService.swift deleted file mode 100644 index 1a45f9d63..000000000 --- a/UnitTests/Sources/MediaProvider/MockBackgroundTaskService.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright 2022 New Vector Ltd -// -// 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. -// -@testable import ElementX -import Foundation - -class MockBackgroundTaskService: BackgroundTaskServiceProtocol { - func startBackgroundTask(withName name: String, isReusable: Bool, expirationHandler: (() -> Void)?) -> ElementX.BackgroundTaskProtocol? { - nil - } -} diff --git a/UnitTests/Sources/RoomFlowCoordinatorTests.swift b/UnitTests/Sources/RoomFlowCoordinatorTests.swift index 4d5da5f26..82b49828a 100644 --- a/UnitTests/Sources/RoomFlowCoordinatorTests.swift +++ b/UnitTests/Sources/RoomFlowCoordinatorTests.swift @@ -295,8 +295,7 @@ class RoomFlowCoordinatorTests: XCTestCase { appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, - userIndicatorController: ServiceLocator.shared.userIndicatorController, - orientationManager: OrientationManagerMock()) + userIndicatorController: ServiceLocator.shared.userIndicatorController) } } diff --git a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift index 9ee8bd640..b8d09aaa6 100644 --- a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift +++ b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift @@ -46,7 +46,6 @@ class UserSessionFlowCoordinatorTests: XCTestCase { userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, navigationRootCoordinator: navigationRootCoordinator, - windowManager: WindowManagerMock(), appLockService: AppLockServiceMock(), bugReportService: BugReportServiceMock(), roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), diff --git a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift index 590fe8396..0ba81a31a 100644 --- a/UnitTests/Sources/VoiceMessageMediaManagerTests.swift +++ b/UnitTests/Sources/VoiceMessageMediaManagerTests.swift @@ -32,8 +32,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageCache = VoiceMessageCacheMock() mediaProvider = MockMediaProvider() voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, - voiceMessageCache: voiceMessageCache, - backgroundTaskService: MockBackgroundTaskService()) + voiceMessageCache: voiceMessageCache) } func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws { @@ -65,8 +64,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache, - audioConverter: AudioConverterMock(), - backgroundTaskService: MockBackgroundTaskService()) + audioConverter: AudioConverterMock()) do { _ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) @@ -119,8 +117,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache, - audioConverter: audioConverter, - backgroundTaskService: MockBackgroundTaskService()) + audioConverter: audioConverter) let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) // The file must have been converted @@ -155,8 +152,7 @@ class VoiceMessageMediaManagerTests: XCTestCase { voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageCache: voiceMessageCache, - audioConverter: audioConverter, - backgroundTaskService: MockBackgroundTaskService()) + audioConverter: audioConverter) let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) for _ in 0..<10 {