iOS core data corruption? - iphone

This is a rather general question, as I don't have any solid evidence atm.
I have an iPhone app with about 20,000 users. It allows users to message each other, and saves those messages in core data. The only other thing it saves in core data is the users profile, a copy locally and a copy on the server.
I have a small percentage of users complaining that they receive messages but nothing shows, when they send a message (which immediately goes into core data then shows on screen) it disappears immediately. Nothing but a full restore seems to fix it, and from what I can gather, even a restore which involves them restoring a backup they just made doesn't fix it.
My first thought was that core data must have become corrupted in some way ... but the messages they attempt to send actually do send, and this would be impossible if their local profile had become corrupted too.
I've never been able to recreate it, or found anyone face to face who has had a similar problem.
Does anyone have any suggestions on what could occur in core data that could lead to a situation like this so I can start to try and track down the problem? I'd estimate it's happening with about 1% of users.
Once again, sorry for the generality of the question, but it's all I have to work with just now!
Thanks
** Edit
Just to clarify, deleting the app and reinstalling it, does not fix the problem when this happens.
** Edit
I just had some more information from a user who is suffering from the problem ... the information my app saved in core data still exists after the app has been deleted and re-installed, all of it. I have deleted and re-installed my app hundreds of times over the last year, on countless different devices, and every time I delete and re-install, all previous data stored in core data is completely erased .. yet for these users, this is not happening. Does this sound like an iPhone issue that basically requires a restore?
** Edit 03/12/2010
AT LAST! I have some real solid information to work on. I added flurry to my latest release so I could track any core data error messages, and was able to correlate the errors received with a user id that I knew for a fact was experiencing the problem ...
The error is: "Msg: Error Domain=NSCocoaErrorDomain Code=133020 'The operation couldn’t be completed. (Cocoa error 133020.)' UserInfo=0x39c7c0 {conflictList=( 'NSMergeConflict (0x39c700) for NSManagedObject (0x38ad00) with objectID '0x375c30
Unfortunately, the error is trimmed. Flurry must have limit on the size of the message it can pass.
I don't have access to my mac and code just now, I'll start investigating as soon as I do and post back with some source code and any findings I have.

I'm having a problem like that right now. So far I have figured out that the same object is modified in two NSManagedObjectContext-s. The object X has (at least) two persistent attribites, a and b, one thread changes a, another thread changes b, and CoreData cannot merge.
The key phrase in the error message is NSMergeConflict:
Error Domain=NSCocoaErrorDomain Code=133020 "The operation couldn’t be completed. (Cocoa error 133020.)" UserInfo=0x1544c7d0 {conflictList=(
"NSMergeConflict (0xc489f40) for NSManagedObject (0xd833ca0) with objectID '0xd82b7c...
Not sure this really helps, but at least it tells you what might go wrong...

Does anyone have any suggestions on what could occur in core data that could lead to a situation like this so I can start to try and track down the problem?
About a zillion different things.
Your real problem is that you are trying to debug in a speculative fashion. To properly fix this bug, you first need to know how to reproduce the bug. There's no other way to know if you've fixed it after you've tried something.
I would not start by blaming the underlying frameworks like Core Data. Odds are your program is the one losing the data. Start with that assumption.

Does it affect these users from the moment they set up there account? Could it be something to do with their username/id/personal information?
If deleting the app completely and reinstalling doesn't solve the problem, then I'm at a loss for how it could be core data - as you yourself say, I often do this and it removes all core data each time. So if it is core data corruption, it would have to be in your construction of the core data instead of the database itself. I don't think it likely that only people who install your app would have problems with core-data being retained.
If you have contact with a user experiencing this problem, I would get them to try to create a new username that is "safe and normal".
Good Luck.

Related

How to load the PersistentStores in a correct way?

I've been looking on Internet the entire weekend and today on how to handle correctly any error when the PersistentStores loads, without a success!
Actually, so many people are looking for the same solution but no one gets an answer. For example: https://www.hackingwithswift.com/forums/swift/core-data-error-checking/3794
Apple give us a fatalError in their template while telling us to not use it for a shipping application.
Here's also how you can have it, from many tutorials on Youtube, for example:
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)!")
}
print("Core Data succeed to load!")
})
As I'm not able to handle a potential error, because I still don't know how to do it, I'd love to just load it without handling any error ; but I can't just delete the if and the print and leave it blank. So, what should I put inside?
The question doesn't have a straightforward answer because what you do depends on what your app does and how you want it to respond in this situation. Every app's requirements are different, and you have not given any indication of yours.
If this error happens, your app has failed to load the persistent store. Whatever data might have been in that store is not available. New data cannot be saved to that store. What should you do? What can you do, in your app, to meet your requirements? What do you want your app to do if this persistent store is not available? What would you consider to be a reasonable response, in your specific app? That is what you do here.
There's no real recovery path; you can't simply make the error go away. You could try deleting the old persistent store and creating a new one. That would lose any previously saved data. Is that OK? It depends on you and your app. If you want to do that, look into the destroyPersistentStore(at:, type:, options:) function to remove it cleanly. Then try loading the store again, and a new one will be created.

