2

In one of my Simperium powered apps, I'm currently trying to implement a clean experience for users when signing out from their sync account, signing in to a sync account with existing data, switching accounts etc.

What I want to achieve is the following (suggestions welcome, if that doesn't make any sense):

  • When signing out of a sync account, I don't want to delete anything locally, so the user can continue using the data (which is also important because syncing is a premium feature and the user might cancel it).
  • When creating a new sync account (which obviously doesn't contain any data), I want to retain the local data and sync it up to the new account (which makes sense if someone starts using the app without sync and then activates sync later).
  • However, when signing in to an existing sync account with existing data, I need to prevent merging hell between the local and the remote data (imagine someone switching accounts!). I think that completely overwriting the local data with the remote data is what makes most sense here.

Simperium does not seem to directly support my approach, but rather only offers to delete the data upon sign out. While trying to bend Simperium to my needs, I'm currently facing two issues/questions:

  1. What is the preferred way to delete all local data (including all Simperium meta data, including that in user defaults)? NB: I need to do this while the user is NOT logged in to Simperium, so I can't use signOutAndRemoveLocalData:YES. I guess I need to do something like [self.buckets.allValues makeObjectsPerformSelector:@selector(deleteAllObjects)]; (which is done in the sign out completion handler), but I can't find any public interface for this. Would it be save to just delete all objects directly via CoreData, or would that leave anything in user defaults that could cause issues later, for example?

  2. I noticed that Simperium leaves its ghost data intact after signing out. This causes massive issues when signing in to another (in my case new) account, because it doesn't treat the objects as new objects to be synced up, but rather deletes all local objects (presumably because it can't find it on the remote)!

    • Is this really the intended behavior? I'd expect Simperium to get rid of all sync metadata after signing out, even if the data itself is not removed. Even more so in the case of the ghost data, because this data is tied directly to the sync account - it represents the remote state of the object, and I'd argue that after signing out there is no remote state anymore (because there is no "remote").
    • Anyways, what's the best way for me to reset the ghost data back to the initial clean state (that is, probably something like { "key" : "...", "version" : "0" })? I could do that manually, but that feels like massively interfering with the internals of Simperium.

Sorry for this lengthy question.. Any help/pointers very much appreciated, thanks!


Update/clarification

My goal is really not so much to carry data on from one account to another. Actually, as I said, my plan was to delete all local data before signing into an exising account, which should prevent any problems like duplicates - the client just discards its local state completely and uses whatever is currently stored in the remote sync account. The only case where the retained ghost data causes me problems is this:

  1. App currently syncing with Account A.
  2. User signs out from Account A (using signOutAndRemoveLocalData:NO completion:).
  3. User happily continues to use the app (perhaps for quite a while)
  4. User creates a new sync account B (that is, one that does not contain any data on the server). Now, with the default behavior, Simperium will delete all local objects that contain ghost data with version > 0 (but will leave any objects that have been created while not signed in to any sync account, thus leaving the database in a potentially invalid state with broken relationships etc.).

I just don't think that this is the behavior that the user would expect and I'm sure it will lead to many support inqueries...

My authentication flow looks exactly like you suggested: I have my own backend that manages subscriptions and generates the auth token via the Simperium Auth API and sends it to the client. But I don't really see how this solves my problems. I completely agree that it's not a common use case that users switch sync accounts (although the situation is a bit different than for mail accounts, because you can use it without sync), and I definitely don't want to encourage users to do that. My problem is that, even if it's something that happens only rarely, it should not break the app. And I don't know how I could prevent users from doing it, because they can always freely decide to signout and sign up/in again.

What behavior would you suggest to ensure a good UX? Always delete local data upon sign out?

Daniel Rinser
  • 8,855
  • 4
  • 41
  • 39

2 Answers2

1

By design, Simperium isn't meant to carry data over from Account A to Account B. Imagine a Mail client, that would upload your Inbox upon 'Signout + Sign into a different account'.

The preferred way to reset the internal metadata is, as you pointed out, the signOutAndRemoveLocalData selector. However, the SPManagedObject's simperiumKey and ghostData are left intact. The big reason we've got for not nuking those two fields is that, if you were to sign into the very same account again, you wouldn't get any duplicate entities (the lib would just know that it's the same object).

