How to completely destroy and recreate a Realm database - ios

I have an application using Realm.io that on logout, we totally destroy the realm cache that has been built up by the user so that when a new user logs in, we can recreate the cache file for the next user.
Deleting all objects is not an option as the realm file is encrypted using a hash of the users passcode so it has to be a fresh Realm (AFAIK).
Currently I am deleting the Realm file like so:
NSString *realmFile = [[DCMCacheManager documentsDirectoryPath] stringByAppendingPathComponent:#"myApp.realm"];
NSFileManager *localFileManager = [NSFileManager new];
[localFileManager removeItemAtPath:realmFile error:&error];
And reinstating it using:
RLMRealm * realm = [RLMRealm realmWithConfiguration:config error:&error];
When the creation code runs for the first time the realm file is created with no issues. When I try to recreate it after deleting, the app seems to return an internally cached version of the realm file so does not create the myApp.realm file for me.
Any suggestions on how to make sure the realm is absolutely dead when I delete it?

Before deleting the Realm file you will need to ensure that all Realm objects using that file have been deallocated. When possible, the easiest way to do this is to simply do the deletion before you ever open the Realm file at all, or if you only need the Realm to be open very briefly, to do so within an explicit autoreleasepool.
If users can log out and log into a different account while the app is running, then this isn't an option. Instead, what I would recommend is to use a different file path for each user (i.e. something like NSString *realmFile = [[DCMCacheManager documentsDirectoryPath] stringByAppendingPathComponent:userName]; rather than #"myapp.realm"). This would sidestep the issue entirely, and make switching back and forth between users faster. If your Realm files take up a nontrivial amount of space, you could clean up the files for all but the currently active user on app startup.
On a side note, if your Realm files are purely just a cache then Apple wants to you put the file in the Caches directory and not the Documents directory, and they do occasionally reject apps for not doing that.

Related

Change Realm file without restarting application

I'm currently using dropbox to backup the Realm database file and when i need to restore, i just delete the old file and than put the new one from dropbox in its place, but to work i need to restart the application.
So, i'm looking a way to avoid the app restart.
You could delete the current files the way Realm suggests in the docs: https://realm.io/docs/swift/latest/#deleting-realm-files
Then nil your current Realm, write the new file to the right location in your file system and create a new Realm instance. Then reload your UI to show the new data.
You'll also have to be careful that your code is not holding references to objects in the previous Realm before it gets deleted.

Unload Realm because of new database file

I am trying to update precompiled database in my app using a new database file downloaded from the internet. However when I download the file and replace the old one used by Realm with it, Realm still uses the old one till the next app restart. Unfortunately I still need to open the first DB to copy some data from it before downloading a new database. Is there a possiblity to force unload/reload the whole database?
I made a mistake and I accidentally created a new instance of Realm. The problem is that Realm instances are cached. I needed to use autoreleasepool {} and ensure that Realm is created only in the block.

Read through my app's Core Data files (.sqlite, .sqlite-wal)

I am trying to browse through the data written by Core Data in an iOS app I am developing.
After the app ran for a while, and I assume collected some data, I now wish to look through the data and see what was written.
I have tried getting and browsing the .sqlite file through getting the app container from the device (Xcode > Devices > myApp > Download Container...).
I got the db files, myAppDB.sqlite, myAppDB.sqlite-shm and myAppDB.sqlite-wal.
When trying to look through them, it seems like the .sqlite is an empty table (except maybe some generic CoreData/sqlite stuff), and the -wal file has all the info.
The thing is I was only able to know that the wal has useful data when opening it with TextEdit, which din't show it in a very readable way, and when I tried to use an SQLite Manager app I an alert saying the wal is encrypted and I am asked to put a password...
For what it matters, I am writing a framework which handles the db (the model file and the code for writing data is inside the framework), then I have this framework running in an app I am developing. This is the code I use to create the store from within the framework (using MagicalRecord):
NSBundle *frameworkBundle = [NSBundle bundleForClass:[self class]];
[MagicalRecord setDefaultModelNamed:#"myAppStore.momd" inBundle:frameworkBundle];
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"myAppStoreDB.sqlite"];
[MagicalRecord setupCoreDataStackWithStoreAtURL:storeURL];
UPDATE: I managed to open the sqlite file with both Core-Data-Editor and CoreDataUtility but they both override and delete the contents of the .wal file, and show an empty table... It does have the model (entity names/properties etc.) but no data.
My wal file is 873KB but when I open the sqlite with one of these 2 tools it becomes 0Bytes...
tl;dr
How can I browse through the info written by Core Data of the app I am developing?
Well, for some reason I had to force not using WAL in my store (using #"journal_mode":#"DELETE" as explained here).
I then got only .sqlite file without the smh and wal files, and was able to open it and view the data using the 2 mentioned tools (Core-Data-Editor and CoreDataUtility).
My guess is that this is something to do with either the fact that I am dealing with CoreData from a framework (creating a moc, creating entities, saving etc.) and not from the application. Another guess is that it has something to do with the fact that I am using MagicalRecord.
Any insights regarding the cause would be appreciated...

Backing up sqlite DB on iOS

I would like to make backup copies of my app's main sqlite DB while my app is running.
1) I have read that it is safe to just copy the sqlite file if the DB has been checkpointed (at that point the wal file contains no important data). Does [managedContext save:] do that checkpointing, or is there something else I have to do? (ref -shm and -wal files in SQLite DB)
2) Is there any way, short of tearing down the whole core data stack, to be sure that core data doesn't try to write to the sqlite file while I'm copying it? My app does save frequently after any user input, and it would be nice if there was some way to force that to block for a second.
I have uploaded a sample app that provides backup and restore capabilities a number of different ways, including local backups, copy backups to and from iCloud, email backups, import from email, and file copy via iTunes. See link below for video demonstrating these capabilities and you can download the sample apps from the site.
http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/sample-apps-explanations/backup-files/
EDIT
It should be safe to create a new persistentStoreCoordinator with the same fileURL and to then use migratePersistentStore API without closing the App, save the main MOC first though. I always use JOURNAL=DELETE mode to ensure I just have to use a single file to deal with. If you are using WAL mode then you would need to backup all three files used by sqlite.
Regarding 2)
When I make backup, I close all mom's, moc's and persistent store. Then it's safe to make backup or restore. Also all views are waiting for events to release all coredata resources and bring them back when database is available again. It's good to have a singleton to manage coredata.