Why is sqflite retaining open database reference on hot reload

I have an app that checks for an sqflite db file in the app storage directory and if it is present, blows away the file and creates a new one and then opens it for the remainder of the app life. What I have noticed is that hot restart fails with sql exceptions because it still sees the database as open somewhere in memory even though all my references are blown away due to the restart. This seems like a bug because I have no way to control the closing of that database unless I try to make sure it is closed if the application is killed. Is this by design? Is there a standard way to approach it?
I noticed this related post
Your assumption and investigation are correct. With bad words, what happens during hot restart is that the dart environment is restarted while the native world is still running and for sqlite that means that open databases remain opened with no easy way to know that from the dart side.
The hot-restart hack in place in sqflite is meant mainly to handle hotrestart even if you are in a sqlite transaction. Without this hack, it should happen what you describe in your scenario. That is said deleteDatabase should be used instead of removing manually the file.
In theory, this should be handled in openDatabase and deleteDatabase.
Can you confirm that you are indeed using deleteDatabase? If yes, in the mean time your workaround is safe and reporting an issue on sqflite for investigation would be great. Thanks!
After further testing it looks like 1 way to handle this is to check for the existence of the file. If the file exists, go ahead and open it using sqflite's openDatabase method. Then immediately close it. Then delete the file using io file delete method. Then you can create a new database file using the openDatabase method. This solution is maybe a little more expensive than I had hoped but it is working.

How can I debug something I cant often re-create?

So I have had this bug for a long time that I have been unable to track down. The problem is I cant recreate it often.
So far I have tracked the bug down to a specific process that basically goes like this.
Their are three simultaneous processes that go on that take different lengths of time. After each process is done it sets a boolean "key" to true, and then triggers a function that checks if all "keys" are in place. Basically once the last "key" is in it will actually start to do things.
Somewhere in the key setting process, or earlier, or even possibly after it crashes. Unfortunately it leaves a really cryptic error message, and when it crashes in Xcode it is "EXEC BAD ACESS" and thus just puts a breakpoint in the app delegate declaration.
I am sure I can easily fix this bug, I just dont know enough on how to fix this. Thankfully I have fabric which allows me to print to a text file that I can see when a user crashes. Each update I add new data to it (at the cost of a tad bit of latency) in order to better understand how it happened. Each new crash gets me closer. Unfortunately though I have slow adoption rates to new versions and the crashes just keep building up! I still dont know why.
Unfortunately because this crash only happens once in a blue moon (atleast on my device) on my device. And because of the low new version updates I have to collect the data myself. Which is really hard!
I have tried tons of methods of trying to get things to go wrong, or making the process that caused it happen rapidly, or even having auto pressing buttons. Still i cant get it to crash again! And when it does all I can do to track down the bug is add more println calls so I can see what is going on.
The freaky thing is for all I know I could have already fixed it because I usually try new tweaks ect. But I won't know because it won't consistently crashes. Honestly i'm fairly sure I fixed it (or at least took down the chances of it happening).
What would you do in a situation like this?
This sound like a race condition
I would use NSOperationQueue to make sure that all he tasks are done in order and that all are actually completed before the final one is performed.

Ways of handling serious problems on an iPhone app

Say you are doing something critical to the app's usability at startup (like copying the SQLite DB or setting up CoreData) and something goes wrong that doesn't cause a crash but you don't want the user to continue. What can you do?
Currently my app has abort() and NSAssert(false,...) calls to make sure the app doesn't continue, obviously after the error has been logged. But somehow I think its not going to score points with Apple on the app store.
Anyone have any ideas what I can do in such situations? I understand for instance that if there is no connectivity you can put your app in 'offline' mode but lets say the DB couldn't be properly setup (for argument sake). There is no 'offline' for that and so the user cannot continue. The user needs to quit the app and try again or report the problem. Wouldn't you agree, or am I missing something?
I just decided to create a view that has a message on it explaining to the user that a critical error has occurred and explained some steps to follow to resolve it. It prevents the user from using the app until it is resolved. So if the DB is missing or the model schema doesn't match the DB schema it will bring up that view.
Its one way of doing it that I took. If anyone has a different way, I'd like to hear it.

When is it best to do an NSManagedObjectContext save?

I have noticed that for a mobile application, saving on the main thread seems to take a bit when it compares to other applications on the device. Is it recommended to only save Core Data when the application enters the background or when an application closes instead of anytime items are added and sent / received from the api?
That's kind of a broad question, but I've found that saving core data after VewDidAppear statements is better than viewWill statements. Giving the user something to engage with and persisting makes it less noticeable than on a load. However, if a user is used to waiting for something like an activity loop, adding the save to that doesn't tax it too much (IMHO).
Not sure this help, just my experience.