iOS client for Grain grain.social
ios photography atproto
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

perf: guard AuthManager @Observable setters against redundant writes

The @Observable macro fires the observation registrar on every set,
even when the value is unchanged, so each token refresh was
invalidating every observer of isAuthenticated / userDID / userHandle
/ userAvatar — including GrainApp.body, which cascaded a full
rebuild of the tab hierarchy. Equality-check each assignment so
routine refreshes become no-ops.

authored by

Hima Aramona and committed by
Chad Miller
1fa23d4a f4906b44

+10 -5
+10 -5
Grain/API/AuthManager.swift
··· 316 316 TokenStorage.userHandle = response.handle 317 317 TokenStorage.tokenExpiresAt = Date().addingTimeInterval(TimeInterval(response.expiresIn)) 318 318 319 - isAuthenticated = true 320 - userDID = response.sub 321 - userHandle = response.handle 319 + // Guard each @Observable assignment: the macro's setter always fires the 320 + // observation registrar even when the value is unchanged, so token refreshes 321 + // would otherwise invalidate every observer (including GrainApp.body). 322 + if !isAuthenticated { isAuthenticated = true } 323 + if userDID != response.sub { userDID = response.sub } 324 + if userHandle != response.handle { userHandle = response.handle } 322 325 } 323 326 324 327 func fetchAvatarIfNeeded() async { ··· 339 342 let client = XRPCClient(baseURL: Self.serverURL) 340 343 do { 341 344 let profile = try await client.getActorProfile(actor: did) 342 - userAvatar = profile.avatar 343 - TokenStorage.userAvatar = profile.avatar 345 + if userAvatar != profile.avatar { 346 + userAvatar = profile.avatar 347 + TokenStorage.userAvatar = profile.avatar 348 + } 344 349 } catch { 345 350 logger.error("Avatar fetch failed: \(error)") 346 351 }