I use CoreData extensively in my app for viewing products. All data syncing is done in the background on a separate NSManagedObjectContexts and when syncing of changes is complete I call mergeChangesFromContextDidSaveNotification
This works perfectly 99% of the time. Here is where it is breaking down:
I view a product in my app.
I decide I want to change a price, so I goto to the web server where I control my products and change my price.
I come back to my app, browse away from that product and my app sync's the changes made on the web server to my app.
I go and view that product again and the price is not changed.
I completely exit the app and come back into it and then I can see the price change.
I believe what is happening is CoreData is serving up a cached version of the product and the fresh one is not served until after the app is restarted of I have viewed a bunch of other items.
How can I clear this cache after my mergeChangesFromContextDidSaveNotification has been called?
This is driving me and my clients crazy - anyone know how I can remedy this situation?
Well, there could actually be a lot of reasons for this. One of the big advantages of Core Data is its flexibility... which is also one of its big drawbacks.
You have a ton of knobs that can be set on your database that relate to caching and fetching. One of the easiest, though, is if the fetch goes to the database or just to the most recent MOC.
Namely, you should look at these:
- (BOOL)includesPendingChanges
- (BOOL)shouldRefreshRefetchedObjects
Also, make sure you are handling the background update and DidSave notification properly, because the MOC should have merged those changes.
Unfortunately, Core Data has some, let's say, hard to discover, interactions when using multiple MOCs. You must be very careful to follow all the rules.
Related
I understand that Core Data is essentially a self-contained local database, but I'm not sure if I should be using it in my app or not. Basically, it would be more for caching purposes if anything, since I retrieve all of my content from a web server database. Regardless, I was wondering if Core Data would be useful is any of these situations:
Scenario #1: I retrieve a list of "items" from the web server and feed them into a table view. This is essentially the first page the user sees. The table can be refreshed to retrieve more results, but existing items likely won't change. Over time this list of items could grow tremendously. Items can be deleted.
Scenario #2: A user has a friends list. This list of friends will stay the same unless he or she adds more friends. I imagine there will be a scenario where a friend deletes their account, in which case the friends list will be altered as well.
Scenario #3: Messages can be attached to items. They can't be edited or deleted, so the only change in state for a list of messages would be if a new message was added. Essentially the same as items, except they can't be deleted.
Actually, for your scenario I would say that you don't need any persistence in your app, but rather fetch your data from the server every time the app starts and just keep it in memory. There are a lot of apps which are doing it this way and this is totally fine behaviour.
However, there are some drawbacks of not using persistence:
worse offline experience for your user since they depend on a network connection, so effectively without a connection they can't do anything within your app
risk of slow loading
On the plus side we have:
using Core Data in your app is a huge implementation overhead (especially if you haven't used it before)
after having integrated Core Data, you still have a lot of issues to tackle, first and foremost: data synching between your app and the backend
If you decide to go for persistence, also take a look at alternatives to Core Data like Realm.
Finally, my advice still is to not use Core Data in your situation. However, keep in mind that you can build a version of your app that doesn't use persistence. And then, once you see that your app is well-received and gets more attention, you can still go and add persistence later on.
I am trying to build an offline synchronization capability into my iOS App and would like to get some feedback/advice from the community on the strategy and best practice to be followed to do the same. The app details are as follows:
The app shows a digital catalog to users and allows them to perform actions like creating and placing orders, among others.
Currently the app only works when online, and we have APIs for all actions like viewing the catalog, creating/placing orders which return JSON data.
We would like to provide offline/synchronization capability to users, through which users can view the catalog and create/place orders while offline, and when they come online the order details will be synchronized and updated to our server.
We would also like to pull the latest data from the server, and have the app keep itself up to date in case of catalog changes or order changes that happened at the Server while the app was offline.
Can you guys help me to come with the best design and approach for handling this kind of functionality?
I have done something similar just in the beginning of this year. After I read about NSOperationQueue and NSOperation I did a straight forward approach:
Whenever an object is changed/added/... in my local database, I add a new "sync"-operation to the queue and I do not care about, if the app is online or offline (I added a reachability observer which either suspended the queue or takes it back working; of course, I do re-queueing if an error occurs (lost network during sync)). The operation itself reads/writes the database and does the networking stuff. My ViewController use a NSFetchedResultsController (with delegate=self) to get callbacks on changes. In some cases I needed some extra local data (it is about counting objects), where I have used NSManagedObjectContextObjectsDidChangeNotification.
Furthermore, I have used Multi-Context CoreData which sounded quite reasonable to use (I have only two contexts).
To get notified about changes from your server, I believe that iOS 7 has something new for you.
On the server side, you should read a little for the actual approach you want to go for: i.e. Data Synchronization by Dan Grover or Developing Android REST Client Applications (of course there are many more good articles out there).
Caution: you might be disappointed when you expect an easy solution. Your requirement is not unusual, but the solution might become more complex than you expect - depending on the "business rules" and other reasonable requirements. If you intelligently restrict your requirements you may find a solution which you can implement yourself, otherwise you may also consider to use a commercial product.
I could imagine, that if you design the business logic such that it takes an offline state into account and exposes this explicitly in the business logic, you may find a solution which you can implement yourself with moderate effort. What I mean by this is for example, when a user creates an order, it is initially in "not committed" stated. The order will only be committed when there is access to the server and if the server gives the "OK" that this order can actually be placed by this user. The server may also deny the order, sending corresponding messages to the user.
There are probably quite a few subtle issues that may arise due to the requirement of eventual consistency.
See also this question which contains pointers to solutions from commercial products, and if you visit their web sites give valuable information about the complexity of the problem and how this can be solved.
when dealing with CoreData, I've run into a few problems I'm trying to nip in the bud for future proofing the system out of the gate. The simple fact of the matter is that I've never done anything like this before (work with CoreData that is). While I've managed to figure out how to work with it in the app, I need to know a decent practice to signal an app between versions that default data needs to be refresh on first app launch.
So right now, in my AppDelegate, I setup my managed object context, and I perform a fetch request to see if there are any records at all in a particular table/entity. I only want this to happen on first launch so im not constantly rewriting the contents of the DB every app launch. Anyways, so it goes ahead and uses Object Models to handle inserting of data amongst the entities in question (theres a few)
Now, for this version of the app, it's going into the store without an API (thats a far future thing), but between versions released to the app store, we may have to update specific information within the entities (for example: prices), again I only want this refresh to happen on app launch. Also, the schema MIGHT change, Im not sure if or when, but I'd like to make sure this can accomodate that just in case.
I figured, versioning the coredata "Add Model Version" would do the trick, set the new db version as the active version, but when I launch the app in the simulator, nothing happens which tells me that the data inside is being retained.
Any help towards what it is that I should do to accomodate this would be appreciated. Thank you!
You should find the Core Data Model Versioning and Data Migration guide useful:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
You'll also probably find Method for import initial data with coredata useful.
As someone that experienced the pain of iCloud while trying to prototype iCloud enabling one of our CoreData apps, Simperium looks very promising, but I'm interested in seeing how it handles some of the sharp edges.
One issue I came across was how to gracefully handle bootstrapping data when the application starts up. The first time a user launches our app, we will load some default data into our CoreData database. If a user launches the app first on the iPhone and then later on the iPad, they will end up getting the bootstrap data duplicated on both devices because of syncing. With iCloud, the solution was to hook into the iCloud merge process.
How would I handle this with Simperium?
There are at least a couple ways to do this.
You can hardcode the simperiumKey for each seeded object. For example, in a notes app, if every new user gets a welcome note, you can locally create that note with the simperiumKey of welcomeNote. This will ensure that only one welcome note will ever exist in that user's account (on any device). With this approach, there can be some redundant data transfer, so it's best if there's not a large amount of seeded data. On the other hand, this approach is good if you want data to be immediately available to new users even if they're offline when they first launch your app.
With Simperium, you also have the option to use a server process. You can seed new user accounts with data by using a Python or Ruby listener that runs some code when accounts are created. This is a good approach if there's a large amount of data, but has the disadvantage that users need to be online before the seeded data will transfer (and of course the transfer itself will take some time).
There are subtleties with these approaches. With the first approach, using the welcomeNote example, if your user deletes the welcomeNote and subsequently reinstalls your app in the future, the welcomeNote will get resurrected (but never duplicated) because it's being created locally. This is often acceptable. With the second approach, the welcomeNote would be seeded once and only once, so it will never get resurrected even if your app is reinstalled.
I am looking for either a sample app or a more architectural discussion to build an app, which maintains a local persistent store (CoreData) and keeps it sync against a Web-Service like Flickr. In my case it is Salesforce, but the pattern should be similar to many apps for Flickr, Twitter, IMAP and so on.
Sample questions:
where are the best points to invoke the syncing?
what are proven datastructures to maintain local changes
- maintain a "changed" BOOL in the local store for every unsynched change; I would prefer a field level flag against a record level flag)?
Of course I have to optimize this on my own, knowing the amount of records (100's) and changes (10's per day) and the probability of conflicts (low in my case on a field level).
Here's how I would approach this:
Start by modeling a local CoreData/Sqlite database that mirrors your online database.
Add an NSDate lastModified property to every row of each table. This will allow me to track changes at the record level, instead of field level. This helps reduce sync complexity, and in most real world scenarios, record level syncing is sufficient.
Perform an automatic sync when the app starts, and also provide a prominent "Sync" button in your navigation bar. This way the user always has an updated dataset when the app launches after a long period, and can sync the latest changes over the course of a day. I would avoid doing background sync while the app is being used. This will make your app more complex and error-prone when you're trying to tackle other things. So postpone working on background/automatic sync until you have the rest working.
Once I have my sync working reasonably well at launch and on-demand, I would try and support background sync. I would also try and eliminate the "Sync" button, so the user never has to think about syncing - (it's always up to date as far as the user is concerned). But this would be a longer term enhancement, which i would attempt only after I have "on-demand" syncing working rock solid.
Hope this helps you get started. I would love to hear if you think differently about any of this.