Small tweaks to the Presence feature. (#5918)

This commit is contained in:
Doug
2026-07-23 08:40:48 +01:00
committed by GitHub
parent 1770e1d815
commit bf498a5d18
3 changed files with 24 additions and 43 deletions
@@ -1285,8 +1285,8 @@ class AppCoordinator: AppCoordinatorProtocol, AuthenticationFlowCoordinatorDeleg
return
}
// State-only (no HTTP): configure the background-refresh sync to carry set_presence=offline so it never
// reuses the client's foreground sync presence and marks the user online or idle.
// Configure the background-refresh sync to carry set_presence=offline so it doesn't mark the
// user online or idle. Note: If already online/idle then setting offline shouldn't override that.
_ = await userSession.clientProxy.configurePresence(.offline, sendImmediately: false)
await resumeClientServices()
@@ -100,10 +100,9 @@ final class PresenceService {
return
}
let clientProxy = clientProxy
sendTask = Task { [weak self, clientProxy] in
while !Task.isCancelled {
guard let presence = await self?.nextPendingPresence() else {
guard let presence = self?.nextPendingPresence() else {
break
}
@@ -113,10 +112,10 @@ final class PresenceService {
break
}
await self?.finishSending(presence)
self?.finishSending(presence)
}
await self?.finishSendTask()
self?.finishSendTask()
}
}
+19 -37
View File
@@ -175,12 +175,12 @@ struct PresenceServiceTests {
func newerTransitionWinsWhenAnEarlierSendIsInFlight() async throws {
appSettings.sharePresence = true
let onlineStarted = PassthroughSubject<Void, Never>()
let gate = AsyncStream<Void>.makeStream()
clientProxy.configurePresenceSendImmediatelyClosure = { [sendsSubject, onlineStarted] presence, sendImmediately in
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream<Void>.makeStream()
let (resumeOnlineSendStream, resumeOnlineSendContinuation) = AsyncStream<Void>.makeStream()
clientProxy.configurePresenceSendImmediatelyClosure = { [sendsSubject] presence, sendImmediately in
if presence == .online {
onlineStarted.send(())
var iterator = gate.stream.makeAsyncIterator()
onlineStartedContinuation.yield()
var iterator = resumeOnlineSendStream.makeAsyncIterator()
_ = await iterator.next()
}
@@ -188,15 +188,15 @@ struct PresenceServiceTests {
return .success(())
}
let onlineStartedDeferred = deferFulfillment(onlineStarted) { _ in true }
let onlineStartedDeferred = deferFulfillment(onlineStartedStream) { _ in true }
let service = makeService(initialApplicationState: .active)
try await onlineStartedDeferred.fulfill()
let unavailableDeferred = deferFulfillment(sendsSubject) { $0.presence == .unavailable }
notificationCenter.post(name: UIApplication.willResignActiveNotification, object: nil)
gate.continuation.yield()
gate.continuation.finish()
resumeOnlineSendContinuation.yield()
resumeOnlineSendContinuation.finish()
try await unavailableDeferred.fulfill()
let applied = clientProxy.configurePresenceSendImmediatelyReceivedInvocations.map(\.presence)
@@ -208,47 +208,29 @@ struct PresenceServiceTests {
func deinitCancelsPendingSend() async throws {
appSettings.sharePresence = true
let onlineStarted = AsyncStream.makeStream(of: Void.self)
let onlineCancelled = AsyncStream.makeStream(of: Void.self)
let onlineCompleted = AsyncStream.makeStream(of: Void.self)
let gate = AsyncStream.makeStream(of: Void.self)
let gateContinuation = gate.continuation
let onlineCancelledContinuation = onlineCancelled.continuation
let onlineCompletedContinuation = onlineCompleted.continuation
let (onlineStartedStream, onlineStartedContinuation) = AsyncStream.makeStream(of: Void.self)
let (onlineFinishedStream, onlineFinishedContinuation) = AsyncStream.makeStream(of: Void.self)
clientProxy.configurePresenceSendImmediatelyClosure = { presence, _ in
guard presence == .online else {
return .success(())
if presence == .online {
onlineStartedContinuation.yield()
try? await Task.sleep(for: .seconds(3600)) // Interrupted by the service's deinit cancelling the send task.
onlineFinishedContinuation.yield()
}
onlineStarted.continuation.yield()
let result: Result<Void, ClientProxyError> = await withTaskCancellationHandler {
var iterator = gate.stream.makeAsyncIterator()
_ = await iterator.next()
return .success(())
} onCancel: {
gateContinuation.finish()
onlineCancelledContinuation.yield()
onlineCancelledContinuation.finish()
}
onlineCompletedContinuation.yield()
onlineCompletedContinuation.finish()
return result
return .success(())
}
let onlineStartedDeferred = deferFulfillment(onlineStarted.stream) { _ in true }
let onlineStartedDeferred = deferFulfillment(onlineStartedStream) { _ in true }
var service: PresenceService? = makeService(initialApplicationState: .active)
#expect(service != nil)
try await onlineStartedDeferred.fulfill()
// Queue an unavailable send behind the in-flight online one.
notificationCenter.post(name: UIApplication.willResignActiveNotification, object: nil)
let onlineCancelledDeferred = deferFulfillment(onlineCancelled.stream) { _ in true }
let onlineCompletedDeferred = deferFulfillment(onlineCompleted.stream) { _ in true }
let onlineFinishedDeferred = deferFulfillment(onlineFinishedStream) { _ in true }
service = nil
try await onlineCancelledDeferred.fulfill()
try await onlineCompletedDeferred.fulfill()
try await onlineFinishedDeferred.fulfill()
let applied = clientProxy.configurePresenceSendImmediatelyReceivedInvocations.map(\.presence)
#expect(applied == [.online], "Expected deinit to cancel the queued unavailable send, got \(applied)")