diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index b06e2987e..0ef69cd88 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 07CC13C5729C24255348CBBD /* ElementCallWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 309AD8BAE6437C31BA7157BF /* ElementCallWidgetDriver.swift */; }; 086D01E79C8E8D3F004FAF21 /* AudioPlayerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC9104846487244648D32C6D /* AudioPlayerProtocol.swift */; }; 08CB4BD12CEEDE6AAE4A18DD /* WindowManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 035177BCD8E8308B098AC3C2 /* WindowManager.swift */; }; + 08E02C56E8B277A22C5E5BA5 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */; }; 095C0ACFC234E0550A6404C5 /* AppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC803282F9268D49F4ABF14 /* AppCoordinator.swift */; }; 095D3906CF2F940C2D2D17CC /* RoomFlowCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FCB2126C091EEF2454B4D56 /* RoomFlowCoordinatorTests.swift */; }; 09713669577CDA8D012EE380 /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 6647C55D93508C7CE9D954A5 /* MatrixRustSDK */; }; @@ -122,7 +123,6 @@ 1AB3D8563AB12635250A6A6E /* StaticLocationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15E0017717EAE3A1D02D005 /* StaticLocationScreenCoordinator.swift */; }; 1AE4AEA0FA8DEF52671832E0 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */; }; 1B2DADC008EE211AF1DA5292 /* NotificationManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */; }; - 1B4B3E847BF944DB2C1C217F /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */; }; 1B88BB631F7FC45A213BB554 /* TimelineItemSender.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55AEEF8142DF1B59DB40FB93 /* TimelineItemSender.swift */; }; 1B8E30B35BF8F541C1318F19 /* SecureBackupScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40316EFFEAC7B206EE9A55AE /* SecureBackupScreenViewModelTests.swift */; }; 1BA04D05EBC6646958B1BE60 /* PlaceholderScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */; }; @@ -205,7 +205,6 @@ 2FEC6652055984389CE1BBEC /* TimelineProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */; }; 3042527CB344A9EF1157FC26 /* AudioRecorderStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C55CC239AE12339C565F6C9A /* AudioRecorderStateTests.swift */; }; 308BD9343B95657FAA583FB7 /* SwiftState in Frameworks */ = {isa = PBXBuildFile; productRef = 19CD5B074D7DD44AF4C58BB6 /* SwiftState */; }; - 3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */; }; 30CC1DB7CE357659C82AA115 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; }; 30CC4F796B27BE8B1DFDBF5A /* NSEUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAA2832D93EC7D2608703FB /* NSEUserSession.swift */; }; 30E5628F74AD3C27A061BF25 /* QRCodeLoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */; }; @@ -322,6 +321,7 @@ 4BAB8222DBA0B4207D1223E0 /* NotificationSettingsProxyMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */; }; 4BB282209EA82015D0DF8F89 /* NavigationStackCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C698E30698EC59302A8EEBD /* NavigationStackCoordinatorTests.swift */; }; 4BB51476A29E7E27BC14EA22 /* UserDetailsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 022E6BD64CB4610B9C95FC02 /* UserDetailsEditScreenViewModel.swift */; }; + 4BBF6C8E3EFC944B55231B19 /* AppMediatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */; }; 4C356F5CCB4CDC99BFA45185 /* AppLockSetupPINScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7884BD256C091EB511B2EDF /* AppLockSetupPINScreenViewModelProtocol.swift */; }; 4C5A638DAA8AF64565BA4866 /* EncryptedRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5351EBD7A0B9610548E4B7B2 /* EncryptedRoomTimelineItem.swift */; }; 4C8C0C9FC10BA73AB7780534 /* RoomListFiltersStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE0C9653870803E4F91F474 /* RoomListFiltersStateTests.swift */; }; @@ -456,7 +456,6 @@ 6BB6944443C421C722ED1E7D /* portrait_test_video.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = F2D513D2477B57F90E98EEC0 /* portrait_test_video.mp4 */; }; 6C34237AFB808E38FC8776B9 /* RoomStateEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */; }; 6CD61FAF03E8986523C2ABB8 /* StartChatScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3005886F00029F058DB62BE /* StartChatScreenCoordinator.swift */; }; - 6D046D653DA28ADF1E6E59A4 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */; }; 6D6E651ACACE27E9C5690818 /* TypingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE47A97726F0675DEE387BF9 /* TypingIndicatorView.swift */; }; 6DC8E43BA04AC2AC4EB2EB97 /* AnalyticsPromptScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18486B87745B1811E7FBD3D2 /* AnalyticsPromptScreenModels.swift */; }; 6E47D126DD7585E8F8237CE7 /* LoadableAvatarImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */; }; @@ -472,7 +471,6 @@ 70394ECD2DCC70741538620D /* AccessibilityIdentifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04BB8DDE245ED86C489BA983 /* AccessibilityIdentifiers.swift */; }; 70558528EF68CAAEF09972D5 /* RoomTimelineItemFixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = E96ED747FF90332EA1333C22 /* RoomTimelineItemFixtures.swift */; }; 706289B086B0A6B0C211763F /* UITestsSignalling.swift in Sources */ = {isa = PBXBuildFile; fileRef = B7F0192CE2F891141A25B49F /* UITestsSignalling.swift */; }; - 706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */; }; 707E49BE07E8EB8A13C0EB1E /* SessionVerificationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FACD034DB52525A3CEF2BDF /* SessionVerificationScreen.swift */; }; 70B83D44043293B4B77440B9 /* PollFormScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */; }; 719E7AAD1F8E68F68F30FECD /* Task.swift in Sources */ = {isa = PBXBuildFile; fileRef = A40C19719687984FD9478FBE /* Task.swift */; }; @@ -485,7 +483,6 @@ 73F547BEB41D3DAFAAF6E0AF /* UserProfileScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 71E2E5103702D13361D09100 /* UserProfileScreenViewModelTests.swift */; }; 7405B4824D45BA7C3D943E76 /* Application.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7D0CBC76C80E04345E11F2DB /* Application.swift */; }; 743790BF6A5B0577EA74AF14 /* ReadMarkerRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = DF3D25B3EDB283B5807EADCF /* ReadMarkerRoomTimelineItem.swift */; }; - 74604ACFDBE7F54260E7B617 /* ApplicationProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */; }; 748F482FEF4E04D61C39AAD7 /* EmojiPickerScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = F174A5627CDB3CAF280D1880 /* EmojiPickerScreenModels.swift */; }; 7501442D52A65F73DF79FFD4 /* PaginationIndicatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B987FC3FDBAA0E1C5AA235C /* PaginationIndicatorRoomTimelineItem.swift */; }; 754602A7B2AAD443C4228ED4 /* Sentry in Frameworks */ = {isa = PBXBuildFile; productRef = 7731767AE437BA3BD2CC14A8 /* Sentry */; }; @@ -516,6 +513,7 @@ 7A170A5A4A352954BB2A1B96 /* AuthenticationStartScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24E8C8817F59BEC7E358EB78 /* AuthenticationStartScreen.swift */; }; 7A642EE5F1ADC5D520F21924 /* MediaProviderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */; }; 7A71AEF419904209BB8C2833 /* UserAgentBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F2529D434C750ED78ADF1ED /* UserAgentBuilder.swift */; }; + 7A8B264506D3DDABC01B4EEB /* AppMediator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B53AC78E49A297AC1D72A7CF /* AppMediator.swift */; }; 7B1605C6FFD4D195F264A684 /* RoomPollsHistoryScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B40233F2989AD49906BB310D /* RoomPollsHistoryScreenViewModelTests.swift */; }; 7B5DAB915357BE596529BF25 /* MapTilerStaticMapProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 20872C3887F835958CE2F1D0 /* MapTilerStaticMapProtocol.swift */; }; 7B66DA4E7E5FE4D1A0FCEAA4 /* JoinRoomScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEAB5662310AE73D93815134 /* JoinRoomScreenViewModelProtocol.swift */; }; @@ -670,7 +668,6 @@ 9EBDC79CAC9B63A0D626E333 /* LegalInformationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5EB2CAA266B921D128C35710 /* LegalInformationScreenCoordinator.swift */; }; 9F11E743EA01482E78A438B0 /* GlobalSearchScreenCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22DB19219E6CC4D002E15D48 /* GlobalSearchScreenCell.swift */; }; 9F19096BFA629C0AC282B1E4 /* CreateRoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8CEB4634C0DD7779C4AB504 /* CreateRoomScreenUITests.swift */; }; - 9F30A18B50D13B10D8444984 /* ApplicationMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 62011D547772F3DF5D924823 /* ApplicationMock.swift */; }; 9FAF6DA7E8E85C9699757764 /* CollapsibleRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6E2656184491C505700D2405 /* CollapsibleRoomTimelineView.swift */; }; 9FB41B0E8B2AA9B404E52C8B /* AppLockSetupBiometricsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CCC6C31102E1D8B9106DEDE /* AppLockSetupBiometricsScreenViewModelProtocol.swift */; }; A009BDFB0A6816D4C392ADCB /* SettingsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2AF715D4FD4710EBB637D661 /* SettingsScreenViewModelProtocol.swift */; }; @@ -688,6 +685,7 @@ A2434D4DFB49A68E5CD0F53C /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; }; A2A5AB2E8B3F5CA769E531FA /* TextBasedRoomTimelineViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E47F18A9A077E351CEA10D4 /* TextBasedRoomTimelineViewProtocol.swift */; }; A33784831AD880A670CAA9F9 /* FileManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DF593C3F7AF4B2FBAEB05D /* FileManager.swift */; }; + A36AD251013402EDBD666C75 /* AppMediatorMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BAC027034248429A438886B /* AppMediatorMock.swift */; }; A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287FC98AF2664EAD79C0D902 /* UIDevice.swift */; }; A3A7A05E8F9B7EB0E1A09A2A /* SoftLogoutScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */; }; A3D7110C1E75E7B4A73BE71C /* VoiceMessageRecorderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D93C94C30E3135BC9290DE13 /* VoiceMessageRecorderTests.swift */; }; @@ -753,6 +751,7 @@ B22D857D1E8FCA6DD74A58E3 /* UserSessionScreenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F899D02CF26EA7675EEBE74C /* UserSessionScreenTests.swift */; }; B245583C63F8F90357B87FAE /* KZFileWatchers in Frameworks */ = {isa = PBXBuildFile; productRef = A2AE110B053B55E38F8D10C7 /* KZFileWatchers */; }; B2F8E01ABA1BA30265B4ECBE /* RoundedCornerShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */; }; + B3066502FA56D9199846C5E7 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */; }; B3D652AA1654270742072FB3 /* DeveloperOptionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86A6F283BC574FDB96ABBB07 /* DeveloperOptionsScreenViewModel.swift */; }; B3EDDEC1839BB5A3747624BB /* FormButtonStyles.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95A1CCDEE545CB6453B084BF /* FormButtonStyles.swift */; }; B402708F8728DD0DB7C324E2 /* StartChatScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 78910787F967CBC6042A101E /* StartChatScreenViewModelProtocol.swift */; }; @@ -851,6 +850,7 @@ C9F5B48D15B9BCAE1F8D564E /* RoomNotificationModeProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */; }; CA12AE0DCD57D49CD96C699A /* WaveformCursorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FB9EABCA9348DFA27439A809 /* WaveformCursorView.swift */; }; CA5BFF0C2EF5A8EF40CA2D69 /* VoiceMessageRecordingComposer.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCB6F36CCE44A29A06FCAF1C /* VoiceMessageRecordingComposer.swift */; }; + CB0C53B67DA62F7E30DDD720 /* UIKitBackgroundTaskService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */; }; CB137BFB3E083C33E398A6CB /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 020597E28A4BC8E1BE8EDF6E /* KeychainAccess */; }; CB498F4E27AA0545DCEF0F6F /* DTCoreText in Frameworks */ = {isa = PBXBuildFile; productRef = 36B7FC232711031AA2B0D188 /* DTCoreText */; }; CB6BCBF28E4B76EA08C2926D /* StateRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B16048D30F0438731C41F775 /* StateRoomTimelineItem.swift */; }; @@ -910,9 +910,11 @@ D5FE90A6AF5FD5AE91BD37C7 /* NotificationSettingsEditScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 780258F1B9D15E30549FF4BE /* NotificationSettingsEditScreenViewModel.swift */; }; D63974A88CF2BC721F109C77 /* Compound in Frameworks */ = {isa = PBXBuildFile; productRef = DCA3C4A997AD28E6918D4CE5 /* Compound */; }; D6661A94DBD97658B2ADBD6A /* MapTilerStaticMap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A4D29F2683F5772AC72406F /* MapTilerStaticMap.swift */; }; + D798A150BB6569FC07CBEB25 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */; }; D7CDBAE82782BD0529DECB5F /* AttributedString.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BD6ED18E2EB61E28C340AD /* AttributedString.swift */; }; D8359F67AF3A83516E9083C1 /* MockUserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = A4756C5A8C8649AD6C10C615 /* MockUserSession.swift */; }; D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24B88AD3D1599E8CB1376E0 /* AvatarSize.swift */; }; + D8ED2FCF46D63A7357E9D502 /* BackgroundTaskServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */; }; D9473FC9B077A6EDB7A12001 /* LocationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 772334731A8BF8E6D90B194D /* LocationRoomTimelineView.swift */; }; D98B5EE8C4F5A2CE84687AE8 /* UTType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 897DF5E9A70CE05A632FC8AF /* UTType.swift */; }; D9F80CE61BF8FF627FDB0543 /* LoadableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = C352359663A0E52BA20761EE /* LoadableImage.swift */; }; @@ -936,7 +938,6 @@ DFD5AA8688A34C72D48AF3B1 /* StaticLocationScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = F5311C989EC15B4C2D699025 /* StaticLocationScreenViewModel.swift */; }; DFF7D6A6C26DDD40D00AE579 /* target.yml in Resources */ = {isa = PBXBuildFile; fileRef = F012CB5EE3F2B67359F6CC52 /* target.yml */; }; E07ABB9FD1C87EBBDDE81DC5 /* ResetRecoveryKeyScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C63616A8920EC6948B31EA1B /* ResetRecoveryKeyScreenViewModel.swift */; }; - E0A4DCA633D174EB43AD599F /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; }; E0B6A569AC3E81D233B43D60 /* SettingsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E625B0EB2F86B37C14EF7E6 /* SettingsScreenViewModel.swift */; }; E0FB26262689F04D66A949D7 /* TestablePreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1E227F34BE43B08E098796E /* TestablePreview.swift */; }; E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = AE52983FAFB4E0998C00EE8A /* CancellableTask.swift */; }; @@ -954,6 +955,7 @@ E3AC72E3E58F364EF15C1CC7 /* NotificationSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 514363244AE7D68080D44C6F /* NotificationSettingsScreenViewModelTests.swift */; }; E3CA565A4B9704F191B191F0 /* JoinedRoomSize+MemberCount.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBF9AEA706926DD0DA2B954C /* JoinedRoomSize+MemberCount.swift */; }; E3E1E255DC8CB34BD8573E0D /* UserIndicatorControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = A12D3B1BCF920880CA8BBB6B /* UserIndicatorControllerProtocol.swift */; }; + E3E3D645C7F80DD64A86D936 /* UIKitBackgroundTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */; }; E45C9FA22BC13B477FD3B4AC /* EmojiDetection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D99730313BEBF08CDE81EE3 /* EmojiDetection.swift */; }; E468CC731C3F4D678499E52F /* LAContextMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1BA5A62DA4B543827FF82354 /* LAContextMock.swift */; }; E481C8FDCB6C089963C95344 /* DeviceKit in Frameworks */ = {isa = PBXBuildFile; productRef = BC01130651CB23340B899032 /* DeviceKit */; }; @@ -1006,7 +1008,6 @@ EF0D0155DD104C7A41A2EB0E /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; }; EF47D802A404A53F15D5D4B6 /* JoinRoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */; }; EF5009AC03212227131C8AF2 /* RoomNotificationSettingsProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */; }; - EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */; }; EF890DEF0479E66548F2BA23 /* AppLockTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 490BEADEFB2D6B7C9F618AE8 /* AppLockTimer.swift */; }; F0570F1ECD70C4C851FB2052 /* SecureBackupRecoveryKeyScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 93E7304F5ECB4CB11CB10E60 /* SecureBackupRecoveryKeyScreenViewModelProtocol.swift */; }; F06CE9132855E81EBB6DDC32 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 940C605265DD82DA0C655E23 /* Kingfisher */; }; @@ -1157,6 +1158,7 @@ 054F469E433864CC6FE6EE8E /* ServerSelectionUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionUITests.swift; sourceTree = ""; }; 05512FB13987D221B7205DE0 /* HomeScreenRecoveryKeyConfirmationBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenRecoveryKeyConfirmationBanner.swift; sourceTree = ""; }; 05596E4A11A8C9346E9E54AE /* SoftLogoutScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SoftLogoutScreenCoordinator.swift; sourceTree = ""; }; + 05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorProtocol.swift; sourceTree = ""; }; 05F598B1B346DAF223651C91 /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = ""; }; 0685156EB62D7E243F097CFC /* ServerSelectionScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerSelectionScreenViewModelProtocol.swift; sourceTree = ""; }; 06B098A612DCB5A7358EECD5 /* DeveloperOptionsScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperOptionsScreenModels.swift; sourceTree = ""; }; @@ -1321,7 +1323,6 @@ 2BDB3E65A79779EDA5D33D8A /* AudioPlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioPlayerState.swift; sourceTree = ""; }; 2BFDCA5A09EE70BC17F2EFA7 /* URLComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLComponents.swift; sourceTree = ""; }; 2C0197EAE9D45A662B8847B6 /* RoomTimelineControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerProtocol.swift; sourceTree = ""; }; - 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskProtocol.swift; sourceTree = ""; }; 2CEBCB9676FCD1D0F13188DD /* StringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; 2CF9FE7E0CF9F40D1509E63A /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = bg; path = bg.lproj/Localizable.stringsdict; sourceTree = ""; }; 2D0946F77B696176E062D037 /* RoomMembersListScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenModels.swift; sourceTree = ""; }; @@ -1338,6 +1339,7 @@ 30ED584467DB380E3CEFB1DB /* NotificationManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationManagerTests.swift; sourceTree = ""; }; 314F1C79850BE46E8ABEAFCB /* ReadReceipt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadReceipt.swift; sourceTree = ""; }; 317F41A4B5C4F457AF710666 /* PollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollView.swift; sourceTree = ""; }; + 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = ""; }; 31A6314FDC51DA25712D9A81 /* PillContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PillContextTests.swift; sourceTree = ""; }; 31D6764D6976D235926FE5FC /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = ""; }; 3203C6566DC17B7AECC1B7FD /* RoomNotificationSettingsUserDefinedScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationSettingsUserDefinedScreen.swift; sourceTree = ""; }; @@ -1372,6 +1374,7 @@ 398817652FA8ABAE0A31AC6D /* ReadableFrameModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReadableFrameModifier.swift; sourceTree = ""; }; 39C0D861FC397AC34BCF089E /* KeychainControllerMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerMock.swift; sourceTree = ""; }; 3B5E97E9615A158C76B2AB77 /* DateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTests.swift; sourceTree = ""; }; + 3BAC027034248429A438886B /* AppMediatorMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediatorMock.swift; sourceTree = ""; }; 3BC1B7CB061C9865B2B91B56 /* QRCodeLoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreenViewModel.swift; sourceTree = ""; }; 3BDCCD2F6B405C14B9BCE94E /* JoinRoomScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JoinRoomScreenCoordinator.swift; sourceTree = ""; }; 3C1A3D524D63815B28FA4D62 /* EmojiCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCategory.swift; sourceTree = ""; }; @@ -1388,7 +1391,6 @@ 3D9FCE4D1E3A81AC1CC5CB91 /* AppLockSetupSettingsScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenCoordinator.swift; sourceTree = ""; }; 3DBE70FFB7936F35811772C1 /* IdentityConfirmedScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenModels.swift; sourceTree = ""; }; 3DC1943ADE6A62ED5129D7C8 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = ""; }; - 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTaskService.swift; sourceTree = ""; }; 3DFE4453AB0B34C203447162 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = ""; }; 3E6A9B9DFEE964962C179DE3 /* RoomAttachmentPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAttachmentPicker.swift; sourceTree = ""; }; 3E93A1BE7D8A2EBCAD51EEB4 /* Array.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; @@ -1518,7 +1520,6 @@ 60F18AECC9D38C2B6D85F99C /* Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Publisher.swift; sourceTree = ""; }; 612EF972F2A1800682D32C5E /* StickerRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StickerRoomTimelineView.swift; sourceTree = ""; }; 61B33F23681660E940BA57F4 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/SAS.strings; sourceTree = ""; }; - 62011D547772F3DF5D924823 /* ApplicationMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationMock.swift; sourceTree = ""; }; 622D09D4ECE759189009AEAF /* MapLibreMapView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapLibreMapView.swift; sourceTree = ""; }; 624244C398804ADC885239AA /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; 62A81CCC2516D9CF9322DF01 /* MediaProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderTests.swift; sourceTree = ""; }; @@ -1644,6 +1645,7 @@ 8319173DD66C07F45DC48848 /* IdentityConfirmedScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmedScreenViewModelProtocol.swift; sourceTree = ""; }; 837B440C4705E4B899BCB899 /* RoomDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModel.swift; sourceTree = ""; }; 839E2C35DF3F9C7B54C3CE49 /* RoundedCornerShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedCornerShape.swift; sourceTree = ""; }; + 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskProtocol.swift; sourceTree = ""; }; 84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = ""; }; 845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = ""; }; 848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = ""; }; @@ -1705,7 +1707,6 @@ 91CF6F7D08228D16BA69B63B /* zh-Hant-TW */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant-TW"; path = "zh-Hant-TW.lproj/Localizable.strings"; sourceTree = ""; }; 92390F9FA98255440A6BF5F8 /* OIDCAuthenticationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthenticationPresenter.swift; sourceTree = ""; }; 92DB574F954CC2B40F7BE892 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = ""; }; - 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = ""; }; 9332DFE9642F0A46ECA0497B /* BlurHashEncode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlurHashEncode.swift; sourceTree = ""; }; 933B074F006F8E930DB98B4E /* TimelineMediaFrame.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineMediaFrame.swift; sourceTree = ""; }; 9342F5D6729627B6393AF853 /* ServerConfirmationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenModels.swift; sourceTree = ""; }; @@ -1780,7 +1781,6 @@ A7D452AF7B5F7E3A0A7DB54C /* SessionVerificationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenViewModelProtocol.swift; sourceTree = ""; }; A84D413BF49F0E980F010A6B /* LogViewerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogViewerScreenCoordinator.swift; sourceTree = ""; }; A861DA5932B128FE1DCB5CE2 /* InviteUsersScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenCoordinator.swift; sourceTree = ""; }; - A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationProtocol.swift; sourceTree = ""; }; A9B069D7772DDF6513E0F1B8 /* AuthenticationFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationFlowCoordinator.swift; sourceTree = ""; }; A9FAFE1C2149E6AC8156ED2B /* Collection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Collection.swift; sourceTree = ""; }; AA19C32BD97F45847724E09A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Untranslated.strings; sourceTree = ""; }; @@ -1788,7 +1788,6 @@ AACE9B8E1A4AE79A7E2914F6 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = es; path = es.lproj/Localizable.stringsdict; sourceTree = ""; }; AAD01F7FC2BBAC7351948595 /* UserProfile+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserProfile+Mock.swift"; sourceTree = ""; }; AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationAnnotation.swift; sourceTree = ""; }; - AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = ""; }; ABA4CF2F5B4F68D02E412004 /* ServerConfirmationScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerConfirmationScreenViewModelProtocol.swift; sourceTree = ""; }; AC0275CEE9CA078B34028BDF /* AppLockScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModelTests.swift; sourceTree = ""; }; AC1DA29A5A041CC0BACA7CB0 /* MockImageCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockImageCache.swift; sourceTree = ""; }; @@ -1837,6 +1836,7 @@ B48B7AD4908C5C374517B892 /* MapAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = MapAssets.xcassets; sourceTree = ""; }; B4CFE236419E830E8946639C /* Analytics+SwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Analytics+SwiftUI.swift"; sourceTree = ""; }; B50F03079F6B5EF9CA005F14 /* TimelineProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineProxyProtocol.swift; sourceTree = ""; }; + B53AC78E49A297AC1D72A7CF /* AppMediator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMediator.swift; sourceTree = ""; }; B590BD4507D4F0A377FDE01A /* LoadableAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadableAvatarImage.swift; sourceTree = ""; }; B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoticeRoomTimelineView.swift; sourceTree = ""; }; B61C339A2FDDBD067FF6635C /* ConfettiScene.scn */ = {isa = PBXFileReference; path = ConfettiScene.scn; sourceTree = ""; }; @@ -1965,6 +1965,7 @@ D196116D2DD3F2757D45FCB7 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/SAS.strings; sourceTree = ""; }; D1BC84BA0AF11C2128D58ABD /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = ""; }; D1D8479BB704B7EF696F8ABE /* RoomPollsHistoryScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPollsHistoryScreenCoordinator.swift; sourceTree = ""; }; + D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackgroundTaskServiceProtocol.swift; sourceTree = ""; }; D263254AFE5B7993FFBBF324 /* NSE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = NSE.entitlements; sourceTree = ""; }; D26813CCE39221FE30BF22CD /* PlatformViewVersionPredicate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlatformViewVersionPredicate.swift; sourceTree = ""; }; D28F7A6CEEA4A2815B0F0F55 /* SettingsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsFlowCoordinator.swift; sourceTree = ""; }; @@ -2066,6 +2067,7 @@ EA880E78AF4BD24E45A7808C /* bg */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bg; path = bg.lproj/InfoPlist.strings; sourceTree = ""; }; EAF710CB1C31F8938EAA3A7D /* RoomChangeRolesScreenSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomChangeRolesScreenSection.swift; sourceTree = ""; }; EB3B237387B8288A5A938F1B /* UserAgentBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAgentBuilderTests.swift; sourceTree = ""; }; + EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBackgroundTask.swift; sourceTree = ""; }; EB63761D9F9CE8B23CBD6179 /* PollFormScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollFormScreenModels.swift; sourceTree = ""; }; EB76A9AFC6CCAD4998D9B045 /* IdentityConfirmationScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentityConfirmationScreenViewModel.swift; sourceTree = ""; }; EBEB8D9F4940E161B18FE4BC /* UITestsNotificationCenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITestsNotificationCenter.swift; sourceTree = ""; }; @@ -2325,7 +2327,7 @@ 7803E03F759061C948D66B7E /* AppLock */, FCE7249621F507F34A8122FB /* Audio */, AAFDD509929A0CCF8BCE51EB /* Authentication */, - EBBEB5471737E9D116DF4738 /* Background */, + C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */, 0ED3F5C21537519389C07644 /* BugReport */, 8039515BAA53B7C3275AC64A /* Client */, 8C3BAE06B336D97DABBE2509 /* CreateRoom */, @@ -2682,7 +2684,7 @@ isa = PBXGroup; children = ( 69CB8242D69B7E4D0B32E18D /* AggregatedReactionMock.swift */, - 62011D547772F3DF5D924823 /* ApplicationMock.swift */, + 3BAC027034248429A438886B /* AppMediatorMock.swift */, E2F96CCBEAAA7F2185BFA354 /* ClientProxyMock.swift */, 382B50F7E379B3DBBD174364 /* NotificationSettingsProxyMock.swift */, D38391154120264910D19528 /* PollMock.swift */, @@ -4333,6 +4335,8 @@ 077D7C3BE199B6E5DDEC07EC /* AppCoordinatorStateMachine.swift */, C75EF87651B00A176AB08E97 /* AppDelegate.swift */, CA89A2DD51B6BBE1DA55E263 /* Application.swift */, + B53AC78E49A297AC1D72A7CF /* AppMediator.swift */, + 05AF58372CA884A789EB9C5A /* AppMediatorProtocol.swift */, AC3F82523D6F48B926D6AF68 /* AppSettings.swift */, B251F5B4511D1CA0BA8361FE /* CoordinatorProtocol.swift */, 7B9FCA1CFD07B8CF9BD21266 /* FlowCoordinatorProtocol.swift */, @@ -4668,6 +4672,17 @@ path = RoomDirectorySearchScreen; sourceTree = ""; }; + C488FC0F4ACF26D0A2C5246E /* BackgroundTasks */ = { + isa = PBXGroup; + children = ( + 83D7FA960FE1A1A63622CC5E /* BackgroundTaskProtocol.swift */, + D23C089E93ECE0EF83E182ED /* BackgroundTaskServiceProtocol.swift */, + EB49AE5D902C0FE40AF4ADF5 /* UIKitBackgroundTask.swift */, + 3199367BC31381F67C870976 /* UIKitBackgroundTaskService.swift */, + ); + path = BackgroundTasks; + sourceTree = ""; + }; C844840F3DD48A154C65AE0C /* View */ = { isa = PBXGroup; children = ( @@ -5016,18 +5031,6 @@ path = AccountSettings; sourceTree = ""; }; - EBBEB5471737E9D116DF4738 /* Background */ = { - isa = PBXGroup; - children = ( - A8903A9F615BBD0E6D7CD133 /* ApplicationProtocol.swift */, - 2CA028DCD4157F9A1F999827 /* BackgroundTaskProtocol.swift */, - AAE73D571D4F9C36DD45255A /* BackgroundTaskServiceProtocol.swift */, - 92FCD9116ADDE820E4E30F92 /* UIKitBackgroundTask.swift */, - 3DF1FFC3336EB23374BBBFCC /* UIKitBackgroundTaskService.swift */, - ); - path = Background; - sourceTree = ""; - }; EC4545C7E37E8294D3FE6800 /* StartChatScreen */ = { isa = PBXGroup; children = ( @@ -5658,8 +5661,8 @@ CDCA8A559E098503DDE29477 /* AttributedStringBuilder.swift in Sources */, BA43D782BE85C7F5F20C624A /* AttributedStringBuilderProtocol.swift in Sources */, 968A5B890004526AB58A217C /* AvatarSize.swift in Sources */, - EF7924005216B8189898F370 /* BackgroundTaskProtocol.swift in Sources */, - 1B4B3E847BF944DB2C1C217F /* BackgroundTaskServiceProtocol.swift in Sources */, + 08E02C56E8B277A22C5E5BA5 /* BackgroundTaskProtocol.swift in Sources */, + D8ED2FCF46D63A7357E9D502 /* BackgroundTaskServiceProtocol.swift in Sources */, 9A3B0CDF097E3838FB1B9595 /* Bundle.swift in Sources */, B5618E3C948584E5C1F67033 /* DTHTMLElement+AttributedStringBuilder.swift in Sources */, DFCA89C4EC2A5332ED6B441F /* DataProtectionManager.swift in Sources */, @@ -5904,11 +5907,12 @@ 4807E8F51DB54F56B25E1C7E /* AppLockSetupSettingsScreenViewModel.swift in Sources */, C85C7A201E4CFDA477ACEBEB /* AppLockSetupSettingsScreenViewModelProtocol.swift in Sources */, EF890DEF0479E66548F2BA23 /* AppLockTimer.swift in Sources */, + 7A8B264506D3DDABC01B4EEB /* AppMediator.swift in Sources */, + A36AD251013402EDBD666C75 /* AppMediatorMock.swift in Sources */, + 4BBF6C8E3EFC944B55231B19 /* AppMediatorProtocol.swift in Sources */, 355B11D08CE0CEF97A813236 /* AppRoutes.swift in Sources */, 12CCA59536EDD99A3272CF77 /* AppSettings.swift in Sources */, 9462C62798F47E39DCC182D2 /* Application.swift in Sources */, - 9F30A18B50D13B10D8444984 /* ApplicationMock.swift in Sources */, - 74604ACFDBE7F54260E7B617 /* ApplicationProtocol.swift in Sources */, 61A36B9BB2ADE36CEFF5E98C /* Array.swift in Sources */, 90EB25D13AE6EEF034BDE9D2 /* Assets.swift in Sources */, 273AB64B9A26B61C51858867 /* AsyncSequence.swift in Sources */, @@ -5940,8 +5944,8 @@ 6146996D5C4DDD5DA816FC87 /* AuthenticationTextFieldStyle.swift in Sources */, 4AAA8606FBA290E23D15422E /* AvatarHeaderView.swift in Sources */, D876EC0FED3B6D46C806912A /* AvatarSize.swift in Sources */, - E0A4DCA633D174EB43AD599F /* BackgroundTaskProtocol.swift in Sources */, - 6D046D653DA28ADF1E6E59A4 /* BackgroundTaskServiceProtocol.swift in Sources */, + B3066502FA56D9199846C5E7 /* BackgroundTaskProtocol.swift in Sources */, + D798A150BB6569FC07CBEB25 /* BackgroundTaskServiceProtocol.swift in Sources */, A4B0BAD62A12ED76BD611B79 /* BadgeView.swift in Sources */, 38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */, EB9F4688006B52E69DF5358F /* BlankFormCoordinator.swift in Sources */, @@ -6537,8 +6541,8 @@ 36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */, A37EED79941AD3B7140B3822 /* UIDevice.swift in Sources */, 0EE5EBA18BA1FE10254BB489 /* UIFont+AttributedStringBuilder.m in Sources */, - 706F79A39BDB32F592B8C2C7 /* UIKitBackgroundTask.swift in Sources */, - 3097A0A867D2B19CE32DAE58 /* UIKitBackgroundTaskService.swift in Sources */, + E3E3D645C7F80DD64A86D936 /* UIKitBackgroundTask.swift in Sources */, + CB0C53B67DA62F7E30DDD720 /* UIKitBackgroundTaskService.swift in Sources */, E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */, 384D6B9A7DFD7260139D6852 /* UITestsNotificationCenter.swift in Sources */, 22882C710BC99EC34A5024A0 /* UITestsScreenIdentifier.swift in Sources */, diff --git a/ElementX/Sources/Application/AppCoordinator.swift b/ElementX/Sources/Application/AppCoordinator.swift index b00747338..31fdc121c 100644 --- a/ElementX/Sources/Application/AppCoordinator.swift +++ b/ElementX/Sources/Application/AppCoordinator.swift @@ -24,6 +24,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private let stateMachine: AppCoordinatorStateMachine private let navigationRootCoordinator: NavigationRootCoordinator private let userSessionStore: UserSessionStoreProtocol + private let appMediator: AppMediator private let appSettings: AppSettings private let appDelegate: AppDelegate @@ -65,6 +66,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg init(appDelegate: AppDelegate) { windowManager = WindowManager(appDelegate: appDelegate) + appMediator = AppMediator(windowManager: windowManager) + Self.setupEnvironmentVariables() let appSettings = AppSettings() @@ -100,9 +103,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator()) - backgroundTaskService = UIKitBackgroundTaskService { - UIApplication.shared - } + backgroundTaskService = UIKitBackgroundTaskService(appMediator: appMediator) let keychainController = KeychainController(service: .sessions, accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier) @@ -409,6 +410,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg authenticationFlowCoordinator = AuthenticationFlowCoordinator(authenticationService: authenticationService, bugReportService: ServiceLocator.shared.bugReportService, navigationRootCoordinator: navigationRootCoordinator, + appMediator: appMediator, appSettings: appSettings, analytics: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, @@ -471,6 +473,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg appLockService: appLockFlowCoordinator.appLockService, bugReportService: ServiceLocator.shared.bugReportService, roomTimelineControllerFactory: RoomTimelineControllerFactory(), + appMediator: appMediator, appSettings: appSettings, analytics: ServiceLocator.shared.analytics, notificationManager: notificationManager, @@ -645,7 +648,7 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg private func handleAppRoute(_ appRoute: AppRoute) { if let userSessionFlowCoordinator { - userSessionFlowCoordinator.handleAppRoute(appRoute, animated: UIApplication.shared.applicationState == .active) + userSessionFlowCoordinator.handleAppRoute(appRoute, animated: appMediator.appState == .active) } else { storedAppRoute = appRoute } diff --git a/ElementX/Sources/Application/AppMediator.swift b/ElementX/Sources/Application/AppMediator.swift new file mode 100644 index 000000000..bb60ae3ab --- /dev/null +++ b/ElementX/Sources/Application/AppMediator.swift @@ -0,0 +1,68 @@ +// +// Copyright 2024 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 UIKit + +class AppMediator: AppMediatorProtocol { + private let windowManager: WindowManagerProtocol + + // UIApplication.State won't update if we store this e.g. in the constructor + private var application: UIApplication { + UIApplication.shared + } + + init(windowManager: WindowManagerProtocol) { + self.windowManager = windowManager + } + + func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier { + application.beginBackgroundTask(withName: taskName, expirationHandler: handler) + } + + func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) { + application.endBackgroundTask(identifier) + } + + func open(_ url: URL) { + application.open(url, options: [:], completionHandler: nil) + } + + func openAppSettings() { + guard let url = URL(string: UIApplication.openSettingsURLString) else { + return + } + + open(url) + } + + var backgroundTimeRemaining: TimeInterval { + application.backgroundTimeRemaining + } + + @MainActor + var appState: UIApplication.State { + switch application.applicationState { + case .active: + windowManager.mainWindow.traitCollection.activeAppearance == .active ? .active : .inactive + case .inactive: + .inactive + case .background: + .background + default: + .inactive + } + } +} diff --git a/ElementX/Sources/Services/Background/ApplicationProtocol.swift b/ElementX/Sources/Application/AppMediatorProtocol.swift similarity index 56% rename from ElementX/Sources/Services/Background/ApplicationProtocol.swift rename to ElementX/Sources/Application/AppMediatorProtocol.swift index 2e9034717..e9387cad3 100644 --- a/ElementX/Sources/Services/Background/ApplicationProtocol.swift +++ b/ElementX/Sources/Application/AppMediatorProtocol.swift @@ -18,7 +18,7 @@ import Foundation import UIKit // sourcery: AutoMockable -protocol ApplicationProtocol { +protocol AppMediatorProtocol { func beginBackgroundTask(withName taskName: String?, expirationHandler handler: (() -> Void)?) -> UIBackgroundTaskIdentifier func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) @@ -29,18 +29,35 @@ protocol ApplicationProtocol { var backgroundTimeRemaining: TimeInterval { get } - var applicationState: UIApplication.State { get } + var appState: UIApplication.State { get } } -extension UIApplication: ApplicationProtocol { - func open(_ url: URL) { - open(url, options: [:], completionHandler: nil) - } - - func openAppSettings() { - guard let url = URL(string: UIApplication.openSettingsURLString) else { - return +extension UIApplication.State: CustomStringConvertible { + public var description: String { + switch self { + case .active: + return "active" + case .inactive: + return "inactive" + case .background: + return "background" + @unknown default: + return "unknown" + } + } +} + +extension UIUserInterfaceActiveAppearance: CustomStringConvertible { + public var description: String { + switch self { + case .active: + return "active" + case .inactive: + return "inactive" + case .unspecified: + return "unspecified" + @unknown default: + return "unknown" } - open(url) } } diff --git a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift index ab3ab5a15..568832489 100644 --- a/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/AuthenticationFlowCoordinator.swift @@ -27,6 +27,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { private let bugReportService: BugReportServiceProtocol private let navigationRootCoordinator: NavigationRootCoordinator private let navigationStackCoordinator: NavigationStackCoordinator + private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analytics: AnalyticsService private let userIndicatorController: UserIndicatorControllerProtocol @@ -44,6 +45,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { init(authenticationService: AuthenticationServiceProxyProtocol, bugReportService: BugReportServiceProtocol, navigationRootCoordinator: NavigationRootCoordinator, + appMediator: AppMediatorProtocol, appSettings: AppSettings, analytics: AnalyticsService, userIndicatorController: UserIndicatorControllerProtocol, @@ -51,6 +53,7 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { self.authenticationService = authenticationService self.bugReportService = bugReportService self.navigationRootCoordinator = navigationRootCoordinator + self.appMediator = appMediator self.appSettings = appSettings self.analytics = analytics self.userIndicatorController = userIndicatorController @@ -107,7 +110,8 @@ class AuthenticationFlowCoordinator: FlowCoordinatorProtocol { private func startQRCodeLogin() { let coordinator = QRCodeLoginScreenCoordinator(parameters: .init(qrCodeLoginService: QRCodeLoginService(), - orientationManager: orientationManager)) + orientationManager: orientationManager, + appMediator: appMediator)) coordinator.actionsPublisher.sink { [weak self] action in guard let self else { return diff --git a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift index e32490d30..b04cce75f 100644 --- a/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift @@ -46,6 +46,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private let roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol private let navigationStackCoordinator: NavigationStackCoordinator private let emojiProvider: EmojiProviderProtocol + private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analytics: AnalyticsService private let userIndicatorController: UserIndicatorControllerProtocol @@ -73,6 +74,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, navigationStackCoordinator: NavigationStackCoordinator, emojiProvider: EmojiProviderProtocol, + appMediator: AppMediatorProtocol, appSettings: AppSettings, analytics: AnalyticsService, userIndicatorController: UserIndicatorControllerProtocol, @@ -83,6 +85,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { self.roomTimelineControllerFactory = roomTimelineControllerFactory self.navigationStackCoordinator = navigationStackCoordinator self.emojiProvider = emojiProvider + self.appMediator = appMediator self.appSettings = appSettings self.analytics = analytics self.userIndicatorController = userIndicatorController @@ -439,7 +442,9 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { voiceMessageMediaManager: userSession.voiceMessageMediaManager, emojiProvider: emojiProvider, completionSuggestionService: completionSuggestionService, + appMediator: appMediator, appSettings: appSettings) + let coordinator = RoomScreenCoordinator(parameters: parameters) coordinator.actions .sink { [weak self] action in @@ -714,7 +719,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { private func presentMapNavigator(interactionMode: StaticLocationInteractionMode) { let stackCoordinator = NavigationStackCoordinator() - let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode) + let params = StaticLocationScreenCoordinatorParameters(interactionMode: interactionMode, appMediator: appMediator) let coordinator = StaticLocationScreenCoordinator(parameters: params) coordinator.actions.sink { [weak self] action in @@ -1126,6 +1131,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol { roomTimelineControllerFactory: roomTimelineControllerFactory, navigationStackCoordinator: navigationStackCoordinator, emojiProvider: emojiProvider, + appMediator: appMediator, appSettings: appSettings, analytics: analytics, userIndicatorController: userIndicatorController, diff --git a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift index 33608e421..5acdc34d2 100644 --- a/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift +++ b/ElementX/Sources/FlowCoordinators/UserSessionFlowCoordinator.swift @@ -30,6 +30,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { private let navigationSplitCoordinator: NavigationSplitCoordinator private let windowManager: WindowManagerProtocol private let bugReportService: BugReportServiceProtocol + private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analytics: AnalyticsService private let notificationManager: NotificationManagerProtocol @@ -71,6 +72,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { appLockService: AppLockServiceProtocol, bugReportService: BugReportServiceProtocol, roomTimelineControllerFactory: RoomTimelineControllerFactoryProtocol, + appMediator: AppMediatorProtocol, appSettings: AppSettings, analytics: AnalyticsService, notificationManager: NotificationManagerProtocol, @@ -81,6 +83,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { self.windowManager = windowManager self.bugReportService = bugReportService self.roomTimelineControllerFactory = roomTimelineControllerFactory + self.appMediator = appMediator self.appSettings = appSettings self.analytics = analytics self.notificationManager = notificationManager @@ -439,6 +442,7 @@ class UserSessionFlowCoordinator: FlowCoordinatorProtocol { roomTimelineControllerFactory: roomTimelineControllerFactory, navigationStackCoordinator: detailNavigationStackCoordinator, emojiProvider: EmojiProvider(), + appMediator: appMediator, appSettings: appSettings, analytics: analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/ElementX/Sources/Mocks/ApplicationMock.swift b/ElementX/Sources/Mocks/AppMediatorMock.swift similarity index 81% rename from ElementX/Sources/Mocks/ApplicationMock.swift rename to ElementX/Sources/Mocks/AppMediatorMock.swift index 8e90a742a..fea1aa74b 100644 --- a/ElementX/Sources/Mocks/ApplicationMock.swift +++ b/ElementX/Sources/Mocks/AppMediatorMock.swift @@ -16,21 +16,21 @@ import UIKit -extension ApplicationMock { - static var `default`: ApplicationProtocol { - ApplicationMock(withState: .active, +extension AppMediatorMock { + static var `default`: AppMediatorProtocol { + AppMediatorMock(withState: .active, backgroundTimeRemaining: 10, allowTasks: true) } - - static var mockBroken: ApplicationProtocol { - ApplicationMock(withState: .inactive, + + static var mockBroken: AppMediatorProtocol { + AppMediatorMock(withState: .inactive, backgroundTimeRemaining: 0, allowTasks: false) } - - static var mockAboutToSuspend: ApplicationProtocol { - ApplicationMock(withState: .background, + + static var mockAboutToSuspend: AppMediatorProtocol { + AppMediatorMock(withState: .background, backgroundTimeRemaining: 2, allowTasks: false) } @@ -42,7 +42,7 @@ extension ApplicationMock { allowTasks: Bool) { self.init() - underlyingApplicationState = applicationState + underlyingAppState = applicationState underlyingBackgroundTimeRemaining = backgroundTimeRemaining beginBackgroundTaskWithNameExpirationHandlerClosure = { _, handler in diff --git a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift index dc6a5358c..5107df23a 100644 --- a/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift +++ b/ElementX/Sources/Mocks/Generated/GeneratedMocks.swift @@ -747,17 +747,17 @@ class AppLockServiceMock: AppLockServiceProtocol { } } } -class ApplicationMock: ApplicationProtocol { +class AppMediatorMock: AppMediatorProtocol { var backgroundTimeRemaining: TimeInterval { get { return underlyingBackgroundTimeRemaining } set(value) { underlyingBackgroundTimeRemaining = value } } var underlyingBackgroundTimeRemaining: TimeInterval! - var applicationState: UIApplication.State { - get { return underlyingApplicationState } - set(value) { underlyingApplicationState = value } + var appState: UIApplication.State { + get { return underlyingAppState } + set(value) { underlyingAppState = value } } - var underlyingApplicationState: UIApplication.State! + var underlyingAppState: UIApplication.State! //MARK: - beginBackgroundTask diff --git a/ElementX/Sources/Screens/LocationSharing/StaticLocationScreenCoordinator.swift b/ElementX/Sources/Screens/LocationSharing/StaticLocationScreenCoordinator.swift index e2887b651..f4d1be1c9 100644 --- a/ElementX/Sources/Screens/LocationSharing/StaticLocationScreenCoordinator.swift +++ b/ElementX/Sources/Screens/LocationSharing/StaticLocationScreenCoordinator.swift @@ -19,6 +19,7 @@ import SwiftUI struct StaticLocationScreenCoordinatorParameters { let interactionMode: StaticLocationInteractionMode + let appMediator: AppMediatorProtocol } enum StaticLocationScreenCoordinatorAction { @@ -27,7 +28,8 @@ enum StaticLocationScreenCoordinatorAction { } final class StaticLocationScreenCoordinator: CoordinatorProtocol { - let viewModel: StaticLocationScreenViewModelProtocol + private let parameters: StaticLocationScreenCoordinatorParameters + private let viewModel: StaticLocationScreenViewModelProtocol private let actionsSubject: PassthroughSubject = .init() private var cancellables = Set() @@ -37,6 +39,8 @@ final class StaticLocationScreenCoordinator: CoordinatorProtocol { } init(parameters: StaticLocationScreenCoordinatorParameters) { + self.parameters = parameters + viewModel = StaticLocationScreenViewModel(interactionMode: parameters.interactionMode) } @@ -49,7 +53,7 @@ final class StaticLocationScreenCoordinator: CoordinatorProtocol { case .close: actionsSubject.send(.close) case .openSystemSettings: - UIApplication.shared.openAppSettings() + parameters.appMediator.openAppSettings() case .sendLocation(let geoURI, let isUserLocation): actionsSubject.send(.selectedLocation(geoURI, isUserLocation: isUserLocation)) } diff --git a/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenCoordinator.swift b/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenCoordinator.swift index 0d98e85ad..c38d869bb 100644 --- a/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenCoordinator.swift +++ b/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenCoordinator.swift @@ -22,6 +22,7 @@ import SwiftUI struct QRCodeLoginScreenCoordinatorParameters { let qrCodeLoginService: QRCodeLoginServiceProtocol let orientationManager: OrientationManagerProtocol + let appMediator: AppMediatorProtocol } enum QRCodeLoginScreenCoordinatorAction { @@ -41,7 +42,7 @@ final class QRCodeLoginScreenCoordinator: CoordinatorProtocol { init(parameters: QRCodeLoginScreenCoordinatorParameters) { viewModel = QRCodeLoginScreenViewModel(qrCodeLoginService: parameters.qrCodeLoginService, - application: UIApplication.shared) + appMediator: parameters.appMediator) orientationManager = parameters.orientationManager } diff --git a/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenViewModel.swift b/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenViewModel.swift index ca751ce53..f5caf5299 100644 --- a/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenViewModel.swift +++ b/ElementX/Sources/Screens/QRCodeLoginScreen/QRCodeLoginScreenViewModel.swift @@ -22,7 +22,7 @@ typealias QRCodeLoginScreenViewModelType = StateStoreViewModel = .init() var actionsPublisher: AnyPublisher { @@ -30,9 +30,9 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr } init(qrCodeLoginService: QRCodeLoginServiceProtocol, - application: ApplicationProtocol) { + appMediator: AppMediatorProtocol) { self.qrCodeLoginService = qrCodeLoginService - self.application = application + self.appMediator = appMediator super.init(initialViewState: QRCodeLoginScreenViewState()) } @@ -45,7 +45,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr case .startScan: Task { await startScanIfPossible() } case .openSettings: - application.openAppSettings() + appMediator.openAppSettings() } } @@ -56,7 +56,7 @@ class QRCodeLoginScreenViewModel: QRCodeLoginScreenViewModelType, QRCodeLoginScr /// Only for mocking initial states fileprivate init(state: QRCodeLoginState) { qrCodeLoginService = QRCodeLoginServiceMock(configuration: .init()) - application = ApplicationMock() + appMediator = AppMediatorMock.default super.init(initialViewState: .init(state: state)) } } diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift index 9eb380b9b..b910a1f0d 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenCoordinator.swift @@ -27,6 +27,7 @@ struct RoomScreenCoordinatorParameters { let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol let emojiProvider: EmojiProviderProtocol let completionSuggestionService: CompletionSuggestionServiceProtocol + let appMediator: AppMediatorProtocol let appSettings: AppSettings } @@ -63,7 +64,7 @@ final class RoomScreenCoordinator: CoordinatorProtocol { mediaPlayerProvider: parameters.mediaPlayerProvider, voiceMessageMediaManager: parameters.voiceMessageMediaManager, userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: UIApplication.shared, + appMediator: parameters.appMediator, appSettings: parameters.appSettings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenter.default) diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift index a991975a6..c5e746538 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenInteractionHandler.swift @@ -40,7 +40,7 @@ class RoomScreenInteractionHandler { private let voiceMessageRecorder: VoiceMessageRecorderProtocol private let voiceMessageMediaManager: VoiceMessageMediaManagerProtocol private let userIndicatorController: UserIndicatorControllerProtocol - private let application: ApplicationProtocol + private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analyticsService: AnalyticsService private let pollInteractionHandler: PollInteractionHandlerProtocol @@ -62,7 +62,7 @@ class RoomScreenInteractionHandler { voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, voiceMessageRecorder: VoiceMessageRecorderProtocol, userIndicatorController: UserIndicatorControllerProtocol, - application: ApplicationProtocol, + appMediator: AppMediatorProtocol, appSettings: AppSettings, analyticsService: AnalyticsService) { self.roomProxy = roomProxy @@ -72,7 +72,7 @@ class RoomScreenInteractionHandler { self.voiceMessageMediaManager = voiceMessageMediaManager self.voiceMessageRecorder = voiceMessageRecorder self.userIndicatorController = userIndicatorController - self.application = application + self.appMediator = appMediator self.appSettings = appSettings self.analyticsService = analyticsService pollInteractionHandler = PollInteractionHandler(analyticsService: analyticsService, roomProxy: roomProxy) @@ -623,7 +623,7 @@ class RoomScreenInteractionHandler { } private func openSystemSettings() { - application.openAppSettings() + appMediator.openAppSettings() } private func displayMediaActionIfPossible(timelineItem: RoomTimelineItemProtocol) async -> RoomTimelineControllerAction { diff --git a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift index a58231762..f18bd72f7 100644 --- a/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift +++ b/ElementX/Sources/Screens/RoomScreen/RoomScreenViewModel.swift @@ -32,7 +32,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol private let timelineController: RoomTimelineControllerProtocol private let mediaPlayerProvider: MediaPlayerProviderProtocol private let userIndicatorController: UserIndicatorControllerProtocol - private let application: ApplicationProtocol + private let appMediator: AppMediatorProtocol private let appSettings: AppSettings private let analyticsService: AnalyticsService private let notificationCenter: NotificationCenterProtocol @@ -54,7 +54,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol mediaPlayerProvider: MediaPlayerProviderProtocol, voiceMessageMediaManager: VoiceMessageMediaManagerProtocol, userIndicatorController: UserIndicatorControllerProtocol, - application: ApplicationProtocol, + appMediator: AppMediatorProtocol, appSettings: AppSettings, analyticsService: AnalyticsService, notificationCenter: NotificationCenterProtocol) { @@ -64,7 +64,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol self.appSettings = appSettings self.analyticsService = analyticsService self.userIndicatorController = userIndicatorController - self.application = application + self.appMediator = appMediator self.notificationCenter = notificationCenter let voiceMessageRecorder = VoiceMessageRecorder(audioRecorder: AudioRecorder(), mediaPlayerProvider: mediaPlayerProvider) @@ -76,7 +76,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol voiceMessageMediaManager: voiceMessageMediaManager, voiceMessageRecorder: voiceMessageRecorder, userIndicatorController: userIndicatorController, - application: application, + appMediator: appMediator, appSettings: appSettings, analyticsService: analyticsService) @@ -417,7 +417,7 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol } private func sendReadReceiptIfNeeded(for lastVisibleItemID: TimelineItemIdentifier) async { - guard application.applicationState == .active else { return } + guard appMediator.appState == .active else { return } await timelineController.sendReadReceipt(for: lastVisibleItemID) } @@ -659,7 +659,7 @@ extension RoomScreenViewModel { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift b/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift index 35976784e..8d0b0b790 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/ReadReceipts/ReadReceiptsSummaryView.swift @@ -58,7 +58,7 @@ struct ReadReceiptsSummaryView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: UserIndicatorControllerMock(), - application: ApplicationMock(), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift index 8fea74949..d75210e52 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/RoomScreen.swift @@ -192,7 +192,7 @@ struct RoomScreen_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift index c668c7542..7bcb54b06 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Supplementary/TimelineReadReceiptsView.swift @@ -96,7 +96,7 @@ struct TimelineReadReceiptsView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/ElementX/Sources/Screens/RoomScreen/View/Timeline/UITimelineView.swift b/ElementX/Sources/Screens/RoomScreen/View/Timeline/UITimelineView.swift index a7e336069..b6be7d932 100644 --- a/ElementX/Sources/Screens/RoomScreen/View/Timeline/UITimelineView.swift +++ b/ElementX/Sources/Screens/RoomScreen/View/Timeline/UITimelineView.swift @@ -83,7 +83,7 @@ struct UITimelineView_Previews: PreviewProvider, TestablePreview { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/ElementX/Sources/Services/Background/BackgroundTaskProtocol.swift b/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift similarity index 100% rename from ElementX/Sources/Services/Background/BackgroundTaskProtocol.swift rename to ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift diff --git a/ElementX/Sources/Services/Background/BackgroundTaskServiceProtocol.swift b/ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift similarity index 100% rename from ElementX/Sources/Services/Background/BackgroundTaskServiceProtocol.swift rename to ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift diff --git a/ElementX/Sources/Services/Background/UIKitBackgroundTask.swift b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift similarity index 76% rename from ElementX/Sources/Services/Background/UIKitBackgroundTask.swift rename to ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift index 4328f8cde..6f8e73b34 100644 --- a/ElementX/Sources/Services/Background/UIKitBackgroundTask.swift +++ b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTask.swift @@ -17,7 +17,6 @@ import Foundation import UIKit -/// UIKitBackgroundTask is a concrete implementation of BackgroundTaskProtocol using UIApplication background task. class UIKitBackgroundTask: BackgroundTaskProtocol { let name: String var isRunning: Bool { @@ -30,28 +29,21 @@ class UIKitBackgroundTask: BackgroundTaskProtocol { Date().timeIntervalSince(startDate) * 1000 } - private let application: ApplicationProtocol + private let appMediator: AppMediatorProtocol private var identifier: UIBackgroundTaskIdentifier = .invalid private var useCounter = 0 private let startDate = Date() - /// Initializes and starts a new background task - /// - Parameters: - /// - name: name - /// - isReusable: flag indicating the task is reusable - /// - application: application instance - /// - expirationHandler: expiration handler init?(name: String, isReusable: Bool, - application: ApplicationProtocol, + appMediator: AppMediatorProtocol, expirationHandler: BackgroundTaskExpirationHandler?) { self.name = name self.isReusable = isReusable - self.application = application + self.appMediator = appMediator self.expirationHandler = expirationHandler - // attempt to start - identifier = application.beginBackgroundTask(withName: name) { [weak self] in + identifier = appMediator.beginBackgroundTask(withName: name) { [weak self] in guard let self else { return } self.stop() self.expirationHandler?(self) @@ -59,13 +51,11 @@ class UIKitBackgroundTask: BackgroundTaskProtocol { if identifier == .invalid { MXLog.error("Do not start background task: \(name), as OS declined") - // call expiration handler immediately expirationHandler?(self) return nil } if isReusable { - // creation itself is a use reuse() } @@ -94,7 +84,7 @@ class UIKitBackgroundTask: BackgroundTaskProtocol { if identifier != .invalid { MXLog.verbose("End background task #\(identifier.rawValue) - \(name) after \(readableElapsedTime)") - application.endBackgroundTask(identifier) + appMediator.endBackgroundTask(identifier) identifier = .invalid } } diff --git a/ElementX/Sources/Services/Background/UIKitBackgroundTaskService.swift b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift similarity index 64% rename from ElementX/Sources/Services/Background/UIKitBackgroundTaskService.swift rename to ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift index 0035cd060..42b3e9e75 100644 --- a/ElementX/Sources/Services/Background/UIKitBackgroundTaskService.swift +++ b/ElementX/Sources/Services/BackgroundTasks/UIKitBackgroundTaskService.swift @@ -19,28 +19,17 @@ import UIKit /// /// UIKitBackgroundTaskService is a concrete implementation of BackgroundTaskServiceProtocol using a given `ApplicationProtocol` instance. class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { - private let applicationBlock: () -> ApplicationProtocol? + private let appMediator: AppMediatorProtocol private var reusableTasks = NSMapTable(keyOptions: .strongMemory, valueOptions: .weakMemory) - - private var application: ApplicationProtocol? { - applicationBlock() - } - - /// Initializer - /// - Parameter applicationBlock: block returning the application instance to use. Defaults to a block returning `UIApplication.extensionSafeShared`. - init(withApplicationBlock applicationBlock: @escaping () -> ApplicationProtocol? = { UIApplication.extensionSafeShared }) { - self.applicationBlock = applicationBlock + + init(appMediator: AppMediatorProtocol) { + self.appMediator = appMediator } func startBackgroundTask(withName name: String, isReusable: Bool, expirationHandler: (() -> Void)?) -> BackgroundTaskProtocol? { - guard let application else { - MXLog.error("Do not start background task: \(name). Application is nil") - return nil - } - - if avoidStartingNewTasks(for: application) { + if shouldAvoidStartingNewTasks() { MXLog.error("Do not start background task: \(name), as not enough time exists") // call expiration handler immediately expirationHandler?() @@ -56,7 +45,7 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { } else { if let newTask = UIKitBackgroundTask(name: name, isReusable: isReusable, - application: application, + appMediator: appMediator, expirationHandler: { [weak self] task in guard let self else { return } self.reusableTasks.removeObject(forKey: task.name as NSString) @@ -70,7 +59,7 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { } else { if let newTask = UIKitBackgroundTask(name: name, isReusable: isReusable, - application: application, + appMediator: appMediator, expirationHandler: { _ in expirationHandler?() }) { @@ -79,8 +68,8 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { } } - let appState = application.applicationState - let remainingTime = readableBackgroundTimeRemaining(application.backgroundTimeRemaining) + 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)") @@ -95,9 +84,9 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { } } - private func avoidStartingNewTasks(for application: ApplicationProtocol) -> Bool { - if application.applicationState == .background, - application.backgroundTimeRemaining < .backgroundTimeRemainingThresholdToStartTasks { + private func shouldAvoidStartingNewTasks() -> Bool { + if appMediator.appState == .background, + appMediator.backgroundTimeRemaining < .backgroundTimeRemainingThresholdToStartTasks { return true } return false @@ -107,27 +96,3 @@ class UIKitBackgroundTaskService: BackgroundTaskServiceProtocol { private extension TimeInterval { static let backgroundTimeRemainingThresholdToStartTasks: TimeInterval = 5 } - -private extension UIApplication { - /// Application instance extension-safe. Will be `nil` on app extensions. - static var extensionSafeShared: UIApplication? { - let selector = NSSelectorFromString("sharedApplication") - guard responds(to: selector) else { return nil } - return perform(selector).takeUnretainedValue() as? UIApplication - } -} - -extension UIApplication.State: CustomStringConvertible { - public var description: String { - switch self { - case .active: - return "active" - case .inactive: - return "inactive" - case .background: - return "background" - @unknown default: - return "unknown" - } - } -} diff --git a/ElementX/Sources/UITests/UITestsAppCoordinator.swift b/ElementX/Sources/UITests/UITestsAppCoordinator.swift index 6c6d14e17..d5b2f8374 100644 --- a/ElementX/Sources/UITests/UITestsAppCoordinator.swift +++ b/ElementX/Sources/UITests/UITestsAppCoordinator.swift @@ -126,6 +126,7 @@ class MockScreen: Identifiable { let flowCoordinator = AuthenticationFlowCoordinator(authenticationService: MockAuthenticationServiceProxy(), bugReportService: BugReportServiceMock(), navigationRootCoordinator: navigationRootCoordinator, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, @@ -243,6 +244,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -258,6 +260,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -273,6 +276,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -288,6 +292,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) navigationStackCoordinator.setRootCoordinator(coordinator) @@ -306,6 +311,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -324,6 +330,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -342,6 +349,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -361,6 +369,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -379,6 +388,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -397,6 +407,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -415,6 +426,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -433,6 +445,7 @@ class MockScreen: Identifiable { voiceMessageMediaManager: VoiceMessageMediaManagerMock(), emojiProvider: EmojiProvider(), completionSuggestionService: CompletionSuggestionServiceMock(configuration: .init()), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings) let coordinator = RoomScreenCoordinator(parameters: parameters) @@ -460,6 +473,7 @@ class MockScreen: Identifiable { appSettings: ServiceLocator.shared.settings), bugReportService: BugReportServiceMock(), roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), + appMediator: AppMediatorMock.default, appSettings: appSettings, analytics: ServiceLocator.shared.analytics, notificationManager: NotificationManagerMock(), diff --git a/NSE/SupportingFiles/target.yml b/NSE/SupportingFiles/target.yml index 30fcccbcf..4ecca46cf 100644 --- a/NSE/SupportingFiles/target.yml +++ b/NSE/SupportingFiles/target.yml @@ -97,8 +97,8 @@ targets: - path: ../../ElementX/Sources/Other/TestablePreview.swift - path: ../../ElementX/Sources/Other/Pills/PlainMentionBuilder.swift - path: ../../ElementX/Sources/Other/Pills/PillConstants.swift - - path: ../../ElementX/Sources/Services/Background/BackgroundTaskProtocol.swift - - path: ../../ElementX/Sources/Services/Background/BackgroundTaskServiceProtocol.swift + - path: ../../ElementX/Sources/Services/BackgroundTasks/BackgroundTaskProtocol.swift + - path: ../../ElementX/Sources/Services/BackgroundTasks/BackgroundTaskServiceProtocol.swift - path: ../../ElementX/Sources/Services/Keychain/KeychainController.swift - path: ../../ElementX/Sources/Services/Keychain/KeychainControllerProtocol.swift - path: ../../ElementX/Sources/Services/Media/Provider diff --git a/UnitTests/Sources/BackgroundTaskTests.swift b/UnitTests/Sources/BackgroundTaskTests.swift index e657a1afb..3d4892ac6 100644 --- a/UnitTests/Sources/BackgroundTaskTests.swift +++ b/UnitTests/Sources/BackgroundTaskTests.swift @@ -24,19 +24,9 @@ class BackgroundTaskTests: XCTestCase { static let bgTaskName = "test" } - func testInAnExtension() { - let service = UIKitBackgroundTaskService { - nil - } - let task = service.startBackgroundTask(withName: Constants.bgTaskName) - - XCTAssertNil(task, "Task should not be created") - } - func testInitAndStop() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) + guard let task = service.startBackgroundTask(withName: Constants.bgTaskName) else { XCTFail("Failed to setup test conditions") return @@ -52,9 +42,7 @@ class BackgroundTaskTests: XCTestCase { } func testNotReusableInit() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) // create two not reusable task with the same name guard let task1 = service.startBackgroundTask(withName: Constants.bgTaskName), @@ -69,9 +57,7 @@ class BackgroundTaskTests: XCTestCase { } func testReusableInit() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) // create two reusable task with the same name guard let task1 = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true), @@ -90,9 +76,7 @@ class BackgroundTaskTests: XCTestCase { } func testMultipleStops() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.default) // create two reusable task with the same name guard let task = service.startBackgroundTask(withName: Constants.bgTaskName, isReusable: true), @@ -113,9 +97,7 @@ class BackgroundTaskTests: XCTestCase { } func testNotValidReuse() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + 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 { @@ -135,9 +117,7 @@ class BackgroundTaskTests: XCTestCase { } func testValidReuse() { - let service = UIKitBackgroundTaskService { - ApplicationMock.default - } + 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 { @@ -161,9 +141,7 @@ class BackgroundTaskTests: XCTestCase { } func testBrokenApp() { - let service = UIKitBackgroundTaskService { - ApplicationMock.mockBroken - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.mockBroken) // create two reusable task with the same name let task = service.startBackgroundTask(withName: Constants.bgTaskName) @@ -172,9 +150,7 @@ class BackgroundTaskTests: XCTestCase { } func testNoTimeApp() { - let service = UIKitBackgroundTaskService { - ApplicationMock.mockAboutToSuspend - } + let service = UIKitBackgroundTaskService(appMediator: AppMediatorMock.mockAboutToSuspend) // create two reusable task with the same name let task = service.startBackgroundTask(withName: Constants.bgTaskName) diff --git a/UnitTests/Sources/PillContextTests.swift b/UnitTests/Sources/PillContextTests.swift index a75463a80..a5705bc08 100644 --- a/UnitTests/Sources/PillContextTests.swift +++ b/UnitTests/Sources/PillContextTests.swift @@ -32,7 +32,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -61,7 +61,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -83,7 +83,7 @@ class PillContextTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: ServiceLocator.shared.userIndicatorController, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/UnitTests/Sources/RoomFlowCoordinatorTests.swift b/UnitTests/Sources/RoomFlowCoordinatorTests.swift index c792a5d86..0c4d7ed20 100644 --- a/UnitTests/Sources/RoomFlowCoordinatorTests.swift +++ b/UnitTests/Sources/RoomFlowCoordinatorTests.swift @@ -202,6 +202,7 @@ class RoomFlowCoordinatorTests: XCTestCase { roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), navigationStackCoordinator: navigationStackCoordinator, emojiProvider: EmojiProvider(), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, userIndicatorController: ServiceLocator.shared.userIndicatorController, diff --git a/UnitTests/Sources/RoomScreenViewModelTests.swift b/UnitTests/Sources/RoomScreenViewModelTests.swift index ca1cd2550..037d137cf 100644 --- a/UnitTests/Sources/RoomScreenViewModelTests.swift +++ b/UnitTests/Sources/RoomScreenViewModelTests.swift @@ -56,7 +56,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -93,7 +93,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -128,7 +128,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -160,7 +160,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -192,7 +192,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -221,7 +221,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -249,7 +249,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -277,7 +277,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -305,7 +305,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) @@ -425,7 +425,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: notificationCenter) @@ -451,7 +451,7 @@ class RoomScreenViewModelTests: XCTestCase { mediaPlayerProvider: MediaPlayerProviderMock(), voiceMessageMediaManager: VoiceMessageMediaManagerMock(), userIndicatorController: userIndicatorControllerMock, - application: ApplicationMock.default, + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analyticsService: ServiceLocator.shared.analytics, notificationCenter: NotificationCenterMock()) diff --git a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift index e2d24cb67..9ee8bd640 100644 --- a/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift +++ b/UnitTests/Sources/UserSessionFlowCoordinatorTests.swift @@ -50,6 +50,7 @@ class UserSessionFlowCoordinatorTests: XCTestCase { appLockService: AppLockServiceMock(), bugReportService: BugReportServiceMock(), roomTimelineControllerFactory: MockRoomTimelineControllerFactory(), + appMediator: AppMediatorMock.default, appSettings: ServiceLocator.shared.settings, analytics: ServiceLocator.shared.analytics, notificationManager: notificationManager,