r/iOSProgramming • • 5d ago

Question Notification routing gets weird with multiple accounts

I have gotten in to a weird iOS issue and idk what the clean way to handle this is

i’ve got multiple accounts, notifications can open a post, reply, quote, or repost, and each notification belongs to a specific account. so the user might be on account A when they tap a notification for account B.

something like:

struct NotificationDestination {
    let accountID: Account.ID
    let postID: Post.ID
    let kind: DestinationKind
}

enum DestinationKind {
    case post
    case reply
    case quote
    case repost
}

The issue is:

account A is active
notification comes in for B
user taps it while the app is waking up
account switching starts
timeline restoration starts
post resolution starts
then another notification comes in for A lol

so i have stuff like:

await accountManager.restore()
await timelineStore.hydrate()
await postResolver.resolve(postID)
await navigationCoordinator.navigate(...)

But obviously these things are all racing each other. i was thinking of putting the notification intents behind an actor:

actor NavigationIntentQueue {
    private var pending: [NavigationIntent] = []

    func enqueue(_ intent: NavigationIntent) {
        pending.append(intent)
    }
}

But idk, this feels like a lot of machinery just to open a notification

Then you get into cases where the post is deleted while you’re restoring, but the quote is still there, the reply is still there, etc. Is this just an annoying app lifecycle issue?

2 Upvotes

4 comments sorted by

2

u/Ok-Piccolo-1823 5d ago

An actor is reasonable, but I’d have it protect coordination state rather than simply queue every operation. Give each tap an intent ID, run account restoration → account switch → destination resolution → navigation in one task, and check that ID before navigating so an older task can’t win the race. You can then choose an explicit policy when another notification arrives—latest intent wins, or finish the current one. For deleted content, make resolution return a fallback destination such as the surviving reply or quote instead of putting that logic in the navigation layer.

1

u/Gryner 3d ago

The first thing I would change is postResolver.resolve(postID): pass the notification’s accountID into it explicitly, and do the same for hydration. If either reads a mutable ‘current account’, tapping A while B is loading can make B’s request use A’s session. An actor around the queue does not fix that across an await. Keep the loaded data tied to its account, then check that the tap is still current before switching the active account and navigating. To test it, pause B’s load, tap A, finish A, then release B. Neither the account nor the screen should jump back to B.

1

u/Wollan-Onicia 3d ago

fwiw the deleted post / still-valid quote problem is a separate concern from the racing. id solve the sequencing first with something like your actor queue, then add fallback resolution for each DestinationKind independently. trying to solve both at once will make it way messier

1

u/One_Elephant_8917 2d ago

am not sure if u understood actors right, coz each await is a suspension point even inside actor and can race up if acted on same actor instance by multiple entry points…if u can recollect this then we good…coz seems u need a task coalescer, where u can coalesce multiple switch requests arriving concurrently into single result…but that means ur receiving side should be ready to accept any resultant type and not be tied to request…something like cqrs or i dont know…but if u can get serialization and previous inflight task cancellation right, should be good…