Check if UI(Managed)Document is already in iCloud via URLForUbiquityContainerIdentifier+Path instead of NSMetadataQuery?

I have a quite simple shoebox-style iOS app with 1 single Core Data database (as a UIManagedDocument) and thought about trying to add iCloud support.
I of course have to check if there is already an existing database in the cloud *before creating a new UIManagedDocument at startup*, saving/opening it, etc.
As i already know the filename and that there's either 1 document or no document at all, I didn't really get if I had to
start a NSMetaDataQuery with a predicate for the exact filename
and then get the fileURL from the result (and download it
explicitly?) and open it if there is one, or
just use [[NSFileManager defaultManager] fileExistsAtPath:self.iCloudDBURL]
with iCloudDBURL created from URLForUbiquityContainerIdentifier + appending ? Is this URL only a local one and doesn't check the "real" cloud automatically?
I know the use of UIManagedDocument might not be the "right" way for this kind of app, but I thought it'd easier and I could try..
You need to use the NSMetadataQuery approach.
When using iOS on iCloud, documents don't download automatically-- they only download when you ask for them. Using NSFileManager as you suggest would only tell you if the file existed on the local device. But, the file might exist up in the cloud, not downloaded locally yet. If you use NSMetadataQuery you can find out if the document exists anywhere, even if it's in the cloud and not actually downloaded yet. You can find out about the document if it was created on a different device. This also covers the case where the user deletes and reinstalls the app, but doesn't delete cloud data-- you find out if it exists even though it's not downloaded.
Since you're using UIManagedDocument you shouldn't need to make a specific download call-- it will handle that for you when you open it.

Resources