Background
#1174 identified that every AuthClient registers its Dependencies (APIClient,
SessionManager, event emitter, etc.) into a process-global dictionary
(Dependencies.instances in Sources/Auth/Internal/Dependencies.swift) keyed by an
ever-incrementing clientID, with nothing ever removing entries.
The immediate leak (point 1 from #1174) and the self-referential lifecycle-observer
retain cycle (point 3) were fixed directly:
AuthClient now has a deinit that removes its entry from Dependencies.instances.
- The app-lifecycle Combine subscriptions are now stored on an instance property
(appLifecycleCancellables) instead of a local var kept alive by a
self-referential closure, so they're released deterministically when the client
deallocates.
Remaining work (point 2 from #1174)
The registry pattern itself is still architecturally awkward: Dependencies is
looked up by clientID through a global LockIsolated<[AuthClientID: Dependencies]>
dictionary rather than being held directly by the AuthClient instance. This exists
so that several nonisolated computed properties on the actor AuthClient
(api, sessionManager, eventEmitter, sessionStorage, pkce, etc., see
Sources/Auth/AuthClient.swift) can synchronously resolve their dependencies without
actor isolation — introduced in #445 ("add support for multiple auth instances").
Now that entries are cleaned up in deinit, the memory-leak angle is resolved, but it
would be cleaner and less error-prone long-term to store Dependencies directly on
AuthClient (e.g. behind a nonisolated(unsafe) stored property protected the same
way _globalClientID is, or restructuring so those computed properties don't need
process-global lookup at all) rather than relying on a global keyed registry + manual
teardown. This would remove an entire class of "forgot to clean up the registry" bugs
by construction.
This is a refactor, not a correctness bug — no user-facing impact expected.
Background
#1174 identified that every
AuthClientregisters itsDependencies(APIClient,SessionManager, event emitter, etc.) into a process-global dictionary
(
Dependencies.instancesinSources/Auth/Internal/Dependencies.swift) keyed by anever-incrementing
clientID, with nothing ever removing entries.The immediate leak (point 1 from #1174) and the self-referential lifecycle-observer
retain cycle (point 3) were fixed directly:
AuthClientnow has adeinitthat removes its entry fromDependencies.instances.(
appLifecycleCancellables) instead of a localvarkept alive by aself-referential closure, so they're released deterministically when the client
deallocates.
Remaining work (point 2 from #1174)
The registry pattern itself is still architecturally awkward:
Dependenciesislooked up by
clientIDthrough a globalLockIsolated<[AuthClientID: Dependencies]>dictionary rather than being held directly by the
AuthClientinstance. This existsso that several
nonisolatedcomputed properties on theactor AuthClient(
api,sessionManager,eventEmitter,sessionStorage,pkce, etc., seeSources/Auth/AuthClient.swift) can synchronously resolve their dependencies withoutactor isolation — introduced in #445 ("add support for multiple auth instances").
Now that entries are cleaned up in
deinit, the memory-leak angle is resolved, but itwould be cleaner and less error-prone long-term to store
Dependenciesdirectly onAuthClient(e.g. behind anonisolated(unsafe)stored property protected the sameway
_globalClientIDis, or restructuring so those computed properties don't needprocess-global lookup at all) rather than relying on a global keyed registry + manual
teardown. This would remove an entire class of "forgot to clean up the registry" bugs
by construction.
This is a refactor, not a correctness bug — no user-facing impact expected.