Implementing a mechanism to reset the SPManagedObject's Simperium Metadata fields is a potential bad idea, for that reason. Nevertheless, if you wanted to test that out, you could loop through the entities to reset, nuke the fields, and call this selector, so that the change is disregarded by the lib itself.

Furthermore, if you wanted to sign into a different account, you would probably get duplicate entities issues (that is, assuming that this second account already has objects uploaded).

I'd like to suggest a potential flow for this scenario:

  1. On Launch, your app can hit this initializer, so that the Core Data stack is ready for work.

  2. Once your users are ready to enable Sync'ing, you could either use this method to trigger the iOS Interface, or you could implement an API on your own backend, to wrap up the Simperium User Authentication.

    This Helper API could return the authToken, which would be used by the iOS client to hit this method.

  3. If you had to disable Sync'ing, for whatever reason, you could either:

    A. Stop hitting the 'authenticate' method, or

    B. Toggle this flag, as needed.

Assuming that your iOS App already has a User Authentication flow, wrapping up in your own backend the Simperium Auth call would be a great idea, because it would be crystal clear that the Sync'ing feature is 100% tied up to his own account.

I'm afraid that carry'ing on data from 'Account A' to 'Account B', although technically possible, sounds like a bad idea. As of now, the only supported mechanism to prevent duplicates relies on having the same simperiumKey locally and remotely. And (as far as i understand), that assumption won't be true in your scenarios.

Hope that helps!

Jorge

  • Thanks a lot for your answer, Jorge! Great to see such responsive support from you guys. I've just updated my question with a response/clarification (because its too long for a comment). – Daniel Rinser May 12 '15 at 19:23
0

I'm sorry about the late reply!. Yes, i believe the best UX, as you say, would be to simply delete the data upon LogOff.

You can always enable or disable sync'ing (by means of the networkEnabled flag). This could help you handle the Sync'ing as a premium feature.

However, upon logout (personally speaking) i would always expect the app to nuke all of the 'personal data' i've added. Imagine how weird it'd be if Mail.app wouldn't cleanup your inbox, even after nuking an email account.

Hope you're doing great! Jorge

  • Thanks for your comment. I agree that this behavior would not be expected in a mail app, but I'm not building a mail app.. ;-) In my app, the account is not a "personal" account, but rather a means to sync rather uncritical data between different family members. You can use the app without sync and "signing out" only means "signing out from the sync account". I know many iOS apps that don't nuke the local data when signing out from sync. Anyways, thanks for your help and sharing your point of view. I think I will try my luck with implementing my original approach (delete open sign **in**). – Daniel Rinser May 20 '15 at 09:08
  • One last thing: Could you please give me a pointer for my first question from above: What's the best way to delete all local data while **not** logged in (incl. all metadata, e.g. stuff in user defaults and Application Support directory)? `signOutAndRemoveLocalData:YES` calls `deleteAllObjects` on all buckets, but I can't find any public API to get all buckets. Simply deleting the persistent store is probably a bad idea, because it leaves the Simperium data (e.g. in Application Support) in place, right? – Daniel Rinser May 20 '15 at 09:15
  • Ahhh sorry i've missed that one!. I'm afraid that the `signOutAndRemoveLocalData:YES` method has a check that would prevent execution if the user is *not* logged in. What you can do is simply delete the objects from CoreData, in the way yo would normally proceed, and once the context is saved, you could call the `[_simperium resetMetadata]` helper. There is no (public) property that would expose all of the buckets, but, if needed, it can be easily added. – Jorge Leandro Perez May 21 '15 at 12:46