Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Current User is not kept in sync between App and Extension #1648

Open
4 tasks done
benpackard opened this issue Jun 3, 2022 · 10 comments
Open
4 tasks done

Current User is not kept in sync between App and Extension #1648

benpackard opened this issue Jun 3, 2022 · 10 comments
Labels
bounty:$50 Bounty applies for fixing this issue (Parse Bounty Program) type:bug Impaired feature or lacking behavior that is likely assumed

Comments

@benpackard
Copy link

benpackard commented Jun 3, 2022

New Issue Checklist

Issue Description

An app extension such as a widget should be able to access the current logged in user in order to make queries. There are good instructions about configuring your app and extension to allow this. However, when the logged-in user changes in the app, the extension's version of this does not update accordingly. I also believe that a user change from the extension is similarly not reflected in the main app.

Steps to reproduce

Please refer to the attached example project, and the included Parse Widget Test Notes.txt. The tests steps themselves are reproduced below:

Test 1

  1. Launch the widget. Note the timestamp. Observe that the current user is nil. Confirm that the user-count operation was successful. This just confirms you are connected to the server correctly.
  2. Tap the widget to open the app. Tap 'log in' and confirm that the current user is updated.
  3. Return to the widget. Confirm that the timestamp is updated, so the widget was refreshed after the user change.

Expected: the current logged-in user is displayed.

Actual: the current user is still nil.

Test 2 (continue from test 1)

  1. Relaunch the widget from Xcode. Confirm that the logged-in user is now correctly displayed.
  2. Tap the widget to open the app and log out. Confirmt hat the displayed user is nil.
  3. Return to the widget. Confirm that the timestamp is updated, so the widget was refreshed after the user change.

Expected: Current user should be nil.

Actual: The current user is still set to the previous value, and an error message is displayed. (The error is correct since the session token was revoked when the user logged out, only the widget's version of the current user didn't update.)

ParseWidgetUserTest.zip

@parse-github-assistant
Copy link

parse-github-assistant bot commented Jun 3, 2022

Thanks for opening this issue!

  • ❌ Please edit your post and use the provided template when creating a new issue. This helps everyone to understand your post better and asks for essential information to quicker review the issue.

@mtrezza mtrezza added the type:bug Impaired feature or lacking behavior that is likely assumed label Jun 3, 2022
@cbaker6
Copy link
Contributor

cbaker6 commented Jul 3, 2022

Have you looked at? #1220

@benpackard
Copy link
Author

Thanks @cbaker6, yes I have seen that issue and it is not related unfortunately. In his instance, queries would fail every time because he missed a configuration step. In this issue, the configuration is correct and queries are actually successful. It is only when the user changes that the extension does not update and queries fail (since the token is now invalid). In other words, the main app's currentUser and the extension's currentUser return different things until the app is relaunched, at which time they are back in sync and queries work again.

@benpackard
Copy link
Author

benpackard commented Jul 3, 2022

For anyone else experiencing this issue, I don't really have a direct workaround, but for now I am avoiding relying the shared currentUser in the extension. Instead, in the main app I am trying to keep track of every action that can cause a change to current user (log in and log out of course, various sign ups, etc) and manually save a copy of the session token to the keychain. Every time the extension needs to make a query, I check for this token. If it is present, I use PFUser.becomInBackground to manually set the current user. If it is nil, I log out of the extension.

@mman
Copy link
Contributor

mman commented Apr 4, 2023

I have just encountered this issue as well. I believe it is caused by the fact that PFUser.current is stored both in keychain and on the filesystem. When you logOut in main app, the keychain is properly cleaned, and filesystem for the main app as well, but PFUser.current remains intact in the memory of the extension, which is thus using stale data.

The extension needs a way to forcibly forget and reload the PFUser.current from the keychain and filesystem. Keychain and filesystem are already shared between the main app and extension, so I believe the underlying issue is that there is simply stale data kept in extension memory... that is all...

still investigating how to workaround this with current SDK.

@mman
Copy link
Contributor

mman commented Apr 5, 2023

Investigation: Both PFUser.currentUser and PFInstallation.currentInstallation are loaded from disk (sessionToken from keychain) and kept in memory for the whole life of process (main or extension).

