Skip to content

[Question]: My watched ObservableObjectAtom isn't updating, am I configuring something wrong? #9

@felixakiragreen

Description

@felixakiragreen

Checklist

  • This is not a bug caused by platform.
  • Reviewed the README and documentation.
  • Checked existing issues & PRs to ensure not duplicated.

What happened?

(I apologize if this is not a bug, I wasn't sure which category of issue to file this under.)
(I have been using Atoms for a few weeks, with zero issues. I've been able to get almost everything working except this one last thing. I got a stuck on it a week ago, left it and moved on, but now I've come back to it and after a day of trying to get it working, I'm stumped.)

Context:

  1. I have an AccountStore, (most of which was copied from the Apple Passkey Sample Code) that is working correctly (when I use it as an EnvironmentObject. Here is part of it, shortened for brevity:
public final class AccountStore: NSObject, ObservableObject, ASAuthorizationControllerDelegate {
   @Published public private(set) var currentUser: User? = .default

   @MainActor
   public func signIn(username: String) async throws {
      // ... 
   }

   @MainActor
   public func signOut() {
      // ... 
   }

   // ...
}
  1. Since I've managed to move the rest of my data model over to Atoms, the only thing left is Auth & fetching the User (Player). Here are the Atoms I created in order to try to do this:
struct AccountStoreAtom: ObservableObjectAtom, Hashable {
	func object(context: Context) -> AccountStore {
		AccountStore()
	}
}

struct FetchPlayerAtom: ThrowingTaskAtom, Hashable {
	func defaultValue(context: Context) -> Player? {
		return nil
	}
	
	func value(context: Context) async throws -> Player {
		print("FetchPlayerAtom().value")
		return try await BackendAPI.fetchPlayer()
	}
}

struct PlayerAtom: ValueAtom, Hashable {
	func value(context: Context) -> Player? {
		let accountStore = context.watch(AccountStoreAtom())
		print("PlayerAtom().value", accountStore.currentUser as Any)
		
		if case let .authenticated(username) = accountStore.currentUser {
			print("PlayerAtom().value.authenticated()", username)
			let fetchPlayerPhase = context.watch(FetchPlayerAtom().phase)
			
			if case let .success(player) = fetchPlayerPhase {
				print("PlayerAtom().value.success()", player)
				return player
			}
		}
		
		return nil
	}
}

struct GameScreenAtom: ValueAtom, Hashable {
	func value(context: Context) -> GameScreen {
		let player = context.watch(PlayerAtom())
		
		print("GameScreenAtom().value", player as Any)

		if player == nil {
			return .mainMenu
		}
	
		if player!.isPlaying {
			return .homeBase
		}
	
		return .setupNew
	}
}

Expected Behavior

After logging in, the AccountStore publishes to currentUser, setting it to authenticated.

I expect that:

  1. The PlayerAtom(), which is watching AccountStoreAtom(), would notice that currentUser was updated. (Not happening)
  2. Then it would watch the FetchPlayerAtom(), and return the Player after it's fetched.
  3. Finally, the GameScreenAtom(), which is watching the PlayerAtom(), would notice that a Player now exists, and would set the gameScreen accordingly.

Reproduction Steps

I don't have a great way to help you reproduce this yet, I'm hoping that you can spot if I'm doing anything incorrectly in my code. If not, then I'll try to put together a reproducible example.

I'm attaching a Screen Recording of this. If you watch it, please play attention to the orange 🏗 Debug Values at the top of the screen. You will note that when the EnvObject.currentScreen updates (which is computed in the same way, from the AccountStore), the Atom.GameScreen does not.

Swift Version

5

Library Version

0.2.0

Platform

iOS

Scrrenshot/Video/Gif

RPReplay_Final1659150219.MP4

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions