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`
This commit is contained in:
Stefan Ceriu 2024-04-22 18:10:24 +03:00 committed by GitHub
parent a3ec0b8505
commit 8ba544bc44
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 117 additions and 929 deletions

View File

@ -50,7 +50,6 @@
07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; }; 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; };
086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9104846487244648D32C6D /* AudioPlayerProtocol.swift */; }; 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9104846487244648D32C6D /* AudioPlayerProtocol.swift */; };
08CB4BD12CEEDE6AAE4A18DD /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035177BCD8E8308B098AC3C2 /* WindowManager.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 */; }; 095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; };
095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; }; 095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; };
09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; }; 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 */; }; 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = E8CA187FE656EE5A3F6C7DE5 /* UIFont+AttributedStringBuilder.m */; };
0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; }; 0EEC614342F823E5BF966C2C /* AppLockTimerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A5B4CD611DE7E94F5BA87B2 /* AppLockTimerTests.swift */; };
0F6C8033FA60CFD36F7CA205 /* AppLockSetupPINScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A019A12C866D64CF072024B9 /* AppLockSetupPINScreenViewModel.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 */; }; 10D60D287025B71F4743A425 /* RoomDirectorySearchProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 471BB7276C97AF60B3A5463B /* RoomDirectorySearchProxy.swift */; };
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; }; 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0376C429FAB1687C3D905F3E /* MockCoder.swift */; };
119AE9A3FC6E0606C1146528 /* NotificationSettingsEditScreenRoomCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97F8963B14EB0AF3940DDBF /* NotificationSettingsEditScreenRoomCell.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 */; }; 4E0D9E09B52CEC4C0E6211A8 /* MediaPickerScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */; };
4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; }; 4E36A66E0EDA74BF3A036FD0 /* RoomChangeRolesScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AAD8C633AA57948B34EDCF7 /* RoomChangeRolesScreenViewModelProtocol.swift */; };
4E8A2A2CFEB212F14E49E1A1 /* AppLockSetupSettingsScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5484457C81B325660901B161 /* AppLockSetupSettingsScreen.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 */; }; 4E945AD6862C403F74E57755 /* RoomTimelineItemFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 105B2A8426404EF66F00CFDB /* RoomTimelineItemFactory.swift */; };
4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; }; 4EA1CE0E88EA68E862FF0EA2 /* NotificationSettingsEditScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B564D748B67A156F413CD97 /* NotificationSettingsEditScreenModels.swift */; };
4F2DF6138E87A4B8C2488CA3 /* VoiceMessageCacheProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43A84EE187D0C772E18A4E39 /* VoiceMessageCacheProtocol.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 */; }; B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */; };
B245583C63F8F90357B87FAE /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = A2AE110B053B55E38F8D10C7 /* KZFileWatchers */; }; B245583C63F8F90357B87FAE /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = A2AE110B053B55E38F8D10C7 /* KZFileWatchers */; };
B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */; }; 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 */; }; B3D652AA1654270742072FB3 /* DeveloperOptionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */; };
B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A1CCDEE545CB6453B084BF /* FormButtonStyles.swift */; }; B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A1CCDEE545CB6453B084BF /* FormButtonStyles.swift */; };
B402708F8728DD0DB7C324E2 /* StartChatScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.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 */; }; C9F5B48D15B9BCAE1F8D564E /* RoomNotificationModeProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */; };
CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; }; CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; };
CA5BFF0C2EF5A8EF40CA2D69 /* VoiceMessageRecordingComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB6F36CCE44A29A06FCAF1C /* VoiceMessageRecordingComposer.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 */; }; CB137BFB3E083C33E398A6CB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; };
CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; };
CB6BCBF28E4B76EA08C2926D /* StateRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16048D30F0438731C41F775 /* StateRoomTimelineItem.swift */; }; 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 */; }; D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; };
D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; }; D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; };
D6661A94DBD97658B2ADBD6A /* MapTilerStaticMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */; }; 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 */; }; D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; };
D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; }; D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; };
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.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 */; }; D9473FC9B077A6EDB7A12001 /* LocationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 772334731A8BF8E6D90B194D /* LocationRoomTimelineView.swift */; };
D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; };
D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.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 */; }; E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; };
E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.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 */; }; 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 */; }; E45C9FA22BC13B477FD3B4AC /* EmojiDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */; };
E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; }; E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; };
E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; }; 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 = "<group>"; }; 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerTests.swift; sourceTree = "<group>"; };
314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceipt.swift; sourceTree = "<group>"; }; 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceipt.swift; sourceTree = "<group>"; };
317F41A4B5C4F457AF710666 /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = "<group>"; }; 317F41A4B5C4F457AF710666 /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = "<group>"; };
3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = "<group>"; };
31A6314FDC51DA25712D9A81 /* PillContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillContextTests.swift; sourceTree = "<group>"; }; 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillContextTests.swift; sourceTree = "<group>"; };
31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; }; 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; };
3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsUserDefinedScreen.swift; sourceTree = "<group>"; }; 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsUserDefinedScreen.swift; sourceTree = "<group>"; };
@ -1369,7 +1360,6 @@
38345442415E07A931197C55 /* AppLockScreenPINKeypad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenPINKeypad.swift; sourceTree = "<group>"; }; 38345442415E07A931197C55 /* AppLockScreenPINKeypad.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenPINKeypad.swift; sourceTree = "<group>"; };
38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenViewModel.swift; sourceTree = "<group>"; }; 38354164AF59C5006CD05878 /* GlobalSearchScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalSearchScreenViewModel.swift; sourceTree = "<group>"; };
38E521D6C2BF8DF0DFB35146 /* DeveloperOptionsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreen.swift; sourceTree = "<group>"; }; 38E521D6C2BF8DF0DFB35146 /* DeveloperOptionsScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreen.swift; sourceTree = "<group>"; };
3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBackgroundTaskService.swift; sourceTree = "<group>"; };
3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenScreenModelProtocol.swift; sourceTree = "<group>"; }; 3984C93B8E9B10C92DADF9EE /* RoomDirectorySearchScreenScreenModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDirectorySearchScreenScreenModelProtocol.swift; sourceTree = "<group>"; };
398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = "<group>"; }; 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = "<group>"; };
39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = "<group>"; }; 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = "<group>"; };
@ -1561,7 +1551,6 @@
6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreen.swift; sourceTree = "<group>"; }; 6DC30DEC0097B9D217493007 /* ResetRecoveryKeyScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetRecoveryKeyScreen.swift; sourceTree = "<group>"; };
6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatViewModelTests.swift; sourceTree = "<group>"; }; 6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StartChatViewModelTests.swift; sourceTree = "<group>"; };
6DF81D7F2A6BA9DE3F6F8D9D /* InvitesScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModel.swift; sourceTree = "<group>"; }; 6DF81D7F2A6BA9DE3F6F8D9D /* InvitesScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvitesScreenViewModel.swift; sourceTree = "<group>"; };
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskTests.swift; sourceTree = "<group>"; };
6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleRoomTimelineView.swift; sourceTree = "<group>"; }; 6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleRoomTimelineView.swift; sourceTree = "<group>"; };
6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelTests.swift; sourceTree = "<group>"; }; 6E5725BC6C63604CB769145B /* LegalInformationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LegalInformationScreenViewModelTests.swift; sourceTree = "<group>"; };
6E5E9C044BEB7C70B1378E91 /* UserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = "<group>"; }; 6E5E9C044BEB7C70B1378E91 /* UserSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = "<group>"; };
@ -1645,7 +1634,6 @@
8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = "<group>"; }; 8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = "<group>"; };
837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = "<group>"; }; 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = "<group>"; };
839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = "<group>"; }; 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = "<group>"; };
83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskProtocol.swift; sourceTree = "<group>"; };
84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; }; 84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = "<group>"; }; 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = "<group>"; };
848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; }; 848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
@ -1965,7 +1953,6 @@
D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = "<group>"; }; D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = "<group>"; };
D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; }; D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenCoordinator.swift; sourceTree = "<group>"; }; D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenCoordinator.swift; sourceTree = "<group>"; };
D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = "<group>"; };
D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = "<group>"; }; D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = "<group>"; };
D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformViewVersionPredicate.swift; sourceTree = "<group>"; }; D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformViewVersionPredicate.swift; sourceTree = "<group>"; };
D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFlowCoordinator.swift; sourceTree = "<group>"; }; D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFlowCoordinator.swift; sourceTree = "<group>"; };
@ -2067,7 +2054,6 @@
EA880E78AF4BD24E45A7808C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = "<group>"; }; EA880E78AF4BD24E45A7808C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = "<group>"; };
EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSection.swift; sourceTree = "<group>"; }; EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSection.swift; sourceTree = "<group>"; };
EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = "<group>"; }; EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = "<group>"; };
EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = "<group>"; };
EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenModels.swift; sourceTree = "<group>"; }; EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenModels.swift; sourceTree = "<group>"; };
EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenViewModel.swift; sourceTree = "<group>"; }; EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenViewModel.swift; sourceTree = "<group>"; };
EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = "<group>"; }; EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = "<group>"; };
@ -2327,7 +2313,6 @@
7803E03F759061C948D66B7E /* AppLock */, 7803E03F759061C948D66B7E /* AppLock */,
FCE7249621F507F34A8122FB /* Audio */, FCE7249621F507F34A8122FB /* Audio */,
AAFDD509929A0CCF8BCE51EB /* Authentication */, AAFDD509929A0CCF8BCE51EB /* Authentication */,
C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */,
0ED3F5C21537519389C07644 /* BugReport */, 0ED3F5C21537519389C07644 /* BugReport */,
8039515BAA53B7C3275AC64A /* Client */, 8039515BAA53B7C3275AC64A /* Client */,
8C3BAE06B336D97DABBE2509 /* CreateRoom */, 8C3BAE06B336D97DABBE2509 /* CreateRoom */,
@ -3551,7 +3536,6 @@
C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */, C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */,
2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */, 2441E2424E78A40FC95DBA76 /* AudioRecorderTests.swift */,
8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */, 8FB89DC7F9A4A91020037001 /* AuthenticationStartScreenViewModelTests.swift */,
6DFCAA239095A116976E32C4 /* BackgroundTaskTests.swift */,
240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */, 240610DF32F3213BEC5611D7 /* BlockedUsersScreenViewModelTests.swift */,
EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */, EFFD3200F9960D4996159F10 /* BugReportServiceTests.swift */,
7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */, 7AB7ED3A898B07976F3AA90F /* BugReportViewModelTests.swift */,
@ -3689,7 +3673,6 @@
children = ( children = (
3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */, 3EF1AC723C2609C7705569CA /* MediaLoaderTests.swift */,
62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */, 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */,
3948D16F021DFDB2CD26EAA8 /* MockBackgroundTaskService.swift */,
AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */, AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */,
4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */, 4AB7D7DAAAF662DED9D02379 /* MockMediaLoader.swift */,
); );
@ -4672,17 +4655,6 @@
path = RoomDirectorySearchScreen; path = RoomDirectorySearchScreen;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */ = {
isa = PBXGroup;
children = (
83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */,
D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */,
EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */,
3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */,
);
path = BackgroundTasks;
sourceTree = "<group>";
};
C844840F3DD48A154C65AE0C /* View */ = { C844840F3DD48A154C65AE0C /* View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -5661,8 +5633,6 @@
CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */, CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */,
BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */, BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */,
968A5B890004526AB58A217C /* AvatarSize.swift in Sources */, 968A5B890004526AB58A217C /* AvatarSize.swift in Sources */,
08E02C56E8B277A22C5E5BA5 /* BackgroundTaskProtocol.swift in Sources */,
D8ED2FCF46D63A7357E9D502 /* BackgroundTaskServiceProtocol.swift in Sources */,
9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */, 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */,
B5618E3C948584E5C1F67033 /* DTHTMLElement+AttributedStringBuilder.swift in Sources */, B5618E3C948584E5C1F67033 /* DTHTMLElement+AttributedStringBuilder.swift in Sources */,
DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */, DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */,
@ -5736,7 +5706,6 @@
3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */, 3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */,
192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */, 192A3CDCD0174AD1E4A128E4 /* AudioRecorderTests.swift in Sources */,
8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */, 8ED8AF57A06F5EE9978ED23F /* AuthenticationStartScreenViewModelTests.swift in Sources */,
0F9E38A75337D0146652ACAB /* BackgroundTaskTests.swift in Sources */,
CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */, CEAEA57B7665C8E790599A78 /* BlockedUsersScreenViewModelTests.swift in Sources */,
7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */, 7F61F9ACD5EC9E845EF3EFBF /* BugReportServiceTests.swift in Sources */,
C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */, C7CFDB4929DDD9A3B5BA085D /* BugReportViewModelTests.swift in Sources */,
@ -5772,7 +5741,6 @@
B9A8C34A00D03094C0CF56F3 /* MediaUploadPreviewScreenViewModelTests.swift in Sources */, B9A8C34A00D03094C0CF56F3 /* MediaUploadPreviewScreenViewModelTests.swift in Sources */,
23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */, 23701DE32ACD6FD40AA992C3 /* MediaUploadingPreprocessorTests.swift in Sources */,
F777C6FEE7D106136E2ED2B2 /* MessageForwardingScreenViewModelTests.swift in Sources */, F777C6FEE7D106136E2ED2B2 /* MessageForwardingScreenViewModelTests.swift in Sources */,
4E8F17EBA24FBBA6ABB62ECB /* MockBackgroundTaskService.swift in Sources */,
1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */, 1146E9EDCF8344F7D6E0D553 /* MockCoder.swift in Sources */,
DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */, DC68E866D6E664B0D2B06E74 /* MockImageCache.swift in Sources */,
A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */, A896998A6784DB6F16E912F4 /* MockMediaLoader.swift in Sources */,
@ -5944,8 +5912,6 @@
6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */, 6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */,
4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */, 4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */,
D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */, D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */,
B3066502FA56D9199846C5E7 /* BackgroundTaskProtocol.swift in Sources */,
D798A150BB6569FC07CBEB25 /* BackgroundTaskServiceProtocol.swift in Sources */,
A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */, A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */,
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */, 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */,
EB9F4688006B52E69DF5358F /* BlankFormCoordinator.swift in Sources */, EB9F4688006B52E69DF5358F /* BlankFormCoordinator.swift in Sources */,
@ -6541,8 +6507,6 @@
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */, 36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */, A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */,
0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */, 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */,
E3E3D645C7F80DD64A86D936 /* UIKitBackgroundTask.swift in Sources */,
CB0C53B67DA62F7E30DDD720 /* UIKitBackgroundTaskService.swift in Sources */,
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */, E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */,
384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */, 384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */,
22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */, 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */,

View File

@ -20,7 +20,7 @@ import MatrixRustSDK
import SwiftUI import SwiftUI
import Version import Version
class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, WindowManagerDelegate { class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDelegate, NotificationManagerDelegate, SecureWindowManagerDelegate {
private let stateMachine: AppCoordinatorStateMachine private let stateMachine: AppCoordinatorStateMachine
private let navigationRootCoordinator: NavigationRootCoordinator private let navigationRootCoordinator: NavigationRootCoordinator
private let userSessionStore: UserSessionStoreProtocol private let userSessionStore: UserSessionStoreProtocol
@ -29,7 +29,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private let appDelegate: AppDelegate private let appDelegate: AppDelegate
/// Common background task to continue long-running tasks in the background. /// Common background task to continue long-running tasks in the background.
private var backgroundTask: BackgroundTaskProtocol? private var backgroundTask: UIBackgroundTaskIdentifier?
private var isSuspended = false private var isSuspended = false
@ -50,15 +50,12 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator? private var appLockSetupFlowCoordinator: AppLockSetupFlowCoordinator?
private var userSessionFlowCoordinator: UserSessionFlowCoordinator? private var userSessionFlowCoordinator: UserSessionFlowCoordinator?
private var softLogoutCoordinator: SoftLogoutScreenCoordinator? private var softLogoutCoordinator: SoftLogoutScreenCoordinator?
private let backgroundTaskService: BackgroundTaskServiceProtocol
private var appDelegateObserver: AnyCancellable? private var appDelegateObserver: AnyCancellable?
private var userSessionObserver: AnyCancellable? private var userSessionObserver: AnyCancellable?
private var clientProxyObserver: AnyCancellable? private var clientProxyObserver: AnyCancellable?
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
let windowManager: WindowManagerProtocol let windowManager: SecureWindowManagerProtocol
let notificationManager: NotificationManagerProtocol let notificationManager: NotificationManagerProtocol
private let appRouteURLParser: AppRouteURLParser private let appRouteURLParser: AppRouteURLParser
@ -103,12 +100,9 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator()) navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
backgroundTaskService = UIKitBackgroundTaskService(appMediator: appMediator)
let keychainController = KeychainController(service: .sessions, let keychainController = KeychainController(service: .sessions,
accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
userSessionStore = UserSessionStore(keychainController: keychainController, userSessionStore = UserSessionStore(keychainController: keychainController)
backgroundTaskService: backgroundTaskService)
let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings) let appLockService = AppLockService(keychainController: keychainController, appSettings: appSettings)
let appLockNavigationCoordinator = NavigationRootCoordinator() let appLockNavigationCoordinator = NavigationRootCoordinator()
@ -227,7 +221,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
// MARK: - WindowManagerDelegate // MARK: - WindowManagerDelegate
func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) { func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) {
windowManager.alternateWindow.rootViewController = UIHostingController(rootView: appLockFlowCoordinator.toPresentable()) windowManager.alternateWindow.rootViewController = UIHostingController(rootView: appLockFlowCoordinator.toPresentable())
ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow
} }
@ -413,8 +407,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings, appSettings: appSettings,
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController)
orientationManager: windowManager)
authenticationFlowCoordinator?.delegate = self authenticationFlowCoordinator?.delegate = self
authenticationFlowCoordinator?.start() authenticationFlowCoordinator?.start()
@ -469,7 +462,6 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, let userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession,
navigationRootCoordinator: navigationRootCoordinator, navigationRootCoordinator: navigationRootCoordinator,
windowManager: windowManager,
appLockService: appLockFlowCoordinator.appLockService, appLockService: appLockFlowCoordinator.appLockService,
bugReportService: ServiceLocator.shared.bugReportService, bugReportService: ServiceLocator.shared.bugReportService,
roomTimelineControllerFactory: RoomTimelineControllerFactory(), roomTimelineControllerFactory: RoomTimelineControllerFactory(),
@ -770,14 +762,16 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
guard backgroundTask == nil else { guard backgroundTask == nil else {
return return
} }
backgroundTask = backgroundTaskService.startBackgroundTask(withName: "SuspendApp: \(UUID().uuidString)") { [weak self] in backgroundTask = appMediator.beginBackgroundTask { [weak self] in
guard let self else { return } guard let self else { return }
stopSync() stopSync()
backgroundTask?.stop() if let backgroundTask {
backgroundTask = nil appMediator.endBackgroundTask(backgroundTask)
self.backgroundTask = nil
}
} }
isSuspended = true isSuspended = true
@ -791,8 +785,10 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
private func applicationDidBecomeActive() { private func applicationDidBecomeActive() {
MXLog.info("Application did become active") MXLog.info("Application did become active")
backgroundTask?.stop() if let backgroundTask {
backgroundTask = nil appMediator.endBackgroundTask(backgroundTask)
self.backgroundTask = nil
}
if isSuspended { if isSuspended {
startSync() startSync()

View File

@ -17,6 +17,6 @@
import Foundation import Foundation
protocol AppCoordinatorProtocol: CoordinatorProtocol { protocol AppCoordinatorProtocol: CoordinatorProtocol {
var windowManager: WindowManagerProtocol { get } var windowManager: SecureWindowManagerProtocol { get }
@discardableResult func handleDeepLink(_ url: URL, isExternalURL: Bool) -> Bool @discardableResult func handleDeepLink(_ url: URL, isExternalURL: Bool) -> Bool
} }

View File

@ -17,7 +17,7 @@
import UIKit import UIKit
class AppMediator: AppMediatorProtocol { class AppMediator: AppMediatorProtocol {
private let windowManager: WindowManagerProtocol let windowManager: WindowManagerProtocol
init(windowManager: WindowManagerProtocol) { init(windowManager: WindowManagerProtocol) {
self.windowManager = windowManager self.windowManager = windowManager
@ -28,7 +28,6 @@ class AppMediator: AppMediatorProtocol {
UIApplication.shared UIApplication.shared
} }
@MainActor
var appState: UIApplication.State { var appState: UIApplication.State {
switch application.applicationState { switch application.applicationState {
case .active: case .active:
@ -42,12 +41,8 @@ class AppMediator: AppMediatorProtocol {
} }
} }
var backgroundTimeRemaining: TimeInterval { func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
application.backgroundTimeRemaining application.beginBackgroundTask(expirationHandler: handler)
}
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
application.beginBackgroundTask(withName: taskName, expirationHandler: handler)
} }
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) { func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {

View File

@ -18,12 +18,13 @@ import Foundation
import UIKit import UIKit
// sourcery: AutoMockable // sourcery: AutoMockable
@MainActor
protocol AppMediatorProtocol { protocol AppMediatorProtocol {
var windowManager: WindowManagerProtocol { get }
var appState: UIApplication.State { get } var appState: UIApplication.State { get }
var backgroundTimeRemaining: TimeInterval { get } func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier)

View File

@ -20,7 +20,7 @@ import SwiftUI
/// ///
/// We don't support multiple scenes right now, so the implementation is pretty basic. /// We don't support multiple scenes right now, so the implementation is pretty basic.
class SceneDelegate: NSObject, UIWindowSceneDelegate { 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) { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return } guard let windowScene = scene as? UIWindowScene else { return }

View File

@ -17,10 +17,10 @@
import Combine import Combine
import SwiftUI import SwiftUI
class WindowManager: WindowManagerProtocol { class WindowManager: SecureWindowManagerProtocol {
private let appDelegate: AppDelegate private let appDelegate: AppDelegate
weak var windowScene: UIWindowScene? weak var windowScene: UIWindowScene?
weak var delegate: WindowManagerDelegate? weak var delegate: SecureWindowManagerDelegate?
private(set) var mainWindow: UIWindow! private(set) var mainWindow: UIWindow!
private(set) var overlayWindow: UIWindow! private(set) var overlayWindow: UIWindow!

View File

@ -16,18 +16,29 @@
import SwiftUI import SwiftUI
protocol WindowManagerDelegate: AnyObject { protocol SecureWindowManagerDelegate: AnyObject {
/// The window manager has configured its windows. /// The window manager has configured its windows.
func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol)
} }
@MainActor @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 /// 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. /// an alternate window to switch contexts whilst also preserving the main view hierarchy.
/// Heavily inspired by https://www.fivestars.blog/articles/swiftui-windows/ /// Heavily inspired by https://www.fivestars.blog/articles/swiftui-windows/
protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol { protocol WindowManagerProtocol: AnyObject, OrientationManagerProtocol {
var delegate: WindowManagerDelegate? { get set }
/// The app's main window (we only support a single scene). /// The app's main window (we only support a single scene).
var mainWindow: UIWindow! { get } var mainWindow: UIWindow! { get }
/// Presented on top of the main window, to display e.g. user indicators. /// 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 /// All the windows being managed
var windows: [UIWindow] { get } 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. /// Makes the global search window key. Used to get automatic text field focus.
func showGlobalSearch() func showGlobalSearch()

View File

@ -31,7 +31,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsService
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let orientationManager: OrientationManagerProtocol
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
@ -48,8 +47,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analytics: AnalyticsService, analytics: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol) {
orientationManager: WindowManagerProtocol) {
self.authenticationService = authenticationService self.authenticationService = authenticationService
self.bugReportService = bugReportService self.bugReportService = bugReportService
self.navigationRootCoordinator = navigationRootCoordinator self.navigationRootCoordinator = navigationRootCoordinator
@ -57,7 +55,6 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
self.appSettings = appSettings self.appSettings = appSettings
self.analytics = analytics self.analytics = analytics
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.orientationManager = orientationManager
navigationStackCoordinator = NavigationStackCoordinator() navigationStackCoordinator = NavigationStackCoordinator()
} }
@ -110,7 +107,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol {
private func startQRCodeLogin() { private func startQRCodeLogin() {
let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(), let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(),
orientationManager: orientationManager, orientationManager: appMediator.windowManager,
appMediator: appMediator)) appMediator: appMediator))
coordinator.actionsPublisher.sink { [weak self] action in coordinator.actionsPublisher.sink { [weak self] action in
guard let self else { guard let self else {

View File

@ -50,7 +50,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
private let appSettings: AppSettings private let appSettings: AppSettings
private let analytics: AnalyticsService private let analytics: AnalyticsService
private let userIndicatorController: UserIndicatorControllerProtocol private let userIndicatorController: UserIndicatorControllerProtocol
private let orientationManager: OrientationManagerProtocol
private var roomProxy: RoomProxyProtocol! private var roomProxy: RoomProxyProtocol!
@ -81,8 +80,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
appMediator: AppMediatorProtocol, appMediator: AppMediatorProtocol,
appSettings: AppSettings, appSettings: AppSettings,
analytics: AnalyticsService, analytics: AnalyticsService,
userIndicatorController: UserIndicatorControllerProtocol, userIndicatorController: UserIndicatorControllerProtocol) async {
orientationManager: OrientationManagerProtocol) async {
self.roomID = roomID self.roomID = roomID
self.userSession = userSession self.userSession = userSession
self.isChildFlow = isChildFlow self.isChildFlow = isChildFlow
@ -93,7 +91,6 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
self.appSettings = appSettings self.appSettings = appSettings
self.analytics = analytics self.analytics = analytics
self.userIndicatorController = userIndicatorController self.userIndicatorController = userIndicatorController
self.orientationManager = orientationManager
setupStateMachine() setupStateMachine()
@ -695,7 +692,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
mediaProvider: userSession.mediaProvider, mediaProvider: userSession.mediaProvider,
navigationStackCoordinator: stackCoordinator, navigationStackCoordinator: stackCoordinator,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController,
orientationManager: orientationManager) orientationManager: appMediator.windowManager)
let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters) let roomDetailsEditCoordinator = RoomDetailsEditScreenCoordinator(parameters: roomDetailsEditParameters)
roomDetailsEditCoordinator.actions.sink { [weak self] action in roomDetailsEditCoordinator.actions.sink { [weak self] action in
@ -752,7 +749,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController, let mediaPickerCoordinator = MediaPickerScreenCoordinator(userIndicatorController: userIndicatorController,
source: source, source: source,
orientationManager: orientationManager) { [weak self] action in orientationManager: appMediator.windowManager) { [weak self] action in
guard let self else { guard let self else {
return return
} }
@ -1239,8 +1236,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings, appSettings: appSettings,
analytics: analytics, analytics: analytics,
userIndicatorController: userIndicatorController, userIndicatorController: userIndicatorController)
orientationManager: orientationManager)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }

View File

@ -28,7 +28,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
private let userSession: UserSessionProtocol private let userSession: UserSessionProtocol
private let navigationRootCoordinator: NavigationRootCoordinator private let navigationRootCoordinator: NavigationRootCoordinator
private let navigationSplitCoordinator: NavigationSplitCoordinator private let navigationSplitCoordinator: NavigationSplitCoordinator
private let windowManager: WindowManagerProtocol
private let bugReportService: BugReportServiceProtocol private let bugReportService: BugReportServiceProtocol
private let appMediator: AppMediatorProtocol private let appMediator: AppMediatorProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
@ -68,7 +67,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
init(userSession: UserSessionProtocol, init(userSession: UserSessionProtocol,
navigationRootCoordinator: NavigationRootCoordinator, navigationRootCoordinator: NavigationRootCoordinator,
windowManager: WindowManagerProtocol,
appLockService: AppLockServiceProtocol, appLockService: AppLockServiceProtocol,
bugReportService: BugReportServiceProtocol, bugReportService: BugReportServiceProtocol,
roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol,
@ -80,7 +78,6 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
stateMachine = UserSessionFlowCoordinatorStateMachine() stateMachine = UserSessionFlowCoordinatorStateMachine()
self.userSession = userSession self.userSession = userSession
self.navigationRootCoordinator = navigationRootCoordinator self.navigationRootCoordinator = navigationRootCoordinator
self.windowManager = windowManager
self.bugReportService = bugReportService self.bugReportService = bugReportService
self.roomTimelineControllerFactory = roomTimelineControllerFactory self.roomTimelineControllerFactory = roomTimelineControllerFactory
self.appMediator = appMediator self.appMediator = appMediator
@ -96,7 +93,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator) navigationSplitCoordinator.setSidebarCoordinator(sidebarNavigationStackCoordinator)
settingsFlowCoordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession, settingsFlowCoordinator = SettingsFlowCoordinator(parameters: .init(userSession: userSession,
windowManager: windowManager, windowManager: appMediator.windowManager,
appLockService: appLockService, appLockService: appLockService,
bugReportService: bugReportService, bugReportService: bugReportService,
notificationSettings: userSession.clientProxy.notificationSettings, notificationSettings: userSession.clientProxy.notificationSettings,
@ -427,8 +424,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
appMediator: appMediator, appMediator: appMediator,
appSettings: appSettings, appSettings: appSettings,
analytics: analytics, analytics: analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController)
orientationManager: windowManager)
coordinator.actions.sink { [weak self] action in coordinator.actions.sink { [weak self] action in
guard let self else { return } guard let self else { return }
@ -480,7 +476,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let startChatNavigationStackCoordinator = NavigationStackCoordinator() let startChatNavigationStackCoordinator = NavigationStackCoordinator()
let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy) let userDiscoveryService = UserDiscoveryService(clientProxy: userSession.clientProxy)
let parameters = StartChatScreenCoordinatorParameters(orientationManager: windowManager, let parameters = StartChatScreenCoordinatorParameters(orientationManager: appMediator.windowManager,
userSession: userSession, userSession: userSession,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController,
navigationStackCoordinator: startChatNavigationStackCoordinator, navigationStackCoordinator: startChatNavigationStackCoordinator,
@ -609,14 +605,14 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol {
let hostingController = UIHostingController(rootView: coordinator.toPresentable()) let hostingController = UIHostingController(rootView: coordinator.toPresentable())
hostingController.view.backgroundColor = .clear hostingController.view.backgroundColor = .clear
windowManager.globalSearchWindow.rootViewController = hostingController appMediator.windowManager.globalSearchWindow.rootViewController = hostingController
windowManager.showGlobalSearch() appMediator.windowManager.showGlobalSearch()
} }
private func dismissGlobalSearch() { private func dismissGlobalSearch() {
windowManager.globalSearchWindow.rootViewController = nil appMediator.windowManager.globalSearchWindow.rootViewController = nil
windowManager.hideGlobalSearch() appMediator.windowManager.hideGlobalSearch()
globalSearchScreenCoordinator = nil globalSearchScreenCoordinator = nil
} }

View File

@ -18,44 +18,11 @@ import UIKit
extension AppMediatorMock { extension AppMediatorMock {
static var `default`: AppMediatorProtocol { static var `default`: AppMediatorProtocol {
AppMediatorMock(withState: .active, let mock = AppMediatorMock()
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()
underlyingAppState = applicationState mock.underlyingAppState = .active
underlyingBackgroundTimeRemaining = backgroundTimeRemaining mock.underlyingWindowManager = WindowManagerMock()
beginBackgroundTaskWithNameExpirationHandlerClosure = { _, handler in return mock
guard allowTasks else {
return .invalid
}
Self.bgTaskIdentifier += 1
let identifier = UIBackgroundTaskIdentifier(rawValue: Self.bgTaskIdentifier)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
handler?()
}
return identifier
}
} }
} }

View File

@ -748,28 +748,28 @@ class AppLockServiceMock: AppLockServiceProtocol {
} }
} }
class AppMediatorMock: AppMediatorProtocol { class AppMediatorMock: AppMediatorProtocol {
var windowManager: WindowManagerProtocol {
get { return underlyingWindowManager }
set(value) { underlyingWindowManager = value }
}
var underlyingWindowManager: WindowManagerProtocol!
var appState: UIApplication.State { var appState: UIApplication.State {
get { return underlyingAppState } get { return underlyingAppState }
set(value) { underlyingAppState = value } set(value) { underlyingAppState = value }
} }
var underlyingAppState: UIApplication.State! var underlyingAppState: UIApplication.State!
var backgroundTimeRemaining: TimeInterval {
get { return underlyingBackgroundTimeRemaining }
set(value) { underlyingBackgroundTimeRemaining = value }
}
var underlyingBackgroundTimeRemaining: TimeInterval!
//MARK: - beginBackgroundTask //MARK: - beginBackgroundTask
var beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = 0 var beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = 0
var beginBackgroundTaskWithNameExpirationHandlerCallsCount: Int { var beginBackgroundTaskExpirationHandlerCallsCount: Int {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount return beginBackgroundTaskExpirationHandlerUnderlyingCallsCount
} else { } else {
var returnValue: Int? = nil var returnValue: Int? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount returnValue = beginBackgroundTaskExpirationHandlerUnderlyingCallsCount
} }
return returnValue! return returnValue!
@ -777,27 +777,27 @@ class AppMediatorMock: AppMediatorProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = newValue beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = newValue
} else { } else {
DispatchQueue.main.sync { DispatchQueue.main.sync {
beginBackgroundTaskWithNameExpirationHandlerUnderlyingCallsCount = newValue beginBackgroundTaskExpirationHandlerUnderlyingCallsCount = newValue
} }
} }
} }
} }
var beginBackgroundTaskWithNameExpirationHandlerCalled: Bool { var beginBackgroundTaskExpirationHandlerCalled: Bool {
return beginBackgroundTaskWithNameExpirationHandlerCallsCount > 0 return beginBackgroundTaskExpirationHandlerCallsCount > 0
} }
var beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue: UIBackgroundTaskIdentifier! var beginBackgroundTaskExpirationHandlerUnderlyingReturnValue: UIBackgroundTaskIdentifier!
var beginBackgroundTaskWithNameExpirationHandlerReturnValue: UIBackgroundTaskIdentifier! { var beginBackgroundTaskExpirationHandlerReturnValue: UIBackgroundTaskIdentifier! {
get { get {
if Thread.isMainThread { if Thread.isMainThread {
return beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue return beginBackgroundTaskExpirationHandlerUnderlyingReturnValue
} else { } else {
var returnValue: UIBackgroundTaskIdentifier? = nil var returnValue: UIBackgroundTaskIdentifier? = nil
DispatchQueue.main.sync { DispatchQueue.main.sync {
returnValue = beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue returnValue = beginBackgroundTaskExpirationHandlerUnderlyingReturnValue
} }
return returnValue! return returnValue!
@ -805,22 +805,22 @@ class AppMediatorMock: AppMediatorProtocol {
} }
set { set {
if Thread.isMainThread { if Thread.isMainThread {
beginBackgroundTaskWithNameExpirationHandlerUnderlyingReturnValue = newValue beginBackgroundTaskExpirationHandlerUnderlyingReturnValue = newValue
} else { } else {
DispatchQueue.main.sync { 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 { func beginBackgroundTask(expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
beginBackgroundTaskWithNameExpirationHandlerCallsCount += 1 beginBackgroundTaskExpirationHandlerCallsCount += 1
if let beginBackgroundTaskWithNameExpirationHandlerClosure = beginBackgroundTaskWithNameExpirationHandlerClosure { if let beginBackgroundTaskExpirationHandlerClosure = beginBackgroundTaskExpirationHandlerClosure {
return beginBackgroundTaskWithNameExpirationHandlerClosure(taskName, handler) return beginBackgroundTaskExpirationHandlerClosure(handler)
} else { } else {
return beginBackgroundTaskWithNameExpirationHandlerReturnValue return beginBackgroundTaskExpirationHandlerReturnValue
} }
} }
//MARK: - endBackgroundTask //MARK: - endBackgroundTask
@ -12976,122 +12976,12 @@ class VoiceMessageRecorderMock: VoiceMessageRecorderProtocol {
} }
} }
class WindowManagerMock: WindowManagerProtocol { class WindowManagerMock: WindowManagerProtocol {
weak var delegate: WindowManagerDelegate?
var mainWindow: UIWindow! var mainWindow: UIWindow!
var overlayWindow: UIWindow! var overlayWindow: UIWindow!
var globalSearchWindow: UIWindow! var globalSearchWindow: UIWindow!
var alternateWindow: UIWindow! var alternateWindow: UIWindow!
var windows: [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 //MARK: - showGlobalSearch
var showGlobalSearchUnderlyingCallsCount = 0 var showGlobalSearchUnderlyingCallsCount = 0

View File

@ -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()
}

View File

@ -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)
}
}

View File

@ -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)
}
}

View File

@ -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<NSString, UIKitBackgroundTask>(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
}

View File

@ -23,7 +23,6 @@ import MatrixRustSDK
class ClientProxy: ClientProxyProtocol { class ClientProxy: ClientProxyProtocol {
private let client: ClientProtocol private let client: ClientProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol
private let appSettings: AppSettings private let appSettings: AppSettings
private let networkMonitor: NetworkMonitorProtocol private let networkMonitor: NetworkMonitorProtocol
@ -122,11 +121,9 @@ class ClientProxy: ClientProxyProtocol {
} }
init(client: ClientProtocol, init(client: ClientProtocol,
backgroundTaskService: BackgroundTaskServiceProtocol,
appSettings: AppSettings, appSettings: AppSettings,
networkMonitor: NetworkMonitorProtocol) async { networkMonitor: NetworkMonitorProtocol) async {
self.client = client self.client = client
self.backgroundTaskService = backgroundTaskService
self.appSettings = appSettings self.appSettings = appSettings
self.networkMonitor = networkMonitor self.networkMonitor = networkMonitor
@ -134,8 +131,7 @@ class ClientProxy: ClientProxyProtocol {
mediaLoader = MediaLoader(client: client) mediaLoader = MediaLoader(client: client)
notificationSettings = NotificationSettingsProxy(notificationSettings: client.getNotificationSettings(), notificationSettings = NotificationSettingsProxy(notificationSettings: client.getNotificationSettings())
backgroundTaskService: backgroundTaskService)
secureBackupController = SecureBackupController(encryption: client.encryption()) secureBackupController = SecureBackupController(encryption: client.encryption())
@ -425,8 +421,7 @@ class ClientProxy: ClientProxyProtocol {
if let roomListItem, let room { if let roomListItem, let room {
return await RoomProxy(roomListItem: roomListItem, return await RoomProxy(roomListItem: roomListItem,
room: room, room: room)
backgroundTaskService: backgroundTaskService)
} }
// Else wait for the visible rooms list to go into fully loaded // Else wait for the visible rooms list to go into fully loaded
@ -453,8 +448,7 @@ class ClientProxy: ClientProxyProtocol {
} }
return await RoomProxy(roomListItem: roomListItem, return await RoomProxy(roomListItem: roomListItem,
room: room, room: room)
backgroundTaskService: backgroundTaskService)
} }
func roomPreviewForIdentifier(_ identifier: String) async -> Result<RoomPreviewDetails, ClientProxyError> { func roomPreviewForIdentifier(_ identifier: String) async -> Result<RoomPreviewDetails, ClientProxyError> {

View File

@ -20,14 +20,11 @@ import UIKit
struct MediaProvider: MediaProviderProtocol { struct MediaProvider: MediaProviderProtocol {
private let mediaLoader: MediaLoaderProtocol private let mediaLoader: MediaLoaderProtocol
private let imageCache: Kingfisher.ImageCache private let imageCache: Kingfisher.ImageCache
private let backgroundTaskService: BackgroundTaskServiceProtocol?
init(mediaLoader: MediaLoaderProtocol, init(mediaLoader: MediaLoaderProtocol,
imageCache: Kingfisher.ImageCache, imageCache: Kingfisher.ImageCache) {
backgroundTaskService: BackgroundTaskServiceProtocol?) {
self.mediaLoader = mediaLoader self.mediaLoader = mediaLoader
self.imageCache = imageCache self.imageCache = imageCache
self.backgroundTaskService = backgroundTaskService
} }
// MARK: Images // MARK: Images
@ -45,11 +42,6 @@ struct MediaProvider: MediaProviderProtocol {
return .success(image) return .success(image)
} }
let loadImageBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadImage: \(source.url.hashValue)")
defer {
loadImageBgTask?.stop()
}
let cacheKey = cacheKeyForURL(source.url, size: size) let cacheKey = cacheKeyForURL(source.url, size: size)
if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey), if case let .success(cacheResult) = await imageCache.retrieveImage(forKey: cacheKey),
@ -92,9 +84,6 @@ struct MediaProvider: MediaProviderProtocol {
// MARK: Files // MARK: Files
func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result<MediaFileHandleProxy, MediaProviderError> { func loadFileFromSource(_ source: MediaSourceProxy, body: String?) async -> Result<MediaFileHandleProxy, MediaProviderError> {
let loadFileBgTask = await backgroundTaskService?.startBackgroundTask(withName: "LoadFile: \(source.url.hashValue)")
defer { loadFileBgTask?.stop() }
do { do {
let file = try await mediaLoader.loadMediaFileForSource(source, body: body) let file = try await mediaLoader.loadMediaFileForSource(source, body: body)
return .success(file) return .success(file)

View File

@ -36,13 +36,11 @@ private final class WeakNotificationSettingsProxy: NotificationSettingsDelegate
final class NotificationSettingsProxy: NotificationSettingsProxyProtocol { final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
private(set) var notificationSettings: MatrixRustSDK.NotificationSettingsProtocol private(set) var notificationSettings: MatrixRustSDK.NotificationSettingsProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol?
let callbacks = PassthroughSubject<NotificationSettingsProxyCallback, Never>() let callbacks = PassthroughSubject<NotificationSettingsProxyCallback, Never>()
init(notificationSettings: MatrixRustSDK.NotificationSettingsProtocol, backgroundTaskService: BackgroundTaskServiceProtocol?) { init(notificationSettings: MatrixRustSDK.NotificationSettingsProtocol) {
self.notificationSettings = notificationSettings self.notificationSettings = notificationSettings
self.backgroundTaskService = backgroundTaskService
notificationSettings.setDelegate(delegate: WeakNotificationSettingsProxy(proxy: self)) notificationSettings.setDelegate(delegate: WeakNotificationSettingsProxy(proxy: self))
} }
@ -52,9 +50,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func setNotificationMode(roomId: String, mode: RoomNotificationModeProxy) async throws { 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) try await notificationSettings.setRoomNotificationMode(roomId: roomId, mode: mode.roomNotificationMode)
await updatedSettings() await updatedSettings()
} }
@ -70,9 +65,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func setDefaultRoomNotificationMode(isEncrypted: Bool, isOneToOne: Bool, mode: RoomNotificationModeProxy) async throws { func setDefaultRoomNotificationMode(isEncrypted: Bool, isOneToOne: Bool, mode: RoomNotificationModeProxy) async throws {
let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setDefaultRoomNotificationMode")
defer { backgroundTask?.stop() }
do { do {
try await notificationSettings.setDefaultRoomNotificationMode(isEncrypted: isEncrypted, isOneToOne: isOneToOne, mode: mode.roomNotificationMode) try await notificationSettings.setDefaultRoomNotificationMode(isEncrypted: isEncrypted, isOneToOne: isOneToOne, mode: mode.roomNotificationMode)
} catch NotificationSettingsError.RuleNotFound(let ruleId) { } catch NotificationSettingsError.RuleNotFound(let ruleId) {
@ -86,17 +78,11 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func restoreDefaultNotificationMode(roomId: String) async throws { func restoreDefaultNotificationMode(roomId: String) async throws {
let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "restoreDefaultNotificationMode")
defer { backgroundTask?.stop() }
try await notificationSettings.restoreDefaultRoomNotificationMode(roomId: roomId) try await notificationSettings.restoreDefaultRoomNotificationMode(roomId: roomId)
await updatedSettings() await updatedSettings()
} }
func unmuteRoom(roomId: String, isEncrypted: Bool, isOneToOne: Bool) async throws { 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) try await notificationSettings.unmuteRoom(roomId: roomId, isEncrypted: isEncrypted, isOneToOne: isOneToOne)
await updatedSettings() await updatedSettings()
} }
@ -106,9 +92,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func setRoomMentionEnabled(enabled: Bool) async throws { func setRoomMentionEnabled(enabled: Bool) async throws {
let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setRoomMentionEnabled")
defer { backgroundTask?.stop() }
try await notificationSettings.setRoomMentionEnabled(enabled: enabled) try await notificationSettings.setRoomMentionEnabled(enabled: enabled)
await updatedSettings() await updatedSettings()
} }
@ -118,9 +101,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func setCallEnabled(enabled: Bool) async throws { func setCallEnabled(enabled: Bool) async throws {
let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setCallEnabled")
defer { backgroundTask?.stop() }
try await notificationSettings.setCallEnabled(enabled: enabled) try await notificationSettings.setCallEnabled(enabled: enabled)
await updatedSettings() await updatedSettings()
} }
@ -130,9 +110,6 @@ final class NotificationSettingsProxy: NotificationSettingsProxyProtocol {
} }
func setInviteForMeEnabled(enabled: Bool) async throws { func setInviteForMeEnabled(enabled: Bool) async throws {
let backgroundTask = await backgroundTaskService?.startBackgroundTask(withName: "setInviteForMeEnabled")
defer { backgroundTask?.stop() }
try await notificationSettings.setInviteForMeEnabled(enabled: enabled) try await notificationSettings.setInviteForMeEnabled(enabled: enabled)
await updatedSettings() await updatedSettings()
} }

View File

@ -26,13 +26,9 @@ class RoomProxy: RoomProxyProtocol {
private let roomListItem: RoomListItemProtocol private let roomListItem: RoomListItemProtocol
private let room: RoomProtocol private let room: RoomProtocol
let timeline: TimelineProxyProtocol 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 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 // periphery:ignore - required for instance retention in the rust codebase
private var roomInfoObservationToken: TaskHandle? private var roomInfoObservationToken: TaskHandle?
// periphery:ignore - required for instance retention in the rust codebase // periphery:ignore - required for instance retention in the rust codebase
@ -60,13 +56,12 @@ class RoomProxy: RoomProxyProtocol {
} }
init?(roomListItem: RoomListItemProtocol, init?(roomListItem: RoomListItemProtocol,
room: RoomProtocol, room: RoomProtocol) async {
backgroundTaskService: BackgroundTaskServiceProtocol) async {
self.roomListItem = roomListItem self.roomListItem = roomListItem
self.room = room self.room = room
self.backgroundTaskService = backgroundTaskService
do { do {
timeline = try await TimelineProxy(timeline: room.timeline(), backgroundTaskService: backgroundTaskService) timeline = try await TimelineProxy(timeline: room.timeline())
} catch { } catch {
MXLog.error("Failed creating timeline with error: \(error)") MXLog.error("Failed creating timeline with error: \(error)")
return nil return nil
@ -165,12 +160,7 @@ class RoomProxy: RoomProxyProtocol {
} }
func redact(_ eventID: String) async -> Result<Void, RoomProxyError> { func redact(_ eventID: String) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) await Task.dispatch(on: userInitiatedDispatchQueue) {
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.redact(eventId: eventID, reason: nil) try self.room.redact(eventId: eventID, reason: nil)
return .success(()) return .success(())
@ -180,14 +170,9 @@ class RoomProxy: RoomProxyProtocol {
} }
} }
} }
func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError> { func reportContent(_ eventID: String, reason: String?) async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) await Task.dispatch(on: userInitiatedDispatchQueue) {
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.room.reportContent(eventId: eventID, score: nil, reason: reason) try self.room.reportContent(eventId: eventID, score: nil, reason: reason)
return .success(()) return .success(())
@ -226,11 +211,6 @@ class RoomProxy: RoomProxyProtocol {
return .success(member) return .success(member)
} }
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
do { do {
let member = try await room.member(userId: userID) let member = try await room.member(userId: userID)
return .success(RoomMemberProxy(member: member)) return .success(RoomMemberProxy(member: member))
@ -239,14 +219,9 @@ class RoomProxy: RoomProxyProtocol {
return .failure(.sdkError(error)) return .failure(.sdkError(error))
} }
} }
func leaveRoom() async -> Result<Void, RoomProxyError> { func leaveRoom() async -> Result<Void, RoomProxyError> {
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true) await Task.dispatch(on: .global()) {
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: .global()) {
do { do {
try self.room.leave() try self.room.leave()
return .success(()) return .success(())

View File

@ -20,10 +20,7 @@ import MatrixRustSDK
final class TimelineProxy: TimelineProxyProtocol { final class TimelineProxy: TimelineProxyProtocol {
private let timeline: Timeline 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 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 messageSendingDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.message_sending", qos: .userInitiated)
private let userInitiatedDispatchQueue = DispatchQueue(label: "io.element.elementx.roomproxy.user_initiated", 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() roomTimelineObservationToken?.cancel()
} }
init(timeline: Timeline, backgroundTaskService: BackgroundTaskServiceProtocol) { init(timeline: Timeline) {
self.timeline = timeline self.timeline = timeline
self.backgroundTaskService = backgroundTaskService
} }
func subscribeForUpdates() async { func subscribeForUpdates() async {
@ -84,11 +80,6 @@ final class TimelineProxy: TimelineProxyProtocol {
func cancelSend(transactionID: String) async { func cancelSend(transactionID: String) async {
MXLog.info("Cancelling sending for transaction ID: \(transactionID)") 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) { return await Task.dispatch(on: messageSendingDispatchQueue) {
self.timeline.cancelSend(txnId: transactionID) self.timeline.cancelSend(txnId: transactionID)
MXLog.info("Finished cancelling sending for transaction ID: \(transactionID)") MXLog.info("Finished cancelling sending for transaction ID: \(transactionID)")
@ -101,11 +92,6 @@ final class TimelineProxy: TimelineProxyProtocol {
intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> { intentionalMentions: IntentionalMentions) async -> Result<Void, TimelineProxyError> {
MXLog.info("Editing message with original event ID: \(eventID)") MXLog.info("Editing message with original event ID: \(eventID)")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
let messageContent = buildMessageContentFor(message, let messageContent = buildMessageContentFor(message,
html: html, html: html,
intentionalMentions: intentionalMentions.toRustMentions()) intentionalMentions: intentionalMentions.toRustMentions())
@ -198,11 +184,6 @@ final class TimelineProxy: TimelineProxyProtocol {
func retrySend(transactionID: String) async { func retrySend(transactionID: String) async {
MXLog.info("Retrying sending for transactionID: \(transactionID)") MXLog.info("Retrying sending for transactionID: \(transactionID)")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: messageSendingDispatchQueue) { return await Task.dispatch(on: messageSendingDispatchQueue) {
self.timeline.retrySend(txnId: transactionID) self.timeline.retrySend(txnId: transactionID)
MXLog.info("Finished retrying sending for transactionID: \(transactionID)") MXLog.info("Finished retrying sending for transactionID: \(transactionID)")
@ -215,11 +196,6 @@ final class TimelineProxy: TimelineProxyProtocol {
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending audio") 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 let handle = timeline.sendAudio(url: url.path(percentEncoded: false), audioInfo: audioInfo, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress) progressSubject?.send(progress)
}) })
@ -245,11 +221,6 @@ final class TimelineProxy: TimelineProxyProtocol {
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending file") 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 let handle = timeline.sendFile(url: url.path(percentEncoded: false), fileInfo: fileInfo, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress) progressSubject?.send(progress)
}) })
@ -276,11 +247,6 @@ final class TimelineProxy: TimelineProxyProtocol {
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending image") 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 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) progressSubject?.send(progress)
}) })
@ -307,11 +273,6 @@ final class TimelineProxy: TimelineProxyProtocol {
assetType: AssetType?) async -> Result<Void, TimelineProxyError> { assetType: AssetType?) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending location") MXLog.info("Sending location")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: messageSendingDispatchQueue) { return await Task.dispatch(on: messageSendingDispatchQueue) {
self.timeline.sendLocation(body: body, self.timeline.sendLocation(body: body,
geoUri: geoURI.string, geoUri: geoURI.string,
@ -334,11 +295,6 @@ final class TimelineProxy: TimelineProxyProtocol {
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending video") 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 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) progressSubject?.send(progress)
}) })
@ -365,11 +321,6 @@ final class TimelineProxy: TimelineProxyProtocol {
requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> { requestHandle: @MainActor (SendAttachmentJoinHandleProtocol) -> Void) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending voice message") 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 let handle = timeline.sendVoiceMessage(url: url.path(percentEncoded: false), audioInfo: audioInfo, waveform: waveform, caption: nil, formattedCaption: nil, progressWatcher: UploadProgressListener { progress in
progressSubject?.send(progress) progressSubject?.send(progress)
}) })
@ -399,11 +350,6 @@ final class TimelineProxy: TimelineProxyProtocol {
MXLog.info("Sending message") MXLog.info("Sending message")
} }
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
let messageContent = buildMessageContentFor(message, let messageContent = buildMessageContentFor(message,
html: html, html: html,
intentionalMentions: intentionalMentions.toRustMentions()) intentionalMentions: intentionalMentions.toRustMentions())
@ -436,12 +382,7 @@ final class TimelineProxy: TimelineProxyProtocol {
func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError> { func sendMessageEventContent(_ messageContent: RoomMessageEventContentWithoutRelation) async -> Result<Void, TimelineProxyError> {
MXLog.info("Sending message content") MXLog.info("Sending message content")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: messageSendingDispatchQueue) { return await Task.dispatch(on: messageSendingDispatchQueue) {
self.timeline.send(msg: messageContent) self.timeline.send(msg: messageContent)
@ -456,11 +397,6 @@ final class TimelineProxy: TimelineProxyProtocol {
func sendReadReceipt(for eventID: String, type: ReceiptType) async -> Result<Void, TimelineProxyError> { func sendReadReceipt(for eventID: String, type: ReceiptType) async -> Result<Void, TimelineProxyError> {
MXLog.verbose("Sending read receipt for eventID: \(eventID)") 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) { return await Task.dispatch(on: lowPriorityDispatchQueue) {
do { do {
try self.timeline.sendReadReceipt(receiptType: type, eventId: eventID) 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<Void, TimelineProxyError> { func toggleReaction(_ reaction: String, to eventID: String) async -> Result<Void, TimelineProxyError> {
MXLog.info("Toggling reaction for eventID: \(eventID)") MXLog.info("Toggling reaction for eventID: \(eventID)")
sendMessageBackgroundTask = await backgroundTaskService.startBackgroundTask(withName: backgroundTaskName, isReusable: true)
defer {
sendMessageBackgroundTask?.stop()
}
return await Task.dispatch(on: userInitiatedDispatchQueue) { return await Task.dispatch(on: userInitiatedDispatchQueue) {
do { do {
try self.timeline.toggleReaction(eventId: eventID, key: reaction) try self.timeline.toggleReaction(eventId: eventID, key: reaction)

View File

@ -20,7 +20,6 @@ import MatrixRustSDK
class UserSessionStore: UserSessionStoreProtocol { class UserSessionStore: UserSessionStoreProtocol {
private let keychainController: KeychainControllerProtocol private let keychainController: KeychainControllerProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol
private let matrixSDKStateKey = "matrix-sdk-state" private let matrixSDKStateKey = "matrix-sdk-state"
/// Whether or not there are sessions in the store. /// Whether or not there are sessions in the store.
@ -33,9 +32,8 @@ class UserSessionStore: UserSessionStoreProtocol {
var clientSessionDelegate: ClientSessionDelegate { keychainController } var clientSessionDelegate: ClientSessionDelegate { keychainController }
init(keychainController: KeychainControllerProtocol, backgroundTaskService: BackgroundTaskServiceProtocol) { init(keychainController: KeychainControllerProtocol) {
self.keychainController = keychainController self.keychainController = keychainController
self.backgroundTaskService = backgroundTaskService
baseDirectory = .sessionsBaseDirectory baseDirectory = .sessionsBaseDirectory
MXLog.info("Setup base directory at: \(baseDirectory)") MXLog.info("Setup base directory at: \(baseDirectory)")
} }
@ -100,11 +98,9 @@ class UserSessionStore: UserSessionStoreProtocol {
private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) -> UserSessionProtocol { private func buildUserSessionWithClient(_ clientProxy: ClientProxyProtocol) -> UserSessionProtocol {
let mediaProvider = MediaProvider(mediaLoader: clientProxy, let mediaProvider = MediaProvider(mediaLoader: clientProxy,
imageCache: .onlyInMemory, imageCache: .onlyInMemory)
backgroundTaskService: backgroundTaskService)
let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, let voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider)
backgroundTaskService: backgroundTaskService)
return UserSession(clientProxy: clientProxy, return UserSession(clientProxy: clientProxy,
mediaProvider: mediaProvider, mediaProvider: mediaProvider,
@ -150,7 +146,6 @@ class UserSessionStore: UserSessionStoreProtocol {
private func setupProxyForClient(_ client: Client) async -> ClientProxyProtocol { private func setupProxyForClient(_ client: Client) async -> ClientProxyProtocol {
await ClientProxy(client: client, await ClientProxy(client: client,
backgroundTaskService: backgroundTaskService,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
networkMonitor: ServiceLocator.shared.networkMonitor) networkMonitor: ServiceLocator.shared.networkMonitor)
} }

View File

@ -30,7 +30,6 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
private let voiceMessageCache: VoiceMessageCacheProtocol private let voiceMessageCache: VoiceMessageCacheProtocol
private let audioConverter: AudioConverterProtocol private let audioConverter: AudioConverterProtocol
private let backgroundTaskService: BackgroundTaskServiceProtocol?
private let processingQueue: DispatchQueue private let processingQueue: DispatchQueue
private var conversionRequests = [MediaSourceProxy: VoiceMessageConversionRequest]() private var conversionRequests = [MediaSourceProxy: VoiceMessageConversionRequest]()
@ -39,13 +38,11 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
init(mediaProvider: MediaProviderProtocol, init(mediaProvider: MediaProviderProtocol,
voiceMessageCache: VoiceMessageCacheProtocol = VoiceMessageCache(), voiceMessageCache: VoiceMessageCacheProtocol = VoiceMessageCache(),
audioConverter: AudioConverterProtocol = AudioConverter(), audioConverter: AudioConverterProtocol = AudioConverter(),
processingQueue: DispatchQueue = .global(), processingQueue: DispatchQueue = .global()) {
backgroundTaskService: BackgroundTaskServiceProtocol?) {
self.mediaProvider = mediaProvider self.mediaProvider = mediaProvider
self.voiceMessageCache = voiceMessageCache self.voiceMessageCache = voiceMessageCache
self.audioConverter = audioConverter self.audioConverter = audioConverter
self.processingQueue = processingQueue self.processingQueue = processingQueue
self.backgroundTaskService = backgroundTaskService
} }
deinit { deinit {
@ -53,9 +50,6 @@ class VoiceMessageMediaManager: VoiceMessageMediaManagerProtocol {
} }
func loadVoiceMessageFromSource(_ source: MediaSourceProxy, body: String?) async throws -> URL { 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 { guard let mimeType = source.mimeType, mimeType.starts(with: supportedVoiceMessageMimeType) else {
throw VoiceMessageMediaManagerError.unsupportedMimeTye throw VoiceMessageMediaManagerError.unsupportedMimeTye
} }

View File

@ -19,7 +19,7 @@ import MatrixRustSDK
import SwiftUI import SwiftUI
import UIKit import UIKit
class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate { class UITestsAppCoordinator: AppCoordinatorProtocol, SecureWindowManagerDelegate {
private let navigationRootCoordinator: NavigationRootCoordinator private let navigationRootCoordinator: NavigationRootCoordinator
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
@ -28,7 +28,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate {
// periphery:ignore - retaining purpose // periphery:ignore - retaining purpose
private var alternateWindowMockScreen: MockScreen? private var alternateWindowMockScreen: MockScreen?
let windowManager: WindowManagerProtocol let windowManager: SecureWindowManagerProtocol
init(appDelegate: AppDelegate) { init(appDelegate: AppDelegate) {
windowManager = WindowManager(appDelegate: appDelegate) windowManager = WindowManager(appDelegate: appDelegate)
@ -70,7 +70,7 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate {
fatalError("Not implemented.") fatalError("Not implemented.")
} }
func windowManagerDidConfigureWindows(_ windowManager: WindowManagerProtocol) { func windowManagerDidConfigureWindows(_ windowManager: SecureWindowManagerProtocol) {
ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow ServiceLocator.shared.userIndicatorController.window = windowManager.overlayWindow
// Set up the alternate window for the App Lock flow coordinator tests. // Set up the alternate window for the App Lock flow coordinator tests.
@ -92,14 +92,14 @@ class UITestsAppCoordinator: AppCoordinatorProtocol, WindowManagerDelegate {
@MainActor @MainActor
class MockScreen: Identifiable { class MockScreen: Identifiable {
let id: UITestsScreenIdentifier let id: UITestsScreenIdentifier
let windowManager: WindowManagerProtocol let windowManager: SecureWindowManagerProtocol
let navigationRootCoordinator: NavigationRootCoordinator let navigationRootCoordinator: NavigationRootCoordinator
private var retainedState = [Any]() private var retainedState = [Any]()
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
init(id: UITestsScreenIdentifier, init(id: UITestsScreenIdentifier,
windowManager: WindowManagerProtocol, windowManager: SecureWindowManagerProtocol,
navigationRootCoordinator: NavigationRootCoordinator) { navigationRootCoordinator: NavigationRootCoordinator) {
self.id = id self.id = id
self.windowManager = windowManager self.windowManager = windowManager
@ -129,8 +129,7 @@ class MockScreen: Identifiable {
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController)
orientationManager: windowManager)
flowCoordinator.start() flowCoordinator.start()
retainedState.append(flowCoordinator) retainedState.append(flowCoordinator)
return nil return nil
@ -468,7 +467,6 @@ class MockScreen: Identifiable {
let flowCoordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()), let flowCoordinator = UserSessionFlowCoordinator(userSession: MockUserSession(clientProxy: clientProxy, mediaProvider: MockMediaProvider(), voiceMessageMediaManager: VoiceMessageMediaManagerMock()),
navigationRootCoordinator: navigationRootCoordinator, navigationRootCoordinator: navigationRootCoordinator,
windowManager: windowManager,
appLockService: AppLockService(keychainController: KeychainControllerMock(), appLockService: AppLockService(keychainController: KeychainControllerMock(),
appSettings: ServiceLocator.shared.settings), appSettings: ServiceLocator.shared.settings),
bugReportService: BugReportServiceMock(), bugReportService: BugReportServiceMock(),

View File

@ -17,7 +17,7 @@
import SwiftUI import SwiftUI
class UnitTestsAppCoordinator: AppCoordinatorProtocol { class UnitTestsAppCoordinator: AppCoordinatorProtocol {
let windowManager: WindowManagerProtocol let windowManager: SecureWindowManagerProtocol
init(appDelegate: AppDelegate) { init(appDelegate: AppDelegate) {
windowManager = WindowManager(appDelegate: appDelegate) windowManager = WindowManager(appDelegate: appDelegate)

View File

@ -22,8 +22,7 @@ final class NSEUserSession {
private let notificationClient: NotificationClient private let notificationClient: NotificationClient
private let userID: String private let userID: String
private(set) lazy var mediaProvider: MediaProviderProtocol = MediaProvider(mediaLoader: MediaLoader(client: baseClient), private(set) lazy var mediaProvider: MediaProviderProtocol = MediaProvider(mediaLoader: MediaLoader(client: baseClient),
imageCache: .onlyOnDisk, imageCache: .onlyOnDisk)
backgroundTaskService: nil)
private let delegateHandle: TaskHandle? private let delegateHandle: TaskHandle?
init(credentials: KeychainCredentials, clientSessionDelegate: ClientSessionDelegate) async throws { init(credentials: KeychainCredentials, clientSessionDelegate: ClientSessionDelegate) async throws {

View File

@ -97,8 +97,6 @@ targets:
- path: ../../ElementX/Sources/Other/TestablePreview.swift - path: ../../ElementX/Sources/Other/TestablePreview.swift
- path: ../../ElementX/Sources/Other/Pills/PlainMentionBuilder.swift - path: ../../ElementX/Sources/Other/Pills/PlainMentionBuilder.swift
- path: ../../ElementX/Sources/Other/Pills/PillConstants.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/KeychainController.swift
- path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift - path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift
- path: ../../ElementX/Sources/Services/Media/Provider - path: ../../ElementX/Sources/Services/Media/Provider

View File

@ -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")
}
}

View File

@ -22,15 +22,13 @@ import XCTest
final class MediaProviderTests: XCTestCase { final class MediaProviderTests: XCTestCase {
private let mediaLoader = MockMediaLoader() private let mediaLoader = MockMediaLoader()
private var imageCache: MockImageCache! private var imageCache: MockImageCache!
private var backgroundTaskService = MockBackgroundTaskService()
var mediaProvider: MediaProvider! var mediaProvider: MediaProvider!
override func setUp() { override func setUp() {
imageCache = MockImageCache(name: "Test") imageCache = MockImageCache(name: "Test")
mediaProvider = MediaProvider(mediaLoader: mediaLoader, mediaProvider = MediaProvider(mediaLoader: mediaLoader,
imageCache: imageCache, imageCache: imageCache)
backgroundTaskService: backgroundTaskService)
} }
func test_whenImageFromSourceWithSourceNil_nilReturned() throws { func test_whenImageFromSourceWithSourceNil_nilReturned() throws {

View File

@ -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
}
}

View File

@ -295,8 +295,7 @@ class RoomFlowCoordinatorTests: XCTestCase {
appMediator: AppMediatorMock.default, appMediator: AppMediatorMock.default,
appSettings: ServiceLocator.shared.settings, appSettings: ServiceLocator.shared.settings,
analytics: ServiceLocator.shared.analytics, analytics: ServiceLocator.shared.analytics,
userIndicatorController: ServiceLocator.shared.userIndicatorController, userIndicatorController: ServiceLocator.shared.userIndicatorController)
orientationManager: OrientationManagerMock())
} }
} }

View File

@ -46,7 +46,6 @@ class UserSessionFlowCoordinatorTests: XCTestCase {
userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession, userSessionFlowCoordinator = UserSessionFlowCoordinator(userSession: userSession,
navigationRootCoordinator: navigationRootCoordinator, navigationRootCoordinator: navigationRootCoordinator,
windowManager: WindowManagerMock(),
appLockService: AppLockServiceMock(), appLockService: AppLockServiceMock(),
bugReportService: BugReportServiceMock(), bugReportService: BugReportServiceMock(),
roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), roomTimelineControllerFactory: MockRoomTimelineControllerFactory(),

View File

@ -32,8 +32,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
voiceMessageCache = VoiceMessageCacheMock() voiceMessageCache = VoiceMessageCacheMock()
mediaProvider = MockMediaProvider() mediaProvider = MockMediaProvider()
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
voiceMessageCache: voiceMessageCache, voiceMessageCache: voiceMessageCache)
backgroundTaskService: MockBackgroundTaskService())
} }
func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws { func testLoadVoiceMessageFromSourceUnsupportedMedia() async throws {
@ -65,8 +64,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
voiceMessageCache: voiceMessageCache, voiceMessageCache: voiceMessageCache,
audioConverter: AudioConverterMock(), audioConverter: AudioConverterMock())
backgroundTaskService: MockBackgroundTaskService())
do { do {
_ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) _ = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
@ -119,8 +117,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL) voiceMessageCache.cacheMediaSourceUsingMoveReturnValue = .success(cachedConvertedFileURL)
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
voiceMessageCache: voiceMessageCache, voiceMessageCache: voiceMessageCache,
audioConverter: audioConverter, audioConverter: audioConverter)
backgroundTaskService: MockBackgroundTaskService())
let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil) let url = try await voiceMessageMediaManager.loadVoiceMessageFromSource(mediaSource, body: nil)
// The file must have been converted // The file must have been converted
@ -155,8 +152,7 @@ class VoiceMessageMediaManagerTests: XCTestCase {
voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider, voiceMessageMediaManager = VoiceMessageMediaManager(mediaProvider: mediaProvider,
voiceMessageCache: voiceMessageCache, voiceMessageCache: voiceMessageCache,
audioConverter: audioConverter, audioConverter: audioConverter)
backgroundTaskService: MockBackgroundTaskService())
let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType) let mediaSource = MediaSourceProxy(url: someURL, mimeType: audioOGGMimeType)
for _ in 0..<10 { for _ in 0..<10 {