0

So I'm working on an iOS app and I’m thinking about how to keep user profile data in sync between server and client.

The first idea that comes to mind is to create a class called User. It would be a subclass of NSMutableDictionary.

Then I would declare a shared-instance within User so all view controllers and other classes could reference it without having to create their own instances.

User would have read-only properties like:

User.DisplayName = "User1234"
User.ProfileUrl = "http://server.com/profile/121234.png"
User.Points = 12345
User.IAPs = ["removeAds","accountUpgrade"]

Then I'd make a function called User.update() that just pulls userdata from the server and populates the User class properties. This is the only function that would be allowed to write over the User properties.

I would run the User.update() function during times such as:

  • App launch
  • App re-opened from sleep
  • Profile update
  • After in-app-purchase
  • etc.

Does this seem like a reasonable design for keeping local user profile data in sync with the server?

1
  • Before you go much farther plan in security. I suspect this scheme would be easily hacked . Commented Mar 2, 2018 at 11:51

2 Answers 2

1

Few things to take into account

Missleading names

Then I'd make a function called User.update() that just pulls userdata from the server and populates the User class properties. This is the only function that would be allowed to write over the User properties.

Whoever is familiar with Active Record design pattern would be fooled by the name of the method.

The name could make us belive that we are pushing changes to the server, but in reality -according to the question- we would be doing the opposite!!!

If we are pulling the latest User representation, then we should consider changing the name of the method for something like retrieve , refresh or reload. If we are really synchronizing, sync would be more acurate than update.

Too many requests

I would run the User.update() function during times such as:

  • App launch
  • App re-opened from sleep
  • Profile update
  • After in-app-purchase
  • etc.

If we need to retrive the User so often, it probably means that we shouldn't hold all its state. Or at least not completely.1

Ideally, we request the latest data when there's an evidence of changes and we need to be aware of them. For example, after specific business operations.

Otherwise, we come to realise that any event|action|moment is as good as any other. Such randomness, eventually, lead us to retrieve half DB without consistent reasons.

If App re-opened from sleep is a good moment for refreshing the User, why shouldn't it be good for any other entity?

Remember that connections consume resources and they also have an impact on the data plan. So, I would consider constraining the requests to the essential.

Caching only the essential

Consider holding the URIs instead of the complete representantion. For example, we could request and hold data that is unlikely to change.

GET /user/1    HTTP/1.1
HTTP 200 OK
{
  "fullname":"Han Solo"
}

Caching the fullname and hold the URIs of those resources sensibles to changes. For example, /user/1/profile and /user/1/score.

Later, we can retrieve these resources only when the business need them instead of randomly after certain events of the application.

Finally, think who else is going to be changing the User beside the user himself. If there's no concurrency over the data, probably the client application always has the most up-to-date representation.


1: What we do with the local storage is caching. If we have to expire the cache often, it means either we don't need cache at all or we are caching the wrong data.

1

The first idea that comes to mind is to create a class called User. It would be a subclass of NSMutableDictionary.

That's a big mistake. If your User class is a subclass of NSMutableDictionary, then any other object with a reference to it can assign any value to any property they wish. They could even change the type of a property or add new properties. It's a very bad architectural choice.

Instead, make your User a struct that has the correct properties with the correct types. Then make a singleton class, maybe called Session that is the single source of truth for the User object. Make the Session observable (à la the Observer pattern) so that whenever its state changes, all observers will be notified so they can update accordingly.

You could do away with the Session class and make the User an observable, singleton class itself, but this IMHO gives the User class too many responsibilities.

I would run the User.update() function during times such as:

  • App launch
  • App re-opened from sleep
  • Profile update
  • After in-app-purchase
  • etc.

Here's a use-case for you. I have the app open on my phone and my tablet. I make changes on the phone and then watch the tablet and expect the changes to appear there, but they don't because I'm not doing anything on the tablet that would cause the update() function to be called.

If you really want to keep the data in sync, see about setting up a socket server that pushes the data to the device whenever changes occur.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.