Because data and keychain are shared between the main app and extension, this is not a problem for cold app start or cold extension start, when Parse loads proper and fresh data from disk/keychain.

But when currentUser or currentInstallation object changes in the main app, as a result of sign in, sign out, sign up, there is no way for extension to know it, unless you tell it somehow. How to actually tell the extension that current user has changed in the main app depends on what works for you. Shared User Defaults flag may work nicely.

I have prototyped a solution that works for me, but may not be ideal. In my extensions I simply forget the current user whenever the extension is called, and force a reload. Since extensions have little time to waste, this was the quickest method I came up with. Another more generic solution may be comparing the filesystem timestamp to see if reload is needed, which would then work both ways.

But for now, I have added a method PFUser.forgetCurrentUser which simply clears the current user from memory, thus forcing any subsequent use of it to load it from disk/keychain again. Similar thing may be done for currentInstallation.

The code for now lives here: mman@dc3307e

ideas?
Martin

@mtrezza
Copy link
Member

mtrezza commented Apr 9, 2023

PFUser.currentUser and PFInstallation.currentInstallation are loaded from disk (sessionToken from keychain)

Maybe a solution would be to store the current user and installation in keychain like the session token, then have both the app and extension access it? If the session token is already stored there, the necessary basic keychain logic may already be there? We may just need to add a simple migration mechanism that migrates from file to keychain.

@mtrezza mtrezza added the bounty:$50 Bounty applies for fixing this issue (Parse Bounty Program) label Apr 9, 2023
@mman
Copy link
Contributor

mman commented Apr 10, 2023

PFUser.currentUser and PFInstallation.currentInstallation are loaded from disk (sessionToken from keychain)

Maybe a solution would be to store the current user and installation in keychain like the session token, then have both the app and extension access it? If the session token is already stored there, the necessary basic keychain logic may already be there? We may just need to add a simple migration mechanism that migrates from file to keychain.

It does not matter much where the user or installation or session token are stored. What matters is when they should be reloaded and how to detect/communicate that from the main app to the extension or the other way. The key issue here is that when you sign out in the main app, the extension still has a current user and session token in memory and will use it until it realizes that session token is actually invalid or current user refresh fails.

The logic to get current user / current installation at the moment simply returns the object that is in memory, and if it is not there, it will load it. What I have done is just added a method to “forget” the object so that it is refetched forcibly, proper systematic fix will somehow check the validity of the in memory object against the permanent storage (disk or keychain) and will reload if needed…

@mtrezza
Copy link
Member

mtrezza commented Apr 10, 2023

Keychain may provide advantages. We could always read the keychain value (instead of occasionally loading it from file) and synch the keychain across app and extension, see the Apple docs. So whenever the extension queries the user, it directly uses the keychain value which is always in-sync with the app that may have modified its value.

@mman
Copy link
Contributor

mman commented Apr 11, 2023

@mtrezza Yeah, always storing/fetching from either keychain or disk could be a solution. The sessionToken being sensitive and unique to everything makes sense to store in keychain (we do that already). currentUser as an object may be stored there as well I assume, since it is always shared across all extensions.

However for example currentInstallation currently represents the iOS app and stores deviceToken for push. Live Activity runs in an extension and can haver its own deviceToken for push. Watch app extension can run independently from the main app and can obtain their own unique deviceToken, so live activity extension and watch app extension nowadays kind of behave like separate Installation objects, so sharing one main Installation object via keychain probably does not make sense.

Another option I was thinking about was to store push tokens for watch app and live activity as separate fields in Installation object, but that way there is no easy way to target those in existing push adapter infra, and I think it will be rather hacky.

So not an easy way forward as far as I can see, because we probably want to unify the support for shared currentUser and separate currentInstallation for main app and all its potential extensions. And all this while supporting existing App Groups config where the app and extensions essentially write to the same directory. Thinking out loud we probably could include extension type or bundle identifier or something unique in a dir path where to store everything... not sure what is the best

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bounty:$50 Bounty applies for fixing this issue (Parse Bounty Program) type:bug Impaired feature or lacking behavior that is likely assumed
Projects
None yet
Development

No branches or pull requests

4 participants