Skip to content

fix: prevent main app from blocking notification extension (take 4) - WPB-23511 🍒#4928

Open
samwyndham wants to merge 1 commit into
release/cycle-4.16from
fix/nse-stuck-and-expiring-4.16-WPB-23511
Open

fix: prevent main app from blocking notification extension (take 4) - WPB-23511 🍒#4928
samwyndham wants to merge 1 commit into
release/cycle-4.16from
fix/nse-stuck-and-expiring-4.16-WPB-23511

Conversation

@samwyndham

@samwyndham samwyndham commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Issue

This is a cherry pick of #4880 to 4.16.

Some minor manual changes were necessary to make use of they SafeCoreCrypto type which wasn't present in 4.16

Testing

See #4880

Checklist

  • Title contains a reference JIRA issue number like [WPB-XXX].
  • Description is filled and free of optional paragraphs.
  • Adds/updates automated tests.

Copilot AI review requested due to automatic review settings June 25, 2026 13:22

@zenkins zenkins left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Auto-approved cherry-pick PR 🍒.

@samwyndham samwyndham requested a review from netbe June 25, 2026 13:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR replaces the prior withExpiringActivity approach with a background-task “executer” abstraction so the main app can use UIApplication.beginBackgroundTask(...) while app extensions can use a no-op passthrough, reducing the chance that the main app blocks the Notification Service Extension (NSE) and causes it to expire.

Changes:

  • Introduces BackgroundTaskExecuter + withBackgroundTask + PassthroughTaskExecuter (WireSystem) and migrates sync/message-sending work from expiring-activity wrapping to background-task wrapping.
  • Adds AppBackgroundTaskExecuter for the main app and threads a backgroundTaskExecuter dependency through session/sync components; introduces SafeCoreCrypto to run CoreCrypto transactions under the executer.
  • Updates NSE and share extension cancellation flows to attempt “extra time” cancellation via ProcessInfo.performExpiringActivity(...), and removes the old ExpiringActivity implementation/tests.

Reviewed changes

Copilot reviewed 64 out of 66 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
WireDomain/Tests/WireDomainTests/Synchronization/PullPendingUpdateEventsSyncV2Tests.swift Updates test CoreCrypto provider setup to use SafeCoreCrypto.
WireDomain/Tests/WireDomainTests/Synchronization/IncrementalSyncV2Tests.swift Updates test setup to inject SafeCoreCrypto and PassthroughTaskExecuter.
WireDomain/Tests/WireDomainTests/Synchronization/IncrementalSyncTests.swift Injects PassthroughTaskExecuter into IncrementalSync under test.
WireDomain/Sources/WireDomain/Synchronization/IncrementalSyncV2.swift Wraps live stream processing in withBackgroundTask(...) and stores executer dependency.
WireDomain/Sources/WireDomain/Synchronization/IncrementalSync.swift Wraps live stream processing in withBackgroundTask(...) and stores executer dependency.
WireDomain/Sources/WireDomain/Notifications/Protocols/NotificationServiceProtocol.swift Removes the protocol previously used by the NSE wrapper.
WireDomain/Sources/WireDomain/Notifications/NotificationServiceExtension.swift Removes protocol conformance; adds task state + async cancellation API.
WireDomain/Sources/WireDomain/Notifications/Components/NSEUserScope.swift Threads backgroundTaskExecuter into NSE scopes and refactors payload processing call shape.
WireDomain/Sources/WireDomain/Notifications/Components/NSEFlow.swift Creates a shared PassthroughTaskExecuter for NSE dependency graph.
WireDomain/Sources/WireDomain/Notifications/Components/NSEClientScope.swift Refactors payload processing and injects backgroundTaskExecuter into CoreCrypto provider.
WireDomain/Sources/WireDomain/Components/UserSessionComponent.swift Threads backgroundTaskExecuter through to client session component creation.
WireDomain/Sources/WireDomain/Components/ClientSessionComponent.swift Injects backgroundTaskExecuter into incremental sync initializers.
wire-ios/Wire-iOS/Sources/AppRootRouter.swift Adds backgroundTaskExecuter dependency to router init.
wire-ios/Wire-iOS/Sources/AppDelegate.swift Constructs AppBackgroundTaskExecuter and passes it into session/router wiring.
wire-ios/Wire-iOS/Sources/AppBackgroundTaskExecuter.swift Adds main-app BackgroundTaskExecuter implementation using UIApplication APIs.
wire-ios/Wire-iOS.xcodeproj/project.pbxproj Registers the new executer test file in the Xcode project.
wire-ios/Wire-iOS Tests/AppBackgroundTaskExecuterTests.swift Adds unit tests for background task begin/end + cancellation behaviors.
wire-ios/Wire-iOS Share Extension/Sources/Content/PostContent.swift Adds cancellation logging + expiring-activity wait to keep share extension alive during cancel.
wire-ios/Wire Notification Service Extension/NotificationService.swift Cancels NSE work on expiration and waits (bounded) for cancellation to release file locks.
wire-ios/Tests/Sourcery/generated/AutoMockable.generated.swift Adds a mock for BackgroundTaskApplication used by executer tests.
wire-ios-system/Tests/ExpiringActivityTests.swift Removes tests for the old expiring-activity helper.
wire-ios-system/Source/ExpiringActivity.swift Removes the old expiring-activity helper implementation.
wire-ios-system/Source/BackgroundTasks.swift Introduces BackgroundTaskExecuter, withBackgroundTask, and PassthroughTaskExecuter.
wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-sync-engine/Tests/Source/Synchronization/SyncAgentTests.swift Injects PassthroughTaskExecuter into SyncAgent creation in tests.
wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSessionBuilder.swift Injects PassthroughTaskExecuter when building user sessions (non-main-app).
wire-ios-sync-engine/Source/UserSession/ZMUserSession/ZMUserSession.swift Threads backgroundTaskExecuter through component construction and sync agent wiring.
wire-ios-sync-engine/Source/Synchronization/SyncAgent.swift Replaces expiring activity usage with withBackgroundTask(...) and stores executer dependency.
wire-ios-sync-engine/Source/Synchronization/StrategyDirectory.swift Adds backgroundTaskExecuter to strategy directory wiring.
wire-ios-sync-engine/Source/SessionManager/UserSessionLoader.swift Threads backgroundTaskExecuter into CoreCrypto provider and session creation.
wire-ios-sync-engine/Source/SessionManager/SessionManager.swift Stores and passes backgroundTaskExecuter down into session loader creation.
wire-ios-sync-engine/Source/E2EI/CRL/CertificateRevocationListsChecker.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-sync-engine/Source/Use cases/GetE2eIdentityCertificatesUseCase.swift Switches CoreCrypto access type to SafeCoreCrypto for identity fetch.
wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift Injects PassthroughTaskExecuter into CoreCrypto provider creation in tests.
wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift Injects PassthroughTaskExecuter into CoreCrypto provider creation in tests.
wire-ios-request-strategy/Sources/Message Sending/MessageSenderTests.swift Adds PassthroughTaskExecuter to strategy directory creation in tests.
wire-ios-request-strategy/Sources/Message Sending/MessageSender.swift Replaces expiring activity usage with withBackgroundTask(...) and stores executer dependency.
wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotator.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-share-engine/WireShareEngineTests/BaseSharingSessionTests.swift Injects PassthroughTaskExecuter into strategy directory creation in tests.
wire-ios-share-engine/Sources/StrategyFactory.swift Adds backgroundTaskExecuter to strategy directory construction.
wire-ios-share-engine/Sources/SharingSessionLoader.swift Creates and threads a PassthroughTaskExecuter through strategy/core-crypto wiring.
wire-ios-share-engine/Sources/SharingSession.swift Creates and threads a PassthroughTaskExecuter through strategy/core-crypto wiring.
wire-ios-data-model/WireDataModel.xcodeproj/project.pbxproj Adds SafeCoreCrypto.swift to the WireDataModel build.
wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/MLS/MLSServiceTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift Updates CoreCrypto provider mocking to return SafeCoreCrypto.
wire-ios-data-model/Support/Sources/CoreCryptoMocksEnvelope.swift Wraps mock core crypto in SafeCoreCrypto for support scaffolding.
wire-ios-data-model/Support/Sourcery/generated/AutoMockable.generated.swift Updates CoreCryptoProviderProtocol mock return type to SafeCoreCrypto.
wire-ios-data-model/Source/Proteus/ProteusService.swift Switches CoreCrypto access type to SafeCoreCrypto and uses .transaction.
wire-ios-data-model/Source/MLS/MLSService.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/MLS/MLSEncryptionService.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/MLS/MLSActionExecutor.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/MLS/CreateMLSGroupUseCase.swift Switches CoreCrypto dependency type to SafeCoreCrypto.
wire-ios-data-model/Source/E2EIdentity/E2EIVerificationStatusService.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/E2EIdentity/E2EISetupService.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/E2EIdentity/E2EIService.swift Switches CoreCrypto access type to SafeCoreCrypto.
wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift Adds wrapper to run CoreCrypto transactions inside withBackgroundTask.
wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift Changes provider API to return SafeCoreCrypto and injects backgroundTaskExecuter.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +23 to +29
public func withBackgroundTask<T: Sendable>(
name: String? = nil,
executer: any BackgroundTaskExecuter,
operation: sending @escaping @isolated(any) () async throws -> T
) async throws -> T {
try await executer.execute(name: name, operation: operation)
}
Comment on lines 103 to +105
func cancel(completion: @escaping () -> Void) {
sendController?.cancel(completion: completion)
guard let sendController else { return }

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Test Results

    6 files    881 suites   9m 29s ⏱️
6 184 tests 6 156 ✅ 28 💤 0 ❌
6 185 runs  6 157 ✅ 28 💤 0 ❌

Results for commit 75b882f.

♻️ This comment has been updated with latest results.

Summary: workflow run #28325826861
Allure report (download zip): html-report-31266-fix_nse-stuck-and-expiring-4.16-WPB-23511

… WPB-23511 (#4880)

# Conflicts:
#	WireDomain/Sources/WireDomain/Notifications/Components/NSEClientScope.swift
#	WireDomain/Sources/WireDomain/Notifications/NotificationServiceExtension.swift
#	WireDomain/Tests/WireDomainTests/Synchronization/IncrementalSyncV2Tests.swift
#	WireDomain/Tests/WireDomainTests/Synchronization/PullPendingUpdateEventsSyncV2Tests.swift
#	wire-ios-data-model/Source/Core Crypto/CoreCryptoProvider.swift
#	wire-ios-data-model/Source/Core Crypto/SafeCoreCrypto.swift
#	wire-ios-data-model/Support/Sources/CoreCryptoMocksEnvelope.swift
#	wire-ios-data-model/Tests/MLS/MLSActionExecutorTests.swift
#	wire-ios-data-model/Tests/MLS/MLSEncryptionServiceTests.swift
#	wire-ios-data-model/Tests/MLS/MLSServiceTests.swift
#	wire-ios-data-model/Tests/Proteus/ProteusServiceTests.swift
#	wire-ios-data-model/Tests/Source/E2EIdentity/E2EIServiceTests.swift
#	wire-ios-data-model/Tests/Source/E2EIdentity/E2EIVerificationStatusServiceTests.swift
#	wire-ios-data-model/Tests/UseCases/IsUserE2EICertifiedUseCaseTests.swift
#	wire-ios-request-strategy/Sources/E2EIdentity/E2EIKeyPackageRotatorTests.swift
#	wire-ios-request-strategy/Tests/Helpers/MessagingTestBase.swift
#	wire-ios-request-strategy/Tests/Helpers/ProteusClientSimulator.swift
#	wire-ios-share-engine/Sources/SharingSession.swift
#	wire-ios-share-engine/Sources/SharingSessionLoader.swift
#	wire-ios-sync-engine/Source/SessionManager/UserSessionLoader.swift
#	wire-ios-sync-engine/Tests/Source/E2EI/CertificateRevocationListsCheckerTests.swift
#	wire-ios-sync-engine/Tests/Source/Use cases/CheckOneOnOneConversationIsReadyUseCaseTests.swift
#	wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTestsBase.swift
#	wire-ios-sync-engine/Tests/Source/UserSession/ZMUserSessionTests_NetworkState.swift
#	wire-ios-system/Source/ExpiringActivity.swift
#	wire-ios-system/Tests/ExpiringActivityTests.swift
#	wire-ios/Wire-iOS/Sources/AppDelegate.swift
@samwyndham samwyndham force-pushed the fix/nse-stuck-and-expiring-4.16-WPB-23511 branch from 44a8401 to 75b882f Compare June 28, 2026 14:45